public async Task <IEventStream> GetStream <T>(string bucket, string streamId, ISnapshot snapshot = null) where T : class, IEventSource { var streamName = _streamGen(typeof(T), StreamTypes.Domain, bucket, streamId); Logger.Write(LogLevel.Debug, () => $"Retreiving stream [{streamId}] in bucket [{bucket}] for type {typeof(T).FullName}"); if (_shouldCache) { var cached = _cache.Retreive(streamName) as EventStream <T>; if (cached != null) { HitMeter.Mark(); Logger.Write(LogLevel.Debug, () => $"Found stream [{streamName}] in cache"); return(new EventStream <T>(cached, Builder, this, snapshot)); } MissMeter.Mark(); } while (await CheckFrozen <T>(bucket, streamId).ConfigureAwait(false)) { Logger.Write(LogLevel.Info, () => $"Stream [{streamName}] is frozen - waiting"); Thread.Sleep(100); } Logger.Write(LogLevel.Debug, () => $"Stream [{streamName}] not in cache - reading from store"); var events = await _store.GetEvents(streamName, start : snapshot?.Version + 1L).ConfigureAwait(false); var eventstream = new EventStream <T>(Builder, this, StreamTypes.Domain, bucket, streamId, events, snapshot); await Cache <T>(eventstream).ConfigureAwait(false); Logger.Write(LogLevel.Debug, () => $"Stream [{streamName}] read - version is {eventstream.CommitVersion}"); return(eventstream); }
public async Task <IEventStream> GetStream <T>(string bucket, Id streamId, IEnumerable <Id> parents = null) where T : class, IEventSource { parents = parents ?? new Id[] { }; var streamName = _streamGen(typeof(T), StreamTypes.Domain, bucket, streamId, parents); Logger.Write(LogLevel.Debug, () => $"Retreiving stream [{streamId}] in bucket [{bucket}] for type {typeof(T).FullName}"); var cached = _cache.Retreive(streamName) as IImmutableEventStream; if (cached != null) { HitMeter.Mark(); Logger.Write(LogLevel.Debug, () => $"Found stream [{streamName}] in cache"); return(new EventStream <T>(cached)); } MissMeter.Mark(); while (await CheckFrozen <T>(bucket, streamId, parents).ConfigureAwait(false)) { Logger.Write(LogLevel.Info, () => $"Stream [{streamId}] in bucket [{bucket}] is frozen - waiting"); await Task.Delay(100).ConfigureAwait(false); } Logger.Write(LogLevel.Debug, () => $"Stream [{streamId}] in bucket [{bucket}] not in cache - reading from store"); ISnapshot snapshot = null; if (typeof(ISnapshotting).IsAssignableFrom(typeof(T))) { snapshot = await _snapstore.GetSnapshot <T>(bucket, streamId, parents).ConfigureAwait(false); Logger.Write(LogLevel.Debug, () => { if (snapshot != null) { return($"Retreived snapshot for entity id [{streamId}] bucket [{bucket}] version {snapshot.Version}"); } return($"No snapshot found for entity id [{streamId}] bucket [{bucket}]"); }); } var events = await _store.GetEvents(streamName, start : snapshot?.Version + 1).ConfigureAwait(false); var oobMetadata = await _store.GetMetadata(streamName, OobMetadataKey).ConfigureAwait(false); IEnumerable <OobDefinition> oobs = null; if (!string.IsNullOrEmpty(oobMetadata)) { oobs = JsonConvert.DeserializeObject <IEnumerable <OobDefinition> >(oobMetadata); } var eventstream = new EventStream <T>(bucket, streamId, parents, oobs, events, snapshot); _cache.Cache(streamName, eventstream.Clone()); Logger.Write(LogLevel.Debug, () => $"Stream [{streamId}] in bucket [{bucket}] read - version is {eventstream.CommitVersion}"); return(eventstream); }
public async Task <TEntity> Get <TEntity, TState>(string bucket, Id id, IEntity parent) where TEntity : IEntity <TState> where TState : class, IState, new() { var uow = (Configuration.Settings.LocalContainer.Value ?? Configuration.Settings.Container).Resolve <Aggregates.UnitOfWork.IDomain>(); var factory = EntityFactory.For <TEntity>(); var parents = getParents(parent); // Todo: pass parent instead of Id[]? var snapshot = await _snapstore.GetSnapshot <TEntity>(bucket, id, parents?.Select(x => x.StreamId).ToArray()).ConfigureAwait(false); var events = await _eventstore.GetEvents <TEntity>(bucket, id, parents?.Select(x => x.StreamId).ToArray(), start : snapshot?.Version).ConfigureAwait(false); var entity = factory.Create(bucket, id, parents, events.Select(x => x.Event as IEvent).ToArray(), snapshot?.Payload); (entity as INeedDomainUow).Uow = uow; (entity as INeedEventFactory).EventFactory = _factory; (entity as INeedStore).Store = _eventstore; (entity as INeedStore).OobWriter = _oobstore; (entity as INeedVersionRegistrar).Registrar = _registrar; (entity as INeedChildTracking).Tracker = _childTracker; Logger.DebugEvent("Get", "[{EntityId:l}] bucket [{Bucket:l}] entity [{EntityType:l}] version {Version}", id, bucket, typeof(TEntity).FullName, entity.Version); return(entity); }
public async Task Resolve <TEntity, TState>(TEntity entity, IFullEvent[] uncommitted, Guid commitId, IDictionary <string, string> commitHeaders) where TEntity : IEntity <TState> where TState : IState, new() { var state = entity.State; Logger.Write(LogLevel.Info, () => $"Resolving {uncommitted.Count()} uncommitted events to stream [{entity.Id}] type [{typeof(TEntity).FullName}] bucket [{entity.Bucket}]"); var latestEvents = await _eventstore.GetEvents <TEntity>(entity.Bucket, entity.Id, entity.Parents, entity.Version).ConfigureAwait(false); Logger.Write(LogLevel.Info, () => $"Stream is {latestEvents.Count()} events behind store"); for (var i = 0; i < latestEvents.Length; i++) { state.Apply(latestEvents[i].Event as IEvent); } Logger.Write(LogLevel.Debug, () => "Merging conflicted events"); try { foreach (var u in uncommitted) { if (u.Descriptor.StreamType == StreamTypes.Domain) { entity.Conflict(u.Event as IEvent); } else if (u.Descriptor.StreamType == StreamTypes.OOB) { // Todo: small hack string id = ""; bool transient = true; int daysToLive = -1; id = u.Descriptor.Headers[Defaults.OobHeaderKey]; if (u.Descriptor.Headers.ContainsKey(Defaults.OobTransientKey)) { bool.TryParse(u.Descriptor.Headers[Defaults.OobTransientKey], out transient); } if (u.Descriptor.Headers.ContainsKey(Defaults.OobDaysToLiveKey)) { int.TryParse(u.Descriptor.Headers[Defaults.OobDaysToLiveKey], out daysToLive); } entity.Raise(u.Event as IEvent, id, transient, daysToLive); } } } 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"); await _eventstore.WriteEvents <TEntity>(entity.Bucket, entity.Id, entity.Parents, entity.Uncommitted, commitHeaders).ConfigureAwait(false); }
public async Task Resolve <TEntity, TState>(TEntity entity, IFullEvent[] uncommitted, Guid commitId, IDictionary <string, string> commitHeaders) where TEntity : IEntity <TState> where TState : IState, new() { var state = entity.State; Logger.DebugEvent("Resolver", "Resolving {Events} conflicting events to stream [{Stream:l}] type [{EntityType:l}] bucket [{Bucket:l}]", uncommitted.Count(), entity.Id, typeof(TEntity).FullName, entity.Bucket); var latestEvents = await _eventstore.GetEvents <TEntity>(entity.Bucket, entity.Id, entity.Parents, entity.Version).ConfigureAwait(false); Logger.DebugEvent("Behind", "Stream is {Count} events behind store", latestEvents.Count()); for (var i = 0; i < latestEvents.Length; i++) { state.Apply(latestEvents[i].Event as IEvent); } try { foreach (var u in uncommitted) { if (u.Descriptor.StreamType == StreamTypes.Domain) { entity.Conflict(u.Event as IEvent); } else if (u.Descriptor.StreamType == StreamTypes.OOB) { // Todo: small hack string id = ""; bool transient = true; int daysToLive = -1; id = u.Descriptor.Headers[Defaults.OobHeaderKey]; if (u.Descriptor.Headers.ContainsKey(Defaults.OobTransientKey)) { bool.TryParse(u.Descriptor.Headers[Defaults.OobTransientKey], out transient); } if (u.Descriptor.Headers.ContainsKey(Defaults.OobDaysToLiveKey)) { int.TryParse(u.Descriptor.Headers[Defaults.OobDaysToLiveKey], out daysToLive); } entity.Raise(u.Event as IEvent, id, transient, daysToLive); } } } catch (NoRouteException e) { Logger.WarnEvent("ResolveFailure", e, "Failed to resolve conflict: {ExceptionType} - {ExceptionMessage}", e.GetType().Name, e.Message); throw new ConflictResolutionFailedException("Failed to resolve conflict", e); } await _eventstore.WriteEvents <TEntity>(entity.Bucket, entity.Id, entity.Parents, entity.Uncommitted, commitHeaders).ConfigureAwait(false); }
public async Task Resolve <T>(T entity, IEnumerable <IFullEvent> uncommitted, Guid commitId, IDictionary <string, string> commitHeaders) where T : class, IEventSource { var sourced = (IEventSourced)entity; var stream = sourced.Stream; var streamName = _streamGen(typeof(T), StreamTypes.Domain, stream.Bucket, stream.StreamId, stream.Parents); Logger.Write(LogLevel.Info, () => $"Resolving {uncommitted.Count()} uncommitted events to stream [{stream.StreamId}] type [{typeof(T).FullName}] bucket [{stream.Bucket}]"); try { await _store.Freeze <T>(stream).ConfigureAwait(false); var latestEvents = await _eventstore.GetEvents(streamName, stream.CommitVersion + 1).ConfigureAwait(false); Logger.Write(LogLevel.Info, () => $"Stream is {latestEvents.Count()} events behind store"); sourced.Hydrate(latestEvents.Select(x => x.Event as IEvent)); Logger.Write(LogLevel.Debug, () => "Merging conflicted events"); try { foreach (var u in uncommitted) { sourced.Conflict(u.Event as IEvent, metadata: new Dictionary <string, string> { { "ConflictResolution", ConcurrencyConflict.ResolveStrongly.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.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.Id}] version {stream.StreamVersion}"); var memento = ((ISnapshotting)entity).TakeSnapshot(); stream.AddSnapshot(memento); } await _store.WriteStream <T>(commitId, stream, commitHeaders).ConfigureAwait(false); } finally { await _store.Unfreeze <T>(stream).ConfigureAwait(false); } }
public async Task migrate_from_sqlite_to_sqlserver() { await SetupSrc(); loop: var i = new Random().Next(0, MaxEntities); _src.Advanced.MigrateEventsTo(_dest, "bubu", c => c.BatchSize(50).AddConverters(new RewriteEvent())); var evs = await _dest.GetEvents(_entities[i]); evs.Value.Count.Should().Be(1); var orig = _events[i].CastAs <Event1>(); if (orig == null) { goto loop; } var ev = evs.Value.First() as Event1; ev.Nr.Should().Be(60 + orig.Nr); }
public Task <IFullEvent[]> GetEvents <TEntity>(string bucket, Id streamId, Id[] parents, string oobId, long?start = null, int?count = null) where TEntity : IEntity { var stream = _generator(typeof(TEntity), StreamTypes.OOB, $"{oobId}.{bucket}", streamId, parents); return(_store.GetEvents(stream, start, count)); }
public Task <IEnumerable <IWritableEvent> > Retrieve <T>(string bucket, string streamId, int?skip = null, int?take = null, bool ascending = true) where T : class, IEventSource { var streamName = _streamGen(typeof(T), bucket + ".OOB", streamId); return(!ascending?_store.GetEventsBackwards(streamName) : _store.GetEvents(streamName)); }
public async Task Resolve <T>(T entity, IEnumerable <IFullEvent> uncommitted, Guid commitId, IDictionary <string, string> commitHeaders) where T : class, IEventSource { var sourced = (IEventSourced)entity; // 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, sourced.Stream.Bucket, sourced.Stream.StreamId, sourced.Stream.Parents); var message = new ConflictingEvents { Bucket = sourced.Stream.Bucket, StreamId = sourced.Stream.StreamId, EntityType = typeof(T).AssemblyQualifiedName, Parents = BuildParentList(entity), Events = uncommitted }; var package = new DelayedMessage { MessageId = Guid.NewGuid().ToString(), Headers = new Dictionary <string, string>(), Message = message, Received = DateTime.UtcNow, ChannelKey = streamName }; foreach (var header in commitHeaders) { package.Headers[$"Conflict.{header.Key}"] = header.Value; } await _delay.AddToQueue(ConcurrencyConflict.ResolveWeakly.ToString(), package, streamName) .ConfigureAwait(false); // Todo: make 30 seconds configurable var age = await _delay.Age(ConcurrencyConflict.ResolveWeakly.ToString(), streamName).ConfigureAwait(false); if (!age.HasValue || age < TimeSpan.FromSeconds(30)) { return; } var stream = sourced.Stream; Logger.Write(LogLevel.Info, () => $"Starting weak conflict resolve for stream [{stream.StreamId}] type [{typeof(T).FullName}] bucket [{stream.Bucket}]"); try { await _store.Freeze <T>(stream).ConfigureAwait(false); var delayed = await _delay.Pull(streamName, max : 200).ConfigureAwait(false); // If someone else pulled while we were waiting if (!delayed.Any()) { return; } Logger.Write(LogLevel.Info, () => $"Resolving {delayed.Count()} uncommitted events to stream [{stream.StreamId}] type [{typeof(T).FullName}] bucket [{stream.Bucket}]"); var latestEvents = await _eventstore.GetEvents(streamName, stream.CommitVersion + 1L) .ConfigureAwait(false); Logger.Write(LogLevel.Info, () => $"Stream [{stream.StreamId}] bucket [{stream.Bucket}] is {latestEvents.Count()} events behind store"); sourced.Hydrate(latestEvents.Select(x => x.Event as IEvent)); Logger.Write(LogLevel.Debug, () => $"Merging {delayed.Count()} conflicted events"); try { foreach (var u in delayed.Select(x => x.Message as ConflictingEvents).SelectMany(x => x.Events)) { sourced.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.Id}] version {stream.StreamVersion}"); var memento = ((ISnapshotting)entity).TakeSnapshot(); stream.AddSnapshot(memento); } await _store.WriteStream <T>(commitId, stream, commitHeaders).ConfigureAwait(false); } finally { await _store.Unfreeze <T>(stream).ConfigureAwait(false); } }