예제 #1
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);
            }
        }
예제 #2
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);
        }
예제 #3
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);
            });
        }
        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);
            });
        }
예제 #5
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.Create(stream, batchCount: 100);
            catchup.Subscribe(new BalanceProjector().Trace((p, es) => eventsAggregated.AddRange(es)), projectionStore);

            await catchup.RunUntilCaughtUp();

            store.WriteEvents(streamId, howMany: 100);

            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(101);
            eventsAggregated.Count.Should().Be(101);
        }
        public async Task GuidQueryPartitioner_partitions_guids_fairly()
        {
            var totalNumberOfGuids = 1000;
            var numberOfPartitions = 50;

            var partitions = StreamQuery.Partition(
                Guid.Empty,
                Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"))
                                        .Among(numberOfPartitions);

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

            var partitioner = Stream.Partition<Guid, int, Guid>(
                async (q, p) =>
                    guids.Where(g => new SqlGuid(g).CompareTo(new SqlGuid(p.LowerBoundExclusive)) > 0 &&
                                     new SqlGuid(g).CompareTo(new SqlGuid(p.UpperBoundInclusive)) <= 0),
                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 partitioner.GetStream(partition);

                var catchup = StreamCatchup.Create(stream, batchCount: int.MaxValue);
                catchup.Subscribe(aggregator, store);
                await catchup.RunSingleBatch();
                await Task.Delay(500);

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

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

            Console.WriteLine("\nMissing guids: ");
            foreach (var guid in guids.Where(g => !store.Any(p => p.Value.Contains(g))))
            {
                Console.WriteLine("     " + guid);
            }

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

            store.ToList().ForEach(projection =>
            {
                projection.Value
                          .Count
                          .Should()
                          .BeInRange(approximateGuidsPerPartition - tolerance,
                                     approximateGuidsPerPartition + tolerance);
            });
        }
        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);
            }
        }