private void InitializeReadModelInfo() { using (var db = createReadModelDbContext()) { EnsureLockResourceNameIsInitialized(db); var existingReadModelInfoNames = GetProjectorNames(); var existingReadModelInfos = db.Set <ReadModelInfo>() .OrderBy(i => i.Name) .Where(i => existingReadModelInfoNames.Contains(i.Name)) .ToList(); unsubscribedReadModelInfos = new List <ReadModelInfo>(existingReadModelInfos); subscribedReadModelInfos = new List <ReadModelInfo>(); // create ReadModelInfo entries for any projectors that don't already have them foreach (var projector in projectors.Where(p => !unsubscribedReadModelInfos.Select(i => i.Name).Contains(ReadModelInfo.NameForProjector(p)))) { var readModelInfo = new ReadModelInfo { Name = ReadModelInfo.NameForProjector(projector) }; db.Set <ReadModelInfo>().Add(readModelInfo); unsubscribedReadModelInfos.Add(readModelInfo); } db.SaveChanges(); } }
private static TimeSpan?TimeTakenForInitialCatchup(ReadModelInfo i, DateTimeOffset now) { if (i.InitialCatchupStartTime.HasValue) { return((i.InitialCatchupEndTime ?? now) - i.InitialCatchupStartTime); } return(null); }
public EventHandlerProgress( ReadModelInfo readModelInfo, DateTimeOffset?asOf = null) { if (readModelInfo == null) { throw new ArgumentNullException(nameof(readModelInfo)); } this.readModelInfo = readModelInfo; now = asOf ?? Clock.Now(); }
internal static void ReportFailure( Domain.EventHandlingError error, Func <DbContext> createDbContext) { // add an EventHandlingError entry as well EventHandlingError sqlError = CreateEventHandlingError((dynamic)error); var errorText = new { error.Exception, Event = sqlError.SerializedEvent }.ToJson(); log.Write(() => errorText); using (var transaction = new TransactionScope(TransactionScopeOption.Suppress)) using (var db = createDbContext()) { var dbSet = db.Set <ReadModelInfo>(); var handler = error.Handler; string readModelInfoName = null; if (handler != null) { // update the affected ReadModelInfo readModelInfoName = ReadModelInfo.NameForProjector(handler); var readModelInfo = dbSet.SingleOrDefault(i => i.Name == readModelInfoName); if (readModelInfo == null) { readModelInfo = new ReadModelInfo { Name = readModelInfoName }; dbSet.Add(readModelInfo); } readModelInfo.Error = errorText; readModelInfo.FailedOnEventId = sqlError.OriginalId; } sqlError.Error = error.Exception.ToJson(); sqlError.Handler = readModelInfoName; db.Set <EventHandlingError>().Add(sqlError); db.SaveChanges(); transaction.Complete(); } }
private void EnsureInitialized() { if (subscribedReadModelInfos != null) { return; } // figure out which event types we will need to query matchEvents = projectors.SelectMany(p => p.MatchesEvents()) .Distinct() .Select(m => { // TODO: (EnsureInitialized) optimize this by figuring out how to query SteamName LIKE 'Scheduled:%' if (m.Type == "Scheduled") { return(new MatchEvent(m.StreamName, "*")); } return(m); }) .ToArray(); Debug.WriteLine(string.Format("Catchup {0}: Subscribing to event types: {1}", Name, matchEvents.Select(m => m.ToString()).ToJson())); using (var db = CreateReadModelDbContext()) { EnsureLockResourceNameIsInitialized(db); var existingReadModelInfoNames = GetProjectorNames(); var existingReadModelInfos = db.Set <ReadModelInfo>() .OrderBy(i => i.Name) .Where(i => existingReadModelInfoNames.Contains(i.Name)) .ToList(); unsubscribedReadModelInfos = new List <ReadModelInfo>(existingReadModelInfos); subscribedReadModelInfos = new List <ReadModelInfo>(); // create ReadModelInfo entries for any projectors that don't already have them foreach (var projector in projectors.Where(p => !unsubscribedReadModelInfos.Select(i => i.Name).Contains(ReadModelInfo.NameForProjector(p)))) { var readModelInfo = new ReadModelInfo { Name = ReadModelInfo.NameForProjector(projector) }; db.Set <ReadModelInfo>().Add(readModelInfo); unsubscribedReadModelInfos.Add(readModelInfo); } db.SaveChanges(); } }
private void IncludeReadModelsNeeding(StorableEvent storedEvent) { if (unsubscribedReadModelInfos.Count > 0) { foreach (var readmodelInfo in unsubscribedReadModelInfos.ToArray()) { if (storedEvent.Id >= readmodelInfo.CurrentAsOfEventId + 1) { var handler = projectors.Single(p => ReadModelInfo.NameForProjector(p) == readmodelInfo.Name); disposables.Add(bus.Subscribe(handler)); unsubscribedReadModelInfos.Remove(readmodelInfo); subscribedReadModelInfos.Add(readmodelInfo); } } } }
internal static void ReportFailure( Domain.EventHandlingError error, Func <DbContext> createDbContext) { // add an EventHandlingError entry as well EventHandlingError sqlError = CreateEventHandlingError((dynamic)error); log.Write(() => new { error.Exception, sqlError.SerializedEvent }); using (var db = createDbContext()) { var dbSet = db.Set <ReadModelInfo>(); var handler = error.Handler; string readModelInfoName = null; var exceptionJson = error.Exception.ToDiagnosticJson(); if (handler != null) { // update the affected ReadModelInfo readModelInfoName = ReadModelInfo.NameForProjector(handler); var readModelInfo = dbSet.SingleOrDefault(i => i.Name == readModelInfoName); if (readModelInfo == null) { readModelInfo = new ReadModelInfo { Name = readModelInfoName }; dbSet.Add(readModelInfo); } readModelInfo.LastError = exceptionJson; readModelInfo.FailedOnEventId = sqlError.EventId; } sqlError.Error = $"{Environment.MachineName}: {exceptionJson}"; sqlError.Handler = readModelInfoName; db.Set <EventHandlingError>().Add(sqlError); db.SaveChanges(); } }
private long StreamEventsToProjections(ExclusiveEventStoreCatchupQuery query) { long eventsProcessed = 0; foreach (var storedEvent in query.Events) { eventsProcessed++; if (unsubscribedReadModelInfos.Count > 0) { foreach (var readmodelInfo in unsubscribedReadModelInfos.ToArray()) { if (storedEvent.Id >= readmodelInfo.CurrentAsOfEventId + 1) { var handler = projectors.Single(p => ReadModelInfo.NameForProjector(p) == readmodelInfo.Name); disposables.Add(bus.Subscribe(handler)); unsubscribedReadModelInfos.Remove(readmodelInfo); subscribedReadModelInfos.Add(readmodelInfo); } } } 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)) { bus.PublishAsync(@event).Wait(); 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); }
internal static void ReportFailure( Domain.EventHandlingError error, Func<DbContext> createDbContext) { // add an EventHandlingError entry as well EventHandlingError sqlError = CreateEventHandlingError((dynamic) error); log.Write(() => new { error.Exception, sqlError.SerializedEvent }); using (var db = createDbContext()) { var dbSet = db.Set<ReadModelInfo>(); var handler = error.Handler; string readModelInfoName = null; var exceptionJson = error.Exception.ToDiagnosticJson(); if (handler != null) { // update the affected ReadModelInfo readModelInfoName = ReadModelInfo.NameForProjector(handler); var readModelInfo = dbSet.SingleOrDefault(i => i.Name == readModelInfoName); if (readModelInfo == null) { readModelInfo = new ReadModelInfo { Name = readModelInfoName }; dbSet.Add(readModelInfo); } readModelInfo.Error = exceptionJson; readModelInfo.FailedOnEventId = sqlError.OriginalId; } sqlError.Error = exceptionJson; sqlError.Handler = readModelInfoName; db.Set<EventHandlingError>().Add(sqlError); db.SaveChanges(); } }
internal static void ReportFailure( Domain.EventHandlingError error, Func<DbContext> createDbContext) { // add an EventHandlingError entry as well EventHandlingError sqlError = CreateEventHandlingError((dynamic) error); var errorText = new { error.Exception, Event = sqlError.SerializedEvent }.ToJson(); log.Write(() => errorText); using (var transaction = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) using (var db = createDbContext()) { var dbSet = db.Set<ReadModelInfo>(); var handler = error.Handler; string readModelInfoName = null; if (handler != null) { // update the affected ReadModelInfo readModelInfoName = ReadModelInfo.NameForProjector(handler); var readModelInfo = dbSet.SingleOrDefault(i => i.Name == readModelInfoName); if (readModelInfo == null) { readModelInfo = new ReadModelInfo { Name = readModelInfoName }; dbSet.Add(readModelInfo); } readModelInfo.Error = errorText; readModelInfo.FailedOnEventId = sqlError.OriginalId; } sqlError.Error = error.Exception.ToJson(); sqlError.Handler = readModelInfoName; db.Set<EventHandlingError>().Add(sqlError); db.SaveChanges(); transaction.Complete(); } }
private static long EventsProcessedOutOfBatch(ReadModelInfo i) => i.BatchTotalEvents - i.BatchRemainingEvents;
private static long EventsProcessedOutOfAllEvents(ReadModelInfo i) => i.InitialCatchupEvents - i.BatchRemainingEvents;