Beispiel #1
0
        private async Task <long> StreamEventsToProjections(ExclusiveEventStoreCatchupQuery query)
        {
            long eventsProcessedOutOfBatch = 0;

            foreach (var storedEvent in query.Events)
            {
                eventsProcessedOutOfBatch++;

                IncludeReadModelsNeeding(storedEvent);

                if (cancellationDisposable.IsDisposed)
                {
                    break;
                }

                IEvent @event = null;
                var    now    = Clock.Now();

                try
                {
                    @event = await UpdateProjectorsAndCursors(
                        query,
                        storedEvent,
                        eventsProcessedOutOfBatch,
                        now);
                }
                catch (Exception ex)
                {
                    var error = @event == null
                                    ? SerializationError(ex, storedEvent)
                                    : new Domain.EventHandlingError(ex, @event: @event);

                    ReadModelUpdate.ReportFailure(
                        error,
                        createReadModelDbContext);
                }

                var status = new ReadModelCatchupStatus
                {
                    BatchCount = query.BatchMatchedEventCount,
                    NumberOfEventsProcessed = eventsProcessedOutOfBatch,
                    CurrentEventId          = storedEvent.Id,
                    EventTimestamp          = storedEvent.Timestamp,
                    StatusTimeStamp         = now,
                    CatchupName             = Name
                };

                if (status.IsEndOfBatch)
                {
                    // reset the re-entrancy flag
                    running = 0;
                    query.Dispose();
                }

                ReportStatus(status);
            }

            return(eventsProcessedOutOfBatch);
        }
Beispiel #2
0
        private async Task <long> StreamEventsToProjections(ExclusiveEventStoreCatchupQuery query)
        {
            long eventsProcessed = 0;

            foreach (var storedEvent in query.Events)
            {
                eventsProcessed++;

                IncludeReadModelsNeeding(storedEvent);

                if (cancellationDisposable.IsDisposed)
                {
                    break;
                }

                IEvent @event = null;
                var    now    = Clock.Now();

                try
                {
                    // update projectors
                    @event = storedEvent.ToDomainEvent();

                    if (@event != null)
                    {
                        using (var work = CreateUnitOfWork(@event))
                        {
                            await bus.PublishAsync(@event);

                            var infos = work.Resource <DbContext>().Set <ReadModelInfo>();

                            subscribedReadModelInfos.ForEach(i =>
                            {
                                var eventsRemaining = query.ExpectedNumberOfEvents - eventsProcessed;

                                infos.Attach(i);
                                i.LastUpdated           = now;
                                i.CurrentAsOfEventId    = storedEvent.Id;
                                i.LatencyInMilliseconds = (now - @event.Timestamp).TotalMilliseconds;
                                i.BatchRemainingEvents  = eventsRemaining;

                                if (eventsProcessed == 1)
                                {
                                    i.BatchStartTime   = now;
                                    i.BatchTotalEvents = query.ExpectedNumberOfEvents;
                                }

                                if (i.InitialCatchupStartTime == null)
                                {
                                    i.InitialCatchupStartTime = now;
                                    i.InitialCatchupEvents    = query.ExpectedNumberOfEvents;
                                }

                                if (eventsRemaining == 0 && i.InitialCatchupEndTime == null)
                                {
                                    i.InitialCatchupEndTime = now;
                                }
                            });

                            work.VoteCommit();
                        }
                    }
                    else
                    {
                        throw new SerializationException(string.Format(
                                                             "Deserialization: Event type '{0}.{1}' not found",
                                                             storedEvent.StreamName,
                                                             storedEvent.Type));
                    }
                }
                catch (Exception ex)
                {
                    var error = @event == null
                                    ? SerializationError(ex, storedEvent)
                                    : new Domain.EventHandlingError(ex, @event: @event);

                    ReadModelUpdate.ReportFailure(
                        error,
                        () => CreateReadModelDbContext());
                }

                var status = new ReadModelCatchupStatus
                {
                    BatchCount = query.ExpectedNumberOfEvents,
                    NumberOfEventsProcessed = eventsProcessed,
                    CurrentEventId          = storedEvent.Id,
                    EventTimestamp          = storedEvent.Timestamp,
                    StatusTimeStamp         = now,
                    CatchupName             = Name
                };

                if (status.IsEndOfBatch)
                {
                    // reset the re-entrancy flag
                    running = 0;
                    query.Dispose();
                }

                ReportStatus(status);
            }
            return(eventsProcessed);
        }
Beispiel #3
0
        /// <summary>
        /// Runs a single catchup operation, which will catch up the subscribed projectors through the latest recorded event.
        /// </summary>
        /// <remarks>This method will return immediately without performing any updates if another catchup is currently in progress for the same read model database.</remarks>
        public async Task <ReadModelCatchupResult> Run()
        {
            // perform a re-entrancy check so that multiple catchups do not try to run concurrently
            if (Interlocked.CompareExchange(ref running, 1, 0) != 0)
            {
                Debug.WriteLine(string.Format("Catchup {0}: ReadModelCatchup already in progress. Skipping.", Name), ToString());
                return(ReadModelCatchupResult.CatchupAlreadyInProgress);
            }

            EnsureInitialized();

            long eventsProcessed = 0;
            var  stopwatch       = new Stopwatch();

            // iterate over the events in order
            try
            {
                using (var query = new ExclusiveEventStoreCatchupQuery(
                           await CreateOpenEventStoreDbContext(),
                           lockResourceName,
                           GetStartingId,
                           matchEvents))
                {
                    ReportStatus(new ReadModelCatchupStatus
                    {
                        BatchCount = query.ExpectedNumberOfEvents,
                        NumberOfEventsProcessed = eventsProcessed,
                        CurrentEventId          = query.StartAtId,
                        CatchupName             = Name
                    });

                    Debug.WriteLine(new { query });

                    if (query.ExpectedNumberOfEvents == 0)
                    {
                        return(ReadModelCatchupResult.CatchupRanButNoNewEvents);
                    }

                    Debug.WriteLine(string.Format("Catchup {0}: Beginning replay of {1} events", Name, query.ExpectedNumberOfEvents));

                    stopwatch.Start();

                    eventsProcessed = await StreamEventsToProjections(query);
                }
            }
            catch (Exception exception)
            {
                // TODO: (Run) this should probably throw
                Debug.WriteLine(string.Format("Catchup {0}: Read model catchup failed after {1}ms at {2} events.\n{3}", Name, stopwatch.ElapsedMilliseconds, eventsProcessed,
                                              exception));
            }
            finally
            {
                // reset the re-entrancy flag
                running = 0;
                Debug.WriteLine(string.Format("Catchup {0}: Catchup batch done.", Name));
            }

            stopwatch.Stop();

            if (eventsProcessed > 0)
            {
                Debug.WriteLine(
                    "Catchup {0}: {1} events projected in {2}ms ({3}ms/event)",
                    Name,
                    eventsProcessed,
                    stopwatch.ElapsedMilliseconds,
                    (stopwatch.ElapsedMilliseconds / eventsProcessed));
            }

            return(ReadModelCatchupResult.CatchupRanAndHandledNewEvents);
        }
Beispiel #4
0
        private async Task <IEvent> UpdateProjectorsAndCursors(
            ExclusiveEventStoreCatchupQuery query,
            StorableEvent storedEvent,
            long eventsProcessedOutOfBatch,
            DateTimeOffset now)
        {
            var @event = storedEvent.ToDomainEvent();

            if (@event != null)
            {
                using (var work = CreateUnitOfWork(@event))
                {
                    var errors = new ConcurrentBag <Domain.EventHandlingError>();

                    using (CaptureErrorsFor(@event, into: errors))
                    {
                        await bus.PublishAsync(@event);
                    }

                    var infos = work.Resource <DbContext>().Set <ReadModelInfo>();

                    subscribedReadModelInfos
                    .Where(i => errors.All(err => err.Handler != i.Handler))
                    .ForEach(i =>
                    {
                        infos.Attach(i);
                        i.LastUpdated           = now;
                        i.CurrentAsOfEventId    = storedEvent.Id;
                        i.LatencyInMilliseconds = (now - @event.Timestamp).TotalMilliseconds;
                        i.BatchRemainingEvents  = query.BatchMatchedEventCount - eventsProcessedOutOfBatch;

                        if (eventsProcessedOutOfBatch == 1)
                        {
                            i.BatchStartTime   = now;
                            i.BatchTotalEvents = query.BatchMatchedEventCount;
                        }

                        if (i.InitialCatchupStartTime == null)
                        {
                            i.InitialCatchupStartTime   = now;
                            i.InitialCatchupTotalEvents = eventStoreTotalCount;
                        }

                        if (i.InitialCatchupEndTime == null)
                        {
                            if (i.CurrentAsOfEventId >= initialCatchupIsDoneAfterEventId)
                            {
                                i.InitialCatchupEndTime         = now;
                                i.InitialCatchupRemainingEvents = 0;
                            }
                            else
                            {
                                // initial catchup is still in progress
                                i.InitialCatchupRemainingEvents = query.TotalMatchedEventCount -
                                                                  eventsProcessedOutOfBatch;
                            }
                        }
                    });

                    work.VoteCommit();
                }
            }
            else
            {
                throw new SerializationException($"Deserialization: Event type '{storedEvent.StreamName}.{storedEvent.Type}' not found");
            }

            return(@event);
        }
Beispiel #5
0
        /// <summary>
        /// Runs a single catchup operation, which will catch up the subscribed projectors through the latest recorded event.
        /// </summary>
        /// <remarks>This method will return immediately without performing any updates if another catchup is currently in progress for the same read model database.</remarks>
        public async Task <ReadModelCatchupResult> Run()
        {
            if (disposables.IsDisposed)
            {
                throw new ObjectDisposedException($"The catchup has been disposed. ({this})");
            }

            // perform a re-entrancy check so that multiple catchups do not try to run concurrently
            if (Interlocked.CompareExchange(ref running, 1, 0) != 0)
            {
                Debug.WriteLine($"Catchup {Name}: ReadModelCatchup already in progress. Skipping.", ToString());
                return(ReadModelCatchupResult.CatchupAlreadyInProgress);
            }

            EnsureInitialized();

            long eventsProcessed = 0;
            var  stopwatch       = new Stopwatch();

            // iterate over the events in order
            try
            {
                using (var query = new ExclusiveEventStoreCatchupQuery(
                           await CreateOpenEventStoreDbContext(),
                           lockResourceName,
                           GetStartingId,
                           q => q.Where(matchEvents, filter),
                           batchSize))
                {
                    ReportStatus(new ReadModelCatchupStatus
                    {
                        BatchCount = query.BatchMatchedEventCount,
                        NumberOfEventsProcessed = eventsProcessed,
                        CurrentEventId          = query.StartAtId,
                        CatchupName             = Name
                    });

                    Debug.WriteLine(new { query });

                    if (query.BatchMatchedEventCount == 0)
                    {
                        return(ReadModelCatchupResult.CatchupRanButNoNewEvents);
                    }

                    Debug.WriteLine($"Catchup {Name}: Beginning replay of {query.BatchMatchedEventCount} events");

                    stopwatch.Start();

                    eventsProcessed = await StreamEventsToProjections(query);
                }
            }
            catch (Exception exception)
            {
                // TODO: (Run) this should probably throw
                Trace.WriteLine($"Catchup {Name}: Read model catchup failed after {stopwatch.ElapsedMilliseconds}ms at {eventsProcessed} events.\n{exception}");
            }
            finally
            {
                // reset the re-entrancy flag
                running = 0;
                Debug.WriteLine($"Catchup {Name}: Catchup batch done.");
            }

            stopwatch.Stop();

            if (eventsProcessed > 0)
            {
                Debug.WriteLine(
                    $"Catchup {Name}: {eventsProcessed} events projected in {stopwatch.ElapsedMilliseconds}ms ({stopwatch.ElapsedMilliseconds/eventsProcessed}ms/event)");
            }

            return(ReadModelCatchupResult.CatchupRanAndHandledNewEvents);
        }