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);
        }
Пример #2
0
        public async Task When_advancing_the_cursor_in_a_multi_stream_catchup_throws_then_the_exception_is_surfaced_to_OnError()
        {
            var streams = Enumerable.Range(1, 24)
                          .AsStream()
                          .IntoMany(async(i, from, to) => Stream.Create <string, int>(
                                        query: async q => Enumerable.Range(from, to).Select(ii => ii.ToString()),
                                        advanceCursor: (q, b) => { throw new Exception("oops!"); }));

            var error = default(StreamCatchupError <Projection <string, int> >);

            var catchup = StreamCatchup.All(streams);

            catchup.Subscribe <Projection <string, int>, string, int>(async(sum, batch) => new Projection <string, int>(),
                                                                      (streamId, use) => use(null),
                                                                      onError: e =>
            {
                error = e;
                e.Continue();
            });

            await catchup.RunSingleBatch();

            error.Should().NotBeNull();
            error.Exception.Message.Should().Contain("oops");
        }
        public async Task RunSingleBatch_throws_when_an_aggregator_throws_an_exception()
        {
            var projectionStore = new InMemoryProjectionStore <BalanceProjection>();

            store.WriteEvents(streamId, 100);

            var projector = new BalanceProjector()
                            .Pipeline(async(projection, batch, next) =>
            {
                Throw();
                await next(projection, batch);
            });

            var catchup = StreamCatchup.Create(stream, batchSize: 50);

            catchup.Subscribe(projector, projectionStore);

            Action runSingleBatch = () => catchup.RunSingleBatch().Wait();

            runSingleBatch.ShouldThrow <Exception>()
            .And
            .Message
            .Should()
            .Contain("oops");
        }
        public async Task Getting_and_storing_projections_and_cursors_can_operate_transactionally_via_a_closure()
        {
            BalanceProjection finalProjection = null;

            var catchup = StreamCatchup.Create(stream);
            FetchAndSaveProjection <BalanceProjection> fetchAndSaveProjection = (async(id, callAggregatorPipeline) =>
            {
                using (var transaction = new TransactionScope())
                {
                    // e.g. get the projection / cursor from the store
                    var proj = new BalanceProjection
                    {
                        CursorPosition = 5
                    };

                    proj = await callAggregatorPipeline(proj);

                    finalProjection = proj;

                    // save the projection / cursor back to the store
                    transaction.Complete();
                }
            });

            catchup.Subscribe(new BalanceProjector(), fetchAndSaveProjection);

            store.WriteEvents(streamId, amount: 100m, howMany: 5);

            await catchup.RunSingleBatch();

            finalProjection.Balance.Should().Be(100m);
        }
        public async Task Catchup_can_query_streams_such_that_repeated_data_is_not_queried()
        {
            var queriedEvents = new ConcurrentBag <IDomainEvent>();

            var catchup = StreamCatchup.Create(stream.Trace(onResults: (q, b) =>
            {
                foreach (var e in b)
                {
                    queriedEvents.Add(e);
                }
            }), batchSize: 1);

            catchup.Subscribe(new BalanceProjector());

            store.WriteEvents(streamId);
            await catchup.RunSingleBatch();

            queriedEvents.Select(e => e.StreamRevision)
            .ShouldBeEquivalentTo(new[] { 1 });

            store.WriteEvents(streamId);
            await catchup.RunSingleBatch();

            queriedEvents.Select(e => e.StreamRevision)
            .ShouldBeEquivalentTo(new[] { 1, 2 });

            store.WriteEvents(streamId);
            await catchup.RunSingleBatch();

            queriedEvents.Select(e => e.StreamRevision)
            .ShouldBeEquivalentTo(new[] { 1, 2, 3 });
        }
Пример #6
0
        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");
        }
Пример #7
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);
        }
Пример #8
0
        public async Task Catchup_Poll_keeps_projections_updated_as_new_events_are_written_to_existing_streams()
        {
            var projectionStore = new InMemoryProjectionStore <BalanceProjection>();

            var catchup = StreamCatchup.All(streamSource.StreamPerAggregate(), batchSize: 5);

            catchup.Subscribe(new BalanceProjector(), projectionStore);

            using (catchup.Poll(TimeSpan.FromMilliseconds(10)))
            {
                // write more events
                Task.Run(async() =>
                {
                    foreach (var streamId in streamIds.Take(20))
                    {
                        store.WriteEvents(streamId);
                        await Task.Delay(1);
                    }
                    Console.WriteLine("wrote 20 more events");
                });

                await Wait.Until(() =>
                {
                    var sum = projectionStore.Sum(b => b.Balance);
                    Console.WriteLine("sum is " + sum);
                    return(sum >= 120);
                });
            }
        }
Пример #9
0
        public async Task When_projections_are_cursors_then_catchup_does_not_replay_previously_seen_events()
        {
            var projectionStore  = new InMemoryProjectionStore <BalanceProjection>();
            var eventsAggregated = new List <IDomainEvent>();
            var catchup          = StreamCatchup.All(streamSource.StreamPerAggregate(), batchSize: 100);

            catchup.Subscribe(new BalanceProjector()
                              .Trace((p, es) => eventsAggregated.AddRange(es)), projectionStore);

            await catchup.RunUntilCaughtUp();

            var streamId = streamIds.First();

            store.WriteEvents(streamId, 100m);

            var cursor = await catchup.RunUntilCaughtUp();

            cursor.Position
            .Should()
            .Be("101");

            var balanceProjection = await projectionStore.Get(streamId);

            balanceProjection.Balance.Should().Be(101);
            balanceProjection.CursorPosition.Should().Be(2);
            eventsAggregated.Count.Should().Be(101);
        }
        public async Task When_advancing_the_cursor_throws_then_an_exception_is_thrown()
        {
            var values = Enumerable.Range(1, 20);
            var stream = Stream.Create <int, int>(
                query: q => values.Skip(q.Cursor.Position)
                .Take(q.BatchSize ?? 1000),
                advanceCursor: (q, b) =>
            {
                Throw();
            });

            var catchup = StreamCatchup.Create(stream);

            catchup.Subscribe <Projection <int, int>, int, int>(async(sum, batch) =>
            {
                sum.Value += batch.Count;
                return(sum);
            });

            Action runSingleBatch = () => catchup.RunSingleBatch().Wait();

            runSingleBatch.ShouldThrow <Exception>()
            .And
            .Message
            .Should()
            .Contain("oops!");
        }
        public async Task One_stream_can_transparently_delegate_to_another()
        {
            var upstream = NEventStoreStream.AllEvents(store);

            store.WriteEvents(i => new AccountOpened(), 100);
            var projection = new Projection <int, string>();

            var dependentStream = Stream.Create <int, string>(
                async q =>
            {
                var mapped = upstream.Map(e => new[] { e.Count() });
                var batch  = await mapped.Fetch(q);
                return(batch);
            });

            var catchup = StreamCatchup.Create(dependentStream, batchSize: 50);

            FetchAndSaveProjection <Projection <int, string> > manageProjection = async(id, aggregate) =>
            {
                await aggregate(projection);
            };

            catchup.Subscribe(async(p, b) =>
            {
                p.Value += b.Sum();
                return(p);
            }, manageProjection);

            await catchup.RunSingleBatch();

            Console.WriteLine(projection.ToLogString());

            projection.Value.Should().Be(50);
            projection.CursorPosition.Should().Be("50");
        }
Пример #12
0
        public async Task When_advancing_the_cursor_in_a_single_stream_catchup_throws_then_the_exception_is_surfaced_to_OnError()
        {
            var stream = Stream.Create <int, int>(q => Enumerable.Range(1, 100).Skip(q.Cursor.Position),
                                                  advanceCursor: (q, b) =>
            {
                throw new Exception("oops!");
            });

            var error = default(StreamCatchupError <Projection <int, int> >);

            var catchup = StreamCatchup.Create(stream);

            catchup.Subscribe <Projection <int, int>, int, int>(async(sum, batch) =>
            {
                sum.Value += batch.Count;
                return(sum);
            },
                                                                (streamId, use) => use(new Projection <int, int>()),
                                                                onError: e =>
            {
                error = e;
                e.Continue();
            });

            await catchup.RunSingleBatch();

            error.Should().NotBeNull();
            error.Exception.Message.Should().Contain("oops");
        }
        public async Task A_stream_can_be_derived_from_an_index_projection_in_order_to_perform_a_reduce_operation()
        {
            store.WriteEvents(i => new AccountOpened
            {
                AccountType = i % 2 == 0
                    ? BankAccountType.Checking
                    : BankAccountType.Savings
            }, howMany: 100);

            var eventsByAggregate = streamSource.StreamPerAggregate()
                                    .Trace()
                                    .Map(ss => ss.Select(s => s.Trace()));

            var indexCatchup = StreamCatchup.All(eventsByAggregate, batchSize: 1);
            var index        = new Projection <ConcurrentBag <AccountOpened>, string>
            {
                Value = new ConcurrentBag <AccountOpened>()
            };

            // subscribe a catchup to the updates stream to build up an index
            indexCatchup.Subscribe(
                Aggregator.Create <Projection <ConcurrentBag <AccountOpened> >, IDomainEvent>(async(p, events) =>
            {
                foreach (var e in events.OfType <AccountOpened>()
                         .Where(e => e.AccountType == BankAccountType.Savings))
                {
                    p.Value.Add(e);
                }

                return(p);
            }).Trace(),
                async(streamId, aggregate) => await aggregate(index));

            // create a catchup over the index
            var savingsAccounts = Stream.Create <IDomainEvent, string>(
                "Savings accounts",
                async q => index.Value.SkipWhile(v => q.Cursor.HasReached(v.CheckpointToken))
                .Take(q.BatchSize ?? 1000));
            var savingsAccountsCatchup = StreamCatchup.Create(savingsAccounts);

            var numberOfSavingsAccounts = new Projection <int, int>();

            savingsAccountsCatchup.Subscribe <Projection <int, int>, IDomainEvent, string>(
                manageProjection: async(streamId, aggregate) =>
            {
                numberOfSavingsAccounts = await aggregate(numberOfSavingsAccounts);
            },
                aggregate: async(c, es) =>
            {
                c.Value += es.Count;
                return(c);
            });

            using (indexCatchup.Poll(TimeSpan.FromMilliseconds(100)))
                using (savingsAccountsCatchup.Poll(TimeSpan.FromMilliseconds(50)))
                {
                    await Wait.Until(() => numberOfSavingsAccounts.Value >= 50);
                }
        }
Пример #14
0
        public async Task When_one_projection_fails_its_cursor_is_not_advanced_while_other_projections_cursors_are_advanced()
        {
            var projections = new InMemoryProjectionStore <BalanceProjection>();

            // first catch up all the projections
            var stream              = streamSource.StreamPerAggregate().Trace();
            var catchup             = StreamCatchup.All(stream);
            var initialSubscription = catchup.Subscribe(new BalanceProjector(), projections);
            await catchup.RunUntilCaughtUp();

            initialSubscription.Dispose();

            // write some additional events
            var streamIdsWithoutErrors = streamIds.Take(5).ToList();
            var streamIdsWithErrors    = streamIds.Skip(5).Take(5).ToList();

            foreach (var streamId in streamIdsWithoutErrors.Concat(streamIdsWithErrors))
            {
                store.WriteEvents(streamId, howMany: 10);
            }

            // subscribe a flaky projector
            catchup.Subscribe(new BalanceProjector()
                              .Pipeline(async(projection, batch, next) =>
            {
                var aggregateId = batch.Select(i => i.AggregateId).First();
                if (streamIdsWithErrors.Contains(aggregateId))
                {
                    throw new Exception("oops");
                }

                await next(projection, batch);
            }),
                              projections.AsHandler(),
                              e => e.Continue());

            await catchup.RunSingleBatch();

            var projectionsWithoutErrors = streamIdsWithoutErrors.Select(
                id => projections.Get(id).Result);
            var projectionsWithErrors = streamIdsWithErrors.Select(
                id => projections.Get(id).Result);

            foreach (var projection in projectionsWithoutErrors)
            {
                projection.CursorPosition.Should().Be(11);
                projection.Balance.Should().Be(11);
            }

            foreach (var projection in projectionsWithErrors)
            {
                projection.CursorPosition.Should().Be(1);
                projection.Balance.Should().Be(1);
            }
        }
Пример #15
0
        public async Task GuidQueryPartitioner_partitions_guids_fairly()
        {
            var totalNumberOfGuids = 1000;
            var numberOfPartitions = 50;

            var partitions = Partition.AllGuids()
                             .Among(numberOfPartitions);

            var guids = Enumerable.Range(1, totalNumberOfGuids).Select(_ => Guid.NewGuid()).ToArray();

            var partitioned = Stream.Partitioned <Guid, int, Guid>(
                async(q, p) =>
                guids.Where(g => g.IsWithinPartition(p)),
                advanceCursor: (q, b) => q.Cursor.AdvanceTo(totalNumberOfGuids));

            var aggregator = Aggregator.Create <Projection <HashSet <Guid>, int>, Guid>((p, b) =>
            {
                if (p.Value == null)
                {
                    p.Value = new HashSet <Guid>();
                }
                foreach (var guid in b)
                {
                    p.Value.Add(guid);
                }
            });
            var store = new InMemoryProjectionStore <Projection <HashSet <Guid>, int> >();

            await Task.WhenAll(partitions.Select(async partition =>
            {
                var stream = await partitioned.GetStream(partition);

                var catchup = StreamCatchup.Create(stream, batchSize: int.MaxValue);
                catchup.Subscribe(aggregator, store);
                await catchup.RunSingleBatch();

                var projection = await store.Get(stream.Id);
                Console.WriteLine(partition + ": " + projection.Value.Count);
            }));

            var approximateGuidsPerPartition = totalNumberOfGuids / numberOfPartitions;
            var tolerance = (int)(totalNumberOfGuids * .12);

            store.Sum(p => p.Value.Count).Should().Be(totalNumberOfGuids);

            store.ToList().ForEach(projection =>
            {
                projection.Value
                .Count
                .Should()
                .BeInRange(approximateGuidsPerPartition - tolerance,
                           approximateGuidsPerPartition + tolerance);
            });
        }
Пример #16
0
        public async Task When_multiple_projectors_are_subscribed_then_data_that_both_projections_have_seen_is_not_requeried()
        {
            var streamId      = Guid.NewGuid().ToString();
            var queriedEvents = new ConcurrentBag <IDomainEvent>();

            var balanceProjections = new InMemoryProjectionStore <BalanceProjection>();
            await balanceProjections.Put(streamId, new BalanceProjection
            {
                CursorPosition = 2
            });

            var catchup = StreamCatchup.All(streamSource.StreamPerAggregate()
                                            .Map(ss => ss.Select(s => s.Trace(onResults: (q, b) =>
            {
                foreach (var e in b)
                {
                    queriedEvents.Add(e);
                }
            }))),
                                            Cursor.New("100"),
                                            batchSize: 1);

            catchup.Subscribe(new BalanceProjector(), balanceProjections);

            store.WriteEvents(streamId); // "101" - 1
            store.WriteEvents(streamId); // "102" - 2
            store.WriteEvents(streamId); // "103" - 3

            await catchup.RunSingleBatch();

            queriedEvents.Count
            .Should()
            .Be(1,
                "the first two events should be skipped because of the starting cursor position");
            queriedEvents.Should()
            .ContainSingle(e => e.StreamRevision == 3,
                           "only the most recent event should be queried");

            var accountHistoryProjections = new InMemoryProjectionStore <AccountHistoryProjection>();
            await accountHistoryProjections.Put(streamId, new AccountHistoryProjection
            {
                CursorPosition = 2
            });

            catchup.Subscribe(new AccountHistoryProjector(), accountHistoryProjections);

            store.WriteEvents(streamId);
            await catchup.RunSingleBatch();

            queriedEvents.Select(e => e.StreamRevision)
            .ShouldBeEquivalentTo(new[] { 3, 4 },
                                  "event 3 needs to be repeated because the newly-subscribed aggregator hasn't seen it yet");
        }
Пример #17
0
        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);
        }
Пример #18
0
        public async Task Catchup_cursor_retrieval_can_be_specified_using_catchup_configuration()
        {
            ICursor <string> storedCursor = Cursor.New("3");

            var catchup = StreamCatchup.All(streamSource.StreamPerAggregate(),
                                            batchSize: 1,
                                            manageCursor: async(id, use) => { await use(storedCursor); });

            catchup.Subscribe(new BalanceProjector());

            var cursor = await catchup.RunSingleBatch();

            storedCursor.Position.Should().Be("4");
            cursor.Position.Should().Be("4");
        }
        public async Task Catchup_batch_size_can_be_specified()
        {
            var projectionStore = new InMemoryProjectionStore <BalanceProjection>();

            store.WriteEvents(streamId, howMany: 50);
            var catchup = StreamCatchup.Create(stream, batchSize: 20);

            catchup.Subscribe(new BalanceProjector(), projectionStore);

            await catchup.RunSingleBatch();

            projectionStore.Single()
            .Balance
            .Should()
            .Be(20);
        }
Пример #20
0
        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_RunUntilCaughtUp_runs_until_the_stream_has_no_more_results()
        {
            var projectionStore = new InMemoryProjectionStore <BalanceProjection>();

            store.WriteEvents(streamId, howMany: 999);
            var catchup = StreamCatchup.Create(stream, batchSize: 10);

            catchup.Subscribe(new BalanceProjector(), projectionStore);

            TaskScheduler.UnobservedTaskException += (sender, args) => Console.WriteLine(args.Exception);

            await catchup.RunUntilCaughtUp();

            projectionStore.Single()
            .Balance
            .Should()
            .Be(1000);
        }
        public async Task An_initial_cursor_can_be_specified()
        {
            var projectionStore = new InMemoryProjectionStore <BalanceProjection>();

            store.WriteEvents(streamId, howMany: 999);

            var catchup = StreamCatchup.Create(stream,
                                               initialCursor: Cursor.New(800),
                                               batchSize: 1000);

            catchup.Subscribe(new BalanceProjector(), projectionStore);

            await catchup.RunSingleBatch();

            projectionStore.Sum(b => b.Balance)
            .Should()
            .Be(200);
        }
Пример #24
0
        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 When_one_batch_is_running_a_second_caller_to_RunSingleBatch_can_await_the_completion_of_the_same_batch()
        {
            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);

            var cursor1 = catchup.RunSingleBatch();
            var cursor2 = catchup.RunSingleBatch();

            (await cursor1).Should()
            .BeSameAs(await cursor2);
        }
        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);
        }
Пример #27
0
        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);
        }
Пример #28
0
        public async Task Catchup_cursor_storage_can_be_specified_using_catchup_configuration()
        {
            ICursor <string> storedCursor = null;

            var catchup = StreamCatchup.All(streamSource.StreamPerAggregate(),
                                            batchSize: 10,
                                            manageCursor: async(id, use) =>
            {
                var c = streamSource.StreamPerAggregate().NewCursor();
                await use(c);
                storedCursor = c;
            });

            catchup.Subscribe(new BalanceProjector());

            var cursor = await catchup.RunSingleBatch();

            cursor.Position.Should().Be("10");
            storedCursor.Position.Should().Be("10");
        }
Пример #29
0
        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);
        }
Пример #30
0
        public async Task Catchup_query_cursor_resumes_from_last_position()
        {
            var projectionStore = new InMemoryProjectionStore <BalanceProjection>();

            var catchup = StreamCatchup.All(streamSource.StreamPerAggregate().Trace(), batchSize: 50);

            catchup.Subscribe(new BalanceProjector(), projectionStore);

            await catchup.RunSingleBatch();

            projectionStore.Sum(b => b.Balance)
            .Should()
            .Be(50);

            await catchup.RunSingleBatch();

            projectionStore.Sum(b => b.Balance)
            .Should()
            .Be(100);
        }