public async Task <Guid> Resolve <T>(T entity, IEnumerable <IWritableEvent> uncommitted, Guid commitId, Guid startingEventId, IDictionary <string, string> commitHeaders) where T : class, IEventSource { var stream = entity.Stream; Logger.Write(LogLevel.Info, () => $"Resolving {uncommitted.Count()} uncommitted events to stream [{stream.StreamId}] bucket [{stream.Bucket}]"); try { await _store.Freeze <T>(stream.Bucket, stream.StreamId).ConfigureAwait(false); var latestEvents = await _store.GetEvents <T>(stream.Bucket, stream.StreamId, stream.CommitVersion + 1) .ConfigureAwait(false); Logger.Write(LogLevel.Debug, () => $"Stream is {latestEvents.Count()} events behind store"); var writableEvents = latestEvents as IWritableEvent[] ?? latestEvents.ToArray(); stream.Concat(writableEvents); entity.Hydrate(writableEvents.Select(x => x.Event as IEvent)); Logger.Write(LogLevel.Debug, () => "Merging conflicted events"); try { foreach (var u in uncommitted) { entity.Conflict(u.Event as IEvent); } } catch (NoRouteException e) { Logger.Write(LogLevel.Info, () => $"Failed to resolve conflict: {e.Message}"); throw new ConflictResolutionFailedException("Failed to resolve conflict", e); } Logger.Write(LogLevel.Debug, () => "Successfully merged conflicted events"); if (stream.StreamVersion != stream.CommitVersion && entity is ISnapshotting && ((ISnapshotting)entity).ShouldTakeSnapshot()) { Logger.Write(LogLevel.Debug, () => $"Taking snapshot of {typeof(T).FullName} id [{entity.StreamId}] version {stream.StreamVersion}"); var memento = ((ISnapshotting)entity).TakeSnapshot(); stream.AddSnapshot(memento, commitHeaders); } startingEventId = await stream.Commit(commitId, startingEventId, commitHeaders).ConfigureAwait(false); } finally { await _store.Unfreeze <T>(stream.Bucket, stream.StreamId).ConfigureAwait(false); } return(startingEventId); }
public Task <IEnumerable <IWritableEvent> > AllEvents(bool?backwards) { return(backwards == true?_store.GetEventsBackwards <T>(Bucket, StreamId) : _store.GetEvents <T>(Bucket, StreamId)); }
public async Task <Guid> Resolve <T>(T entity, IEnumerable <IWritableEvent> uncommitted, Guid commitId, Guid startingEventId, IDictionary <string, string> commitHeaders) where T : class, IEventSource { // Store conflicting events in memory // After 100 or so pile up pull the latest stream and attempt to write them again foreach (var @event in uncommitted) { await _delay.AddToQueue(entity.StreamId, @event).ConfigureAwait(false); } // Todo: make 30 seconds configurable if (await _delay.Age(entity.StreamId).ConfigureAwait(false) < TimeSpan.FromSeconds(30)) { return(startingEventId); } var stream = entity.Stream; Logger.Write(LogLevel.Debug, () => $"Starting weak conflict resolve for stream [{stream.StreamId}] bucket [{stream.Bucket}]"); try { try { await _store.Freeze <T>(stream.Bucket, stream.StreamId).ConfigureAwait(false); } catch (VersionException) { Logger.Write(LogLevel.Debug, () => $"Stopping weak conflict resolve - someone else is processing"); return(startingEventId); } uncommitted = (await _delay.Pull(entity.StreamId).ConfigureAwait(false)).Cast <IWritableEvent>(); Logger.Write(LogLevel.Info, () => $"Resolving {uncommitted.Count()} uncommitted events to stream [{stream.StreamId}] bucket [{stream.Bucket}]"); var latestEvents = await _store.GetEvents <T>(stream.Bucket, stream.StreamId, stream.CommitVersion + 1) .ConfigureAwait(false); Logger.Write(LogLevel.Debug, () => $"Stream is {latestEvents.Count()} events behind store"); var writableEvents = latestEvents as IWritableEvent[] ?? latestEvents.ToArray(); stream.Concat(writableEvents); entity.Hydrate(writableEvents.Select(x => x.Event as IEvent)); Logger.Write(LogLevel.Debug, () => "Merging conflicted events"); try { foreach (var u in uncommitted) { entity.Conflict(u.Event as IEvent); } } catch (NoRouteException e) { Logger.Write(LogLevel.Info, () => $"Failed to resolve conflict: {e.Message}"); throw new ConflictResolutionFailedException("Failed to resolve conflict", e); } Logger.Write(LogLevel.Debug, () => "Successfully merged conflicted events"); if (stream.StreamVersion != stream.CommitVersion && entity is ISnapshotting && ((ISnapshotting)entity).ShouldTakeSnapshot()) { Logger.Write(LogLevel.Debug, () => $"Taking snapshot of [{typeof(T).FullName}] id [{entity.StreamId}] version {stream.StreamVersion}"); var memento = ((ISnapshotting)entity).TakeSnapshot(); stream.AddSnapshot(memento, commitHeaders); } startingEventId = await stream.Commit(commitId, startingEventId, commitHeaders).ConfigureAwait(false); } finally { await _store.Unfreeze <T>(stream.Bucket, stream.StreamId).ConfigureAwait(false); } return(startingEventId); }
public async Task Resolve <T>(T entity, IEnumerable <IWritableEvent> uncommitted, Guid commitId, IDictionary <string, string> commitHeaders) where T : class, IEventSource { // Todo fix delayed channel impl - currently doesn't do Age from store anymore throw new NotImplementedException("See todo - needs fix"); // Store conflicting events in memory // After 100 or so pile up pull the latest stream and attempt to write them again var streamName = _streamGen(typeof(T), StreamTypes.Domain, entity.Bucket, entity.StreamId); foreach (var @event in uncommitted) { await _delay.AddToQueue(streamName, @event).ConfigureAwait(false); } // Todo: make 30 seconds configurable // Todo: if a stream has a single conflict and never conflicts again, the event we delay will never be processed var age = await _delay.Age(streamName).ConfigureAwait(false); if (!age.HasValue || age < TimeSpan.FromSeconds(30)) { return; } var stream = entity.Stream; Logger.Write(LogLevel.Info, () => $"Starting weak conflict resolve for stream [{stream.StreamId}] type [{typeof(T).FullName}] bucket [{stream.Bucket}]"); try { try { await _store.Freeze <T>(stream.Bucket, stream.StreamId).ConfigureAwait(false); } catch (VersionException) { Logger.Write(LogLevel.Info, () => $"Stopping weak conflict resolve - someone else is processing"); return; } uncommitted = (await _delay.Pull(streamName, max: _maxPulledDelayed).ConfigureAwait(false)).Cast <IWritableEvent>(); // If someone else pulled while we were waiting if (!uncommitted.Any()) { return; } Logger.Write(LogLevel.Info, () => $"Resolving {uncommitted.Count()} uncommitted events to stream [{stream.StreamId}] type [{typeof(T).FullName}] bucket [{stream.Bucket}]"); var latestEvents = await _store.GetEvents <T>(stream.Bucket, stream.StreamId, stream.CommitVersion + 1) .ConfigureAwait(false); Logger.Write(LogLevel.Info, () => $"Stream [{stream.StreamId}] bucket [{stream.Bucket}] is {latestEvents.Count()} events behind store"); var writableEvents = latestEvents as IWritableEvent[] ?? latestEvents.ToArray(); stream.Concat(writableEvents); entity.Hydrate(writableEvents.Select(x => x.Event as IEvent)); Logger.Write(LogLevel.Debug, () => "Merging conflicted events"); try { foreach (var u in uncommitted) { entity.Conflict(u.Event as IEvent, metadata: new Dictionary <string, string> { { "ConflictResolution", ConcurrencyConflict.ResolveWeakly.ToString() } }); } } catch (NoRouteException e) { Logger.Write(LogLevel.Info, () => $"Failed to resolve conflict: {e.Message}"); throw new ConflictResolutionFailedException("Failed to resolve conflict", e); } Logger.Write(LogLevel.Info, () => "Successfully merged conflicted events"); if (stream.StreamVersion != stream.CommitVersion && entity is ISnapshotting && ((ISnapshotting)entity).ShouldTakeSnapshot()) { Logger.Write(LogLevel.Debug, () => $"Taking snapshot of [{typeof(T).FullName}] id [{entity.StreamId}] version {stream.StreamVersion}"); var memento = ((ISnapshotting)entity).TakeSnapshot(); stream.AddSnapshot(memento); } await stream.Commit(commitId, commitHeaders).ConfigureAwait(false); } finally { await _store.Unfreeze <T>(stream.Bucket, stream.StreamId).ConfigureAwait(false); } }