public async Task AppendsEventWhenLastEventIsFromDifferentDay() { var now = new DateTime(2019, 12, 25, 12, 00, 00); var utcNow = now.ToUniversalTime(); var clockProviderStub = new ClockProviderStub(now); var inMemoryStreamStore = new InMemoryStreamStore(() => utcNow); var clockHasTicked = new ClockHasTicked(new DateTime(2019, 12, 24, 12, 00, 00)); var cancellationToken = CancellationToken.None; await inMemoryStreamStore.AppendToStream( streamId : new StreamId(ClockTickingService.ClockStreamId), expectedVersion : ExpectedVersion.Any, message : new NewStreamMessage( messageId: Guid.NewGuid(), type: _eventMapping.GetEventName(clockHasTicked.GetType()), jsonData: JsonConvert.SerializeObject(clockHasTicked)), cancellationToken : cancellationToken); var sut = new ClockTickingService(inMemoryStreamStore, clockProviderStub); await sut.StartAsync(cancellationToken); var forwards = await inMemoryStreamStore.ReadAllForwards(Position.Start, Int32.MaxValue, true, cancellationToken); forwards.Messages.Should().HaveCount(2); var message = forwards.Messages[1]; message.StreamId.Should().Be(ClockTickingService.ClockStreamId); message.StreamVersion.Should().Be(1); var messageData = JsonConvert.DeserializeObject <ClockHasTicked>(await message.GetJsonData(cancellationToken)); messageData.DateTime.Should().Be(now); }
private static async Task <long> AddSqlStreamStore( Func <IStreamStore> getStreamStore, Func <ConcurrentUnitOfWork> getUnitOfWork, EventMapping eventMapping, EventSerializer eventSerializer, CommandMessage message, CancellationToken ct) { var aggregate = getUnitOfWork().GetChanges().SingleOrDefault(); if (aggregate == null) { return(-1L); } if (!message.Metadata.ContainsKey("CommandId")) { message.Metadata.Add("CommandId", message.CommandId); } var i = 1; var result = await getStreamStore().AppendToStream( aggregate.Identifier, aggregate.ExpectedVersion, aggregate.Root.GetChangesWithMetadata() .Select(o => new NewStreamMessage( messageId: Deterministic.Create(Deterministic.Namespaces.Events, $"{message.CommandId}-{i++}"), type: eventMapping.GetEventName(o.Event.GetType()), jsonData: eventSerializer.SerializeObject(o.Event), jsonMetadata: eventSerializer.SerializeObject(GetMetadata(message.Metadata, o.Metadata)))) .ToArray(), ct); return(result.CurrentPosition); }
private Func<TestMessage, Task<AppendResult>> CreateMessageStorerAndStartHandling(ConnectedProjection<TestDbContext> projection) { var eventMapping = new EventMapping(EventMapping.DiscoverEventNamesInAssembly(typeof(RunnerTests).Assembly)); var testRunner = new TestRunner( new EnvelopeFactory( eventMapping, new EventDeserializer(JsonConvert.DeserializeObject)), new Logger<TestRunner>(new LoggerFactory()), projection); var options = new DbContextOptionsBuilder<TestDbContext>() .UseInMemoryDatabase("testing") .Options; using (var testDbContext = new TestDbContext(options)) { testDbContext.Database.EnsureDeleted(); } var inMemoryStreamStore = new InMemoryStreamStore(() => DateTime.UtcNow); testRunner.Handle( inMemoryStreamStore, () => new Owned<TestDbContext>(new TestDbContext(options), this)); return async message => await inMemoryStreamStore.AppendToStream(new StreamId("Bla"), ExpectedVersion.Any, new NewStreamMessage(Guid.NewGuid(), eventMapping.GetEventName(typeof(TestMessage)), JsonConvert.SerializeObject(message))); }
private static async Task <long> AddSqlStreamStore( Func <IStreamStore> getStreamStore, Func <ConcurrentUnitOfWork> getUnitOfWork, EventMapping eventMapping, EventSerializer eventSerializer, CommandMessage message, CancellationToken ct) { var uow = getUnitOfWork(); var aggregate = uow.GetChanges().SingleOrDefault(); if (aggregate == null) { return(-1L); } var streamStore = getStreamStore(); if (!message.Metadata.ContainsKey("CommandId")) { message.Metadata.Add("CommandId", message.CommandId); } var i = 1; var events = aggregate.Root.GetChangesWithMetadata().ToImmutableList(); var changes = events .Select(o => new NewStreamMessage( messageId: Deterministic.Create(Deterministic.Namespaces.Events, $"{message.CommandId}-{i++}"), type: eventMapping.GetEventName(o.Event.GetType()), jsonData: eventSerializer.SerializeObject(o.Event), jsonMetadata: eventSerializer.SerializeObject(GetMetadata(message.Metadata, o.Metadata)))) .ToArray(); var result = await streamStore.AppendToStream( aggregate.Identifier, aggregate.ExpectedVersion, changes, ct); if (aggregate.Root is ISnapshotable support) { await CreateSnapshot( support, new SnapshotStrategyContext( aggregate, events, result.CurrentPosition), streamStore, uow, eventMapping, eventSerializer, ct); } return(result.CurrentPosition); // Position of the last event written }
public static ICommandHandlerBuilder <IRoadRegistryContext, TCommand> UseRoadRegistryContext <TCommand>( this ICommandHandlerBuilder <TCommand> builder, IStreamStore store, IRoadNetworkSnapshotReader snapshotReader, EventEnricher enricher) { if (store == null) { throw new ArgumentNullException(nameof(store)); } if (snapshotReader == null) { throw new ArgumentNullException(nameof(snapshotReader)); } if (enricher == null) { throw new ArgumentNullException(nameof(enricher)); } return(builder.Pipe <IRoadRegistryContext>(next => async(message, ct) => { var map = new EventSourcedEntityMap(); var context = new RoadRegistryContext(map, store, snapshotReader, SerializerSettings, EventMapping); await next(context, message, ct); foreach (var entry in map.Entries) { var events = entry.Entity.TakeEvents(); if (events.Length != 0) { var messageId = message.MessageId.ToString("N"); var version = entry.ExpectedVersion; Array.ForEach(events, @event => enricher(@event)); var messages = Array.ConvertAll( events, @event => new NewStreamMessage( Deterministic.Create(Deterministic.Namespaces.Events, $"{messageId}-{version++}"), EventMapping.GetEventName(@event.GetType()), JsonConvert.SerializeObject(@event, SerializerSettings) )); await store.AppendToStream(entry.Stream, entry.ExpectedVersion, messages, ct); } } } )); }
public async Task WhenMessageIsAppended() { var mapping = new EventMapping(EventMapping.DiscoverEventNamesInAssembly(typeof(RoadNetworkEvents).Assembly)); var settings = EventsJsonSerializerSettingsProvider.CreateSerializerSettings(); var archiveStream = new StreamName("archive-1"); var commandStream = new StreamName("road-network-commands"); var id = Guid.NewGuid(); var reaction = new ReactionScenarioBuilder() .Given(new RecordedEvent(archiveStream, new Messages.UploadRoadNetworkChangesArchive { ArchiveId = "123" }).WithMessageId(id)) .Then(new RecordedEvent(commandStream, new Messages.UploadRoadNetworkChangesArchive { ArchiveId = "123" }).WithMessageId(id).WithMetadata(new { Position = 1 })) .Build(); var sut = new Subscriber(_store, commandStream); using (sut) { sut.Start(); //Act foreach (var stream in reaction.Givens.GroupBy(given => given.Stream)) { await _store.AppendToStream( stream.Key, ExpectedVersion.NoStream, stream.Select((given, index) => new NewStreamMessage( Deterministic.Create(Deterministic.Namespaces.Events, $"{given.Stream}-{index}"), mapping.GetEventName(given.Event.GetType()), JsonConvert.SerializeObject(given.Event, settings), given.Metadata != null ? JsonConvert.SerializeObject(given.Metadata, settings) : null )).ToArray()); } //Await var page = await _store.ReadStreamForwards(commandStream, StreamVersion.Start, 1); while (page.Status == PageReadStatus.StreamNotFound) { page = await _store.ReadStreamForwards(commandStream, StreamVersion.Start, 1); } //Assert //Assert.Equal(_messageId, page.Messages[0].MessageId); } await sut.Disposed; }
private async Task AppendClockHasTicked( CancellationToken cancellationToken, StreamId clockStreamId, EventMapping eventMapping) { var clockHasTicked = new ClockHasTicked(_clockProvider.Now); await _streamStore.AppendToStream( streamId : clockStreamId, expectedVersion : ExpectedVersion.Any, message : new NewStreamMessage( messageId: Guid.NewGuid(), type: eventMapping.GetEventName(clockHasTicked.GetType()), jsonData: JsonConvert.SerializeObject(clockHasTicked)), cancellationToken : cancellationToken); }
public async Task WriteAsync(IEnumerable <StreamEvent> events) { if (events == null) { throw new ArgumentNullException(nameof(events)); } var expectedVersions = new ConcurrentDictionary <StreamId, int>(); foreach (var batch in events.Batch(1000)) { foreach (var stream in batch.GroupBy(item => item.Stream, item => item.Event)) { if (!expectedVersions.TryGetValue(stream.Key, out var expectedVersion)) { expectedVersion = ExpectedVersion.NoStream; } var watch = Stopwatch.StartNew(); var appendResult = await _streamStore.AppendToStream( stream.Key, expectedVersion, stream .Select(@event => new NewStreamMessage( Deterministic.Create(Deterministic.Namespaces.Events, $"{stream.Key}-{expectedVersion++}"), Mapping.GetEventName(@event.GetType()), JsonConvert.SerializeObject(@event, SerializerSettings), JsonConvert.SerializeObject(new Dictionary <string, string> { { "$version", "0" } }, SerializerSettings) )) .ToArray() ); _logger.LogInformation("Append took {0}ms for stream {1}@{2}", watch.ElapsedMilliseconds, stream.Key, appendResult.CurrentVersion); expectedVersions[stream.Key] = appendResult.CurrentVersion; } } }
// private class PointMComparer : BaseTypeComparer // { // public PointMComparer(RootComparer comparer) // :base(comparer) // { // } // // public override void CompareType(CompareParms parms) // { // var left = (PointM)parms.Object1; // var right = (PointM)parms.Object2; // if(!Equals(left.X, right.X) // || !Equals(left.Y, right.Y) // || !Equals(left.Z, right.Z) // || !Equals(left.M, right.M)) // { // var difference = new Difference // { // Object1 = left, // Object1TypeName = left.GetType().Name, // Object1Value = left.ToString(), // Object2 = right, // Object2TypeName = right.GetType().Name, // Object2Value = right.ToString(), // ParentObject1 = parms.ParentObject1, // ParentObject2 = parms.ParentObject2 // }; // parms.Result.Differences.Add(difference); // } // } // // public override bool IsTypeMatch(Type type1, Type type2) // { // return type1 == typeof(PointM) && type2 == typeof(PointM); // } // } private async Task <long> WriteGivens(RecordedEvent[] givens) { var checkpoint = SqlStreamStore.Streams.Position.Start; foreach (var stream in givens.GroupBy(given => given.Stream)) { var result = await _store.AppendToStream( _converter(new StreamName(stream.Key)).ToString(), ExpectedVersion.NoStream, stream.Select((given, index) => new NewStreamMessage( Deterministic.Create(Deterministic.Namespaces.Events, $"{given.Stream}-{index}"), _mapping.GetEventName(given.Event.GetType()), JsonConvert.SerializeObject(given.Event, _settings) )).ToArray()); checkpoint = result.CurrentPosition + 1; } return(checkpoint); }
public async Task <long> PersistFacts(Fact[] facts) { var factsByAggregate = facts.GroupBy(x => x.Identifier); AppendResult result = null; foreach (var aggregateWithEvents in factsByAggregate) { result = await _streamStore.AppendToStream( aggregateWithEvents.Key, ExpectedVersion.Any, aggregateWithEvents .Select(e => new NewStreamMessage( Guid.NewGuid(), _eventMapping.GetEventName(e.Event.GetType()), _eventSerializer.SerializeObject(e.Event))).ToArray()); } return(result?.CurrentPosition ?? await _streamStore.ReadHeadPosition()); }
private static async Task CreateSnapshot( ISnapshotable snapshotSupport, SnapshotStrategyContext context, IStreamStore streamStore, ConcurrentUnitOfWork uow, EventMapping eventMapping, EventSerializer eventSerializer, CancellationToken ct) { if (!snapshotSupport.Strategy.ShouldCreateSnapshot(context)) { return; } var snapshot = snapshotSupport.TakeSnapshot(); if (snapshot == null) { throw new InvalidOperationException("Snapshot missing."); } var snapshotContainer = new SnapshotContainer { Data = eventSerializer.SerializeObject(snapshot), Info = { Type = eventMapping.GetEventName(snapshot.GetType()), Position = context.SnapshotPosition } }; await streamStore.AppendToStream( uow.GetSnapshotIdentifier(context.Aggregate.Identifier), ExpectedVersion.Any, new NewStreamMessage( Deterministic.Create(Deterministic.Namespaces.Events, $"snapshot-{context.SnapshotPosition}"), $"SnapshotContainer<{snapshotContainer.Info.Type}>", eventSerializer.SerializeObject(snapshotContainer)), ct); }