public async Task OnError_Continue_prevents_aggregator_exceptions_from_stopping_catchup() { var count = 0; var projectionStore = new InMemoryProjectionStore <BalanceProjection>(); var projector = new BalanceProjector() .Pipeline(async(projection, batch, next) => { Interlocked.Increment(ref count); if (count < 20) { throw new Exception("oops"); } await next(projection, batch); }).Trace(); var catchup = StreamCatchup.All(streamSource.StreamPerAggregate().Trace(), batchSize: 50); catchup.Subscribe(projector, projectionStore.AsHandler(), onError: e => e.Continue()); await catchup.RunSingleBatch(); projectionStore.Count().Should().Be(31); }
public async Task Distributed_catchups_can_store_a_cursor_per_partition() { var cursorStore = new InMemoryProjectionStore <ICursor <int> >(_ => Cursor.New <int>()); var aggregator = Aggregator.Create <Projection <HashSet <string>, int>, string>((p, xs) => { if (p.Value == null) { p.Value = new HashSet <string>(); } foreach (var x in xs) { p.Value.Add(x); } }).Trace(); var catchup = partitionedStream .DistributeAmong(partitions, batchSize: 73, cursorPerPartition: cursorStore.Trace().AsHandler()); catchup.Subscribe(aggregator); await catchup.RunUntilCaughtUp(); cursorStore.Count().Should().Be(26); Enumerable.Range(1, 26).ToList().ForEach(i => { cursorStore.Should().Contain(c => c.Position == i * 100); }); }
public async Task RunSingleBatch_throws_when_an_aggregator_throws_an_exception() { var projectionStore = new InMemoryProjectionStore <BalanceProjection>(); var projector = new BalanceProjector() .Pipeline(async(projection, batch, next) => { if (projectionStore.Count() >= 30) { throw new Exception("oops"); } await next(projection, batch); }); var catchup = StreamCatchup.All(streamSource.StreamPerAggregate(), batchSize: 50); catchup.Subscribe(projector, projectionStore); Action runSingleBatch = () => catchup.RunSingleBatch().Wait(); runSingleBatch.ShouldThrow <Exception>() .And .Message .Should() .Contain("oops"); }
public async Task OnError_Continue_prevents_aggregator_exceptions_from_stopping_catchup() { var projectionStore = new InMemoryProjectionStore <BalanceProjection>(); var count = 0; var projector = new BalanceProjector() .Pipeline(async(projection, batch, next) => { Interlocked.Increment(ref count); Console.WriteLine(count); if (count < 49) { Throw(); } await next(projection, batch); }).Trace(); var catchup = StreamCatchup.Create(stream.Trace(), batchSize: 50); catchup.Subscribe(projector, projectionStore.AsHandler(), onError: e => e.Continue()); await catchup.RunUntilCaughtUp(); projectionStore.Count().Should().Be(1); projectionStore.Single().CursorPosition.Should().Be(1); }
public async Task Catchup_upstream_batch_size_can_be_specified() { var projectionStore = new InMemoryProjectionStore <BalanceProjection>(); var catchup = StreamCatchup.All(streamSource.StreamPerAggregate(), batchSize: 20); catchup.Subscribe(new BalanceProjector(), projectionStore); await catchup.RunSingleBatch(); projectionStore.Count() .Should() .Be(20); }
public async Task Catchup_can_use_a_sequence_of_keys_to_traverse_all_aggregates() { var projectionStore = new InMemoryProjectionStore<BalanceProjection>(); var catchup = StreamCatchup.All(streamSource.StreamPerAggregate()); catchup.Subscribe(new BalanceProjector(), projectionStore); await catchup.RunSingleBatch(); projectionStore.Sum(b => b.Balance) .Should() .Be(100); projectionStore.Count() .Should() .Be(100); }
public async Task Catchup_can_traverse_all_events() { var projectionStore = new InMemoryProjectionStore<BalanceProjection>(); store.WriteEvents(streamId, 999); var catchup = StreamCatchup.Create(stream); catchup.Subscribe(new BalanceProjector(), projectionStore); await catchup.RunSingleBatch(); projectionStore.Sum(b => b.Balance) .Should() .Be(1000); projectionStore.Count() .Should() .Be(1); }
public async Task Catchup_can_use_a_sequence_of_keys_to_traverse_all_aggregates() { var projectionStore = new InMemoryProjectionStore <BalanceProjection>(); var catchup = StreamCatchup.All(streamSource.StreamPerAggregate()); catchup.Subscribe(new BalanceProjector(), projectionStore); await catchup.RunSingleBatch(); projectionStore.Sum(b => b.Balance) .Should() .Be(100); projectionStore.Count() .Should() .Be(100); }
public async Task Catchup_can_traverse_all_events() { var projectionStore = new InMemoryProjectionStore <BalanceProjection>(); store.WriteEvents(streamId, 999); var catchup = StreamCatchup.Create(stream); catchup.Subscribe(new BalanceProjector(), projectionStore); await catchup.RunSingleBatch(); projectionStore.Sum(b => b.Balance) .Should() .Be(1000); projectionStore.Count() .Should() .Be(1); }
public async Task When_one_batch_is_running_a_second_call_to_RunSingleBatch_will_not_do_anything() { var projectionStore = new InMemoryProjectionStore<BalanceProjection>(); var catchup = StreamCatchup.Create(stream, batchCount: 1); catchup.Subscribe(new BalanceProjector() .Pipeline(async (projection, batch, next) => { await Task.Delay(500); await next(projection, batch); }), projectionStore); await Task.WhenAll(catchup.RunSingleBatch(), catchup.RunSingleBatch()); projectionStore.Count() .Should() .Be(1); }
public async Task When_one_batch_is_running_a_second_call_to_RunSingleBatch_will_not_do_anything() { var projectionStore = new InMemoryProjectionStore <BalanceProjection>(); var catchup = StreamCatchup.Create(stream, batchSize: 1); catchup.Subscribe(new BalanceProjector() .Pipeline(async(projection, batch, next) => { await Task.Delay(500); await next(projection, batch); }), projectionStore); await Task.WhenAll(catchup.RunSingleBatch(), catchup.RunSingleBatch()); projectionStore.Count() .Should() .Be(1); }
public async Task Catchup_RunUntilCaughtUp_runs_until_the_stream_has_no_more_results() { var projectionStore = new InMemoryProjectionStore <BalanceProjection>(); var catchup = StreamCatchup.All(streamSource.StreamPerAggregate(), batchSize: 10); catchup.Subscribe(new BalanceProjector(), projectionStore); TaskScheduler.UnobservedTaskException += (sender, args) => Console.WriteLine(args.Exception); await catchup.RunUntilCaughtUp(); projectionStore.Sum(b => b.Balance) .Should() .Be(100); projectionStore.Count() .Should() .Be(100); }
public async Task Stream_traversal_can_continue_from_upstream_cursor_that_was_returned_by_RunSingleBatch() { var catchup = StreamCatchup.All(streamSource.StreamPerAggregate(), batchSize: 50); catchup.Subscribe(new BalanceProjector(), new InMemoryProjectionStore <BalanceProjection>()); var cursor = await catchup.RunSingleBatch(); var projectionStore = new InMemoryProjectionStore <BalanceProjection>(); catchup = StreamCatchup.All(streamSource.StreamPerAggregate(), cursor); catchup.Subscribe(new BalanceProjector(), projectionStore); await catchup.RunSingleBatch(); projectionStore.Count() .Should() .Be(50); }
public async Task Catchup_starting_cursor_can_be_specified() { var catchup = StreamCatchup.All(streamSource.StreamPerAggregate(), batchSize: 50); catchup.Subscribe(new BalanceProjector(), new InMemoryProjectionStore <BalanceProjection>()); var projectionStore = new InMemoryProjectionStore <BalanceProjection>(); var updatedStreams = streamSource.StreamPerAggregate(); var cursor = updatedStreams.NewCursor(); cursor.AdvanceTo("50"); catchup = StreamCatchup.All(updatedStreams, cursor); catchup.Subscribe(new BalanceProjector(), projectionStore); await catchup.RunSingleBatch(); projectionStore.Count() .Should() .Be(50); }
public async Task When_an_aggregation_fails_then_the_projection_is_not_updated() { var projections = new InMemoryProjectionStore<BalanceProjection>(); var catchup = StreamCatchup.All(streamSource.StreamPerAggregate().Trace()); // subscribe a flaky projector catchup.Subscribe(new BalanceProjector() .Pipeline(async (projection, batch, next) => { bool shouldThrow = true; // declared outside to nudge the compiler into inferring the correct .Pipeline overload if (shouldThrow) { throw new Exception("oops"); } await next(projection, batch); }), projections.AsHandler(), e => e.Continue()); await catchup.RunSingleBatch(); projections.Count().Should().Be(0); }
public async Task When_an_aggregation_fails_then_the_projection_is_not_updated() { var projections = new InMemoryProjectionStore <BalanceProjection>(); var catchup = StreamCatchup.All(streamSource.StreamPerAggregate().Trace()); // subscribe a flaky projector catchup.Subscribe(new BalanceProjector() .Pipeline(async(projection, batch, next) => { bool shouldThrow = true; // declared outside to nudge the compiler into inferring the correct .Pipeline overload if (shouldThrow) { throw new Exception("oops"); } await next(projection, batch); }), projections.AsHandler(), e => e.Continue()); await catchup.RunSingleBatch(); projections.Count().Should().Be(0); }
public async Task OnError_Continue_prevents_aggregator_exceptions_from_stopping_catchup() { var count = 0; var projectionStore = new InMemoryProjectionStore<BalanceProjection>(); var projector = new BalanceProjector() .Pipeline(async (projection, batch, next) => { Interlocked.Increment(ref count); if (count < 20) { throw new Exception("oops"); } await next(projection, batch); }).Trace(); var catchup = StreamCatchup.All(streamSource.StreamPerAggregate().Trace(), batchSize: 50); catchup.Subscribe(projector, projectionStore.AsHandler(), onError: e => e.Continue()); await catchup.RunSingleBatch(); projectionStore.Count().Should().Be(31); }
public async Task Stream_traversal_can_continue_from_upstream_cursor_that_was_returned_by_RunSingleBatch() { var catchup = StreamCatchup.All(streamSource.StreamPerAggregate(), batchSize: 50); catchup.Subscribe(new BalanceProjector(), new InMemoryProjectionStore<BalanceProjection>()); var cursor = await catchup.RunSingleBatch(); var projectionStore = new InMemoryProjectionStore<BalanceProjection>(); catchup = StreamCatchup.All(streamSource.StreamPerAggregate(), cursor); catchup.Subscribe(new BalanceProjector(), projectionStore); await catchup.RunSingleBatch(); projectionStore.Count() .Should() .Be(50); }
public async Task OnError_Continue_prevents_aggregator_exceptions_from_stopping_catchup() { var projectionStore = new InMemoryProjectionStore<BalanceProjection>(); var count = 0; var projector = new BalanceProjector() .Pipeline(async (projection, batch, next) => { Interlocked.Increment(ref count); Console.WriteLine(count); if (count < 49) { Throw(); } await next(projection, batch); }).Trace(); var catchup = StreamCatchup.Create(stream.Trace(), batchCount: 50); catchup.Subscribe(projector, projectionStore.AsHandler(), onError: e => e.Continue()); await catchup.RunUntilCaughtUp(); projectionStore.Count().Should().Be(1); projectionStore.Single().CursorPosition.Should().Be(1); }
public async Task Catchup_RunUntilCaughtUp_runs_until_the_stream_has_no_more_results() { var projectionStore = new InMemoryProjectionStore<BalanceProjection>(); var catchup = StreamCatchup.All(streamSource.StreamPerAggregate(), batchSize: 10); catchup.Subscribe(new BalanceProjector(), projectionStore); TaskScheduler.UnobservedTaskException += (sender, args) => Console.WriteLine(args.Exception); await catchup.RunUntilCaughtUp(); projectionStore.Sum(b => b.Balance) .Should() .Be(100); projectionStore.Count() .Should() .Be(100); }
public async Task RunSingleBatch_throws_when_an_aggregator_throws_an_exception() { var projectionStore = new InMemoryProjectionStore<BalanceProjection>(); var projector = new BalanceProjector() .Pipeline(async (projection, batch, next) => { if (projectionStore.Count() >= 30) { throw new Exception("oops"); } await next(projection, batch); }); var catchup = StreamCatchup.All(streamSource.StreamPerAggregate(), batchSize: 50); catchup.Subscribe(projector, projectionStore); Action runSingleBatch = () => catchup.RunSingleBatch().Wait(); runSingleBatch.ShouldThrow<Exception>() .And .Message .Should() .Contain("oops"); }
public async Task Distributed_catchups_can_store_a_cursor_per_partition() { var cursorStore = new InMemoryProjectionStore<ICursor<int>>(_ => Cursor.New<int>()); var aggregator = Aggregator.Create<Projection<HashSet<string>, int>, string>((p, xs) => { if (p.Value == null) { p.Value = new HashSet<string>(); } foreach (var x in xs) { p.Value.Add(x); } }).Trace(); var catchup = partitionedStream .DistributeAmong(partitions, batchSize: 73, cursorPerPartition: cursorStore.Trace().AsHandler()); catchup.Subscribe(aggregator); await catchup.RunUntilCaughtUp(); cursorStore.Count().Should().Be(26); Enumerable.Range(1, 26).ToList().ForEach(i => { cursorStore.Should().Contain(c => c.Position == i*100); }); }
public async Task Catchup_upstream_batch_size_can_be_specified() { var projectionStore = new InMemoryProjectionStore<BalanceProjection>(); var catchup = StreamCatchup.All(streamSource.StreamPerAggregate(), batchSize: 20); catchup.Subscribe(new BalanceProjector(), projectionStore); await catchup.RunSingleBatch(); projectionStore.Count() .Should() .Be(20); }
public async Task Catchup_starting_cursor_can_be_specified() { var catchup = StreamCatchup.All(streamSource.StreamPerAggregate(), batchSize: 50); catchup.Subscribe(new BalanceProjector(), new InMemoryProjectionStore<BalanceProjection>()); var projectionStore = new InMemoryProjectionStore<BalanceProjection>(); var updatedStreams = streamSource.StreamPerAggregate(); var cursor = updatedStreams.NewCursor(); cursor.AdvanceTo("50"); catchup = StreamCatchup.All(updatedStreams, cursor); catchup.Subscribe(new BalanceProjector(), projectionStore); await catchup.RunSingleBatch(); projectionStore.Count() .Should() .Be(50); }