Exemple #1
0
        public void When_a_catchup_has_been_waiting_for_several_poll_intervals_it_only_runs_once()
        {
            // arrange
            int numberOfEventsToWrite = Any.Int(10, 20);

            Events.Write(numberOfEventsToWrite);
            var testScheduler = new TestScheduler();

            var projector1 = new Projector <IEvent>(() => new ReadModels1DbContext())
            {
                OnUpdate = (work, e) =>
                {
                    // create some delay so that catchup2 will attempt to poll multiple times
                    testScheduler.Sleep(1000);
                }
            };
            var projector2            = new Projector <IEvent>(() => new ReadModels1DbContext());
            var catchup2StatusReports = new List <ReadModelCatchupStatus>();

            using (var catchup1 = CreateReadModelCatchup <ReadModels1DbContext>(projector1))
                using (var catchup2 = CreateReadModelCatchup <ReadModels1DbContext>(projector2))
                {
                    bool catchup1Disposed = false;
                    catchup1.Progress.ForEachAsync(s =>
                    {
                        Console.WriteLine("catchup1: " + s);

                        // when the batch is done, dispose, which should allow catchup2 to try
                        if (s.IsEndOfBatch)
                        {
                            Console.WriteLine("disposing catchup1");
                            catchup1.Dispose();
                            catchup1Disposed = true;
                        }
                    });
                    catchup2.Progress.ForEachAsync(s =>
                    {
                        catchup2StatusReports.Add(s);
                        Console.WriteLine("catchup2: " + s);
                    });

                    // act
                    var scheduler1 = new SchedulerWatcher(testScheduler, "scheduler1");
                    catchup1.PollEventStore(TimeSpan.FromSeconds(1), scheduler1);

                    var scheduler2 = new SchedulerWatcher(testScheduler, "scheduler2");
                    scheduler2.Schedule(TimeSpan.FromSeconds(1.5),
                                        () =>
                    {
                        Console.WriteLine("catchup2 polling starting");
                        // use a higher poll frequency so the poll timer fires many times while catchup1 is running
                        catchup2.PollEventStore(TimeSpan.FromSeconds(.5), scheduler2);
                    });

                    while (!catchup1Disposed)
                    {
                        testScheduler.AdvanceBy(TimeSpan.FromSeconds(.1).Ticks);
                    }
                    testScheduler.AdvanceBy(TimeSpan.FromSeconds(1).Ticks);
                }

            // assert
            catchup2StatusReports.Count(s => s.IsStartOfBatch)
            .Should()
            .Be(1);
        }
Exemple #2
0
        public void When_one_concurrent_catchup_instance_terminates_due_to_eventstore_connection_loss_then_another_tries_to_take_over_immediately()
        {
            // arrange
            var numberOfEventsToWrite = 100;

            Events.Write(numberOfEventsToWrite);

            var projector1            = new Projector <IEvent>(() => new ReadModels1DbContext());
            var projector2            = new Projector <IEvent>(() => new ReadModels1DbContext());
            var catchup1StatusReports = new List <ReadModelCatchupStatus>();
            var catchup2StatusReports = new List <ReadModelCatchupStatus>();

            DbConnection dbConnection1 = null;

            using (var catchup1 = CreateReadModelCatchup <ReadModels1DbContext>(() =>
            {
                var eventStoreDbContext = EventStoreDbContext();
                dbConnection1 = ((IObjectContextAdapter)eventStoreDbContext).ObjectContext.Connection;
                return(eventStoreDbContext);
            }, projectors: projector1))
                using (var catchup2 = CreateReadModelCatchup <ReadModels1DbContext>(projector2))
                {
                    catchup1.Progress.ForEachAsync(s =>
                    {
                        catchup1StatusReports.Add(s);
                        Console.WriteLine("catchup1: " + s);

                        // when we've processed one event, cancel this catchup
                        dbConnection1.Close();
                    });
                    catchup2.Progress.ForEachAsync(s =>
                    {
                        catchup2StatusReports.Add(s);
                        Console.WriteLine("catchup2: " + s);
                    });

                    // act
                    catchup1.PollEventStore(TimeSpan.FromSeconds(10), new NewThreadScheduler());

                    new NewThreadScheduler().Schedule(TimeSpan.FromSeconds(5),
                                                      () =>
                    {
                        Console.WriteLine("scheduling catchup2 polling");
                        catchup2.PollEventStore(TimeSpan.FromSeconds(10), new NewThreadScheduler());
                    });

                    var waitingOnEventId = HighestEventId + numberOfEventsToWrite;
                    Console.WriteLine($"waiting on event id {waitingOnEventId} to be processed");
                    catchup1.Progress
                    .Merge(catchup2.Progress)
                    .FirstAsync(s => s.CurrentEventId == waitingOnEventId)
                    .Timeout(DefaultTimeout)
                    .Wait();
                }

            // assert
            catchup1StatusReports.Count(s => !s.IsStartOfBatch)
            .Should()
            .Be(1, "sanity check that catchup1 polled");
            catchup2StatusReports.Count(s => !s.IsStartOfBatch)
            .Should()
            .Be(numberOfEventsToWrite - 1);
            catchup2StatusReports.Count(s => s.IsStartOfBatch)
            .Should()
            .Be(1, "sanity check that catchup2 polled");

            var expected = Enumerable.Range((int)HighestEventId + 1, numberOfEventsToWrite);

            Console.WriteLine("expected: " + expected.ToLogString());
            var processed = catchup1StatusReports.Concat(catchup2StatusReports).Where(s => !s.IsStartOfBatch).Select(s => s.CurrentEventId).ToArray();

            Console.WriteLine("actual: " + processed.ToLogString());
            processed.ShouldBeEquivalentTo(expected);
        }
Exemple #3
0
        public void When_one_concurrent_catchup_instance_terminates_due_to_deliberate_disposal_then_another_tries_to_take_over_immediately()
        {
            // arrange
            int numberOfEventsToWrite = 15;

            Events.Write(numberOfEventsToWrite);
            var scheduler = new TestScheduler();

            var projector1            = new Projector <IEvent>(() => new ReadModels1DbContext());
            var projector2            = new Projector <IEvent>(() => new ReadModels1DbContext());
            var catchup1StatusReports = new List <ReadModelCatchupStatus>();
            var catchup2StatusReports = new List <ReadModelCatchupStatus>();

            using (var catchup1 = CreateReadModelCatchup <ReadModels1DbContext>(projector1))
                using (var catchup2 = CreateReadModelCatchup <ReadModels1DbContext>(projector2))
                {
                    catchup1.Progress.ForEachAsync(s =>
                    {
                        catchup1StatusReports.Add(s);
                        Console.WriteLine("catchup1: " + s);

                        // when we've processed at least one event, cancel this catchup
                        if (!s.IsStartOfBatch && s.NumberOfEventsProcessed > 0)
                        {
                            catchup1.Dispose();
                        }
                    });
                    catchup2.Progress.ForEachAsync(s =>
                    {
                        catchup2StatusReports.Add(s);
                        Console.WriteLine("catchup2: " + s);
                    });

                    // act
                    catchup1.PollEventStore(TimeSpan.FromSeconds(3), scheduler);

                    scheduler.Schedule(TimeSpan.FromSeconds(.5),
                                       () =>
                    {
                        Console.WriteLine("scheduling catchup2 polling");
                        catchup2.PollEventStore(TimeSpan.FromSeconds(3), scheduler);
                    });
                    scheduler.AdvanceBy(TimeSpan.FromSeconds(3.5).Ticks);
                }

            // assert
            catchup1StatusReports.Count(s => !s.IsStartOfBatch)
            .Should()
            .Be(1, "sanity check that catchup1 polled");
            catchup2StatusReports.Count(s => !s.IsStartOfBatch)
            .Should()
            .Be(numberOfEventsToWrite - 1);
            catchup2StatusReports.Count(s => s.IsStartOfBatch)
            .Should()
            .Be(1, "sanity check that catchup2 polled");

            var expected = Enumerable.Range((int)HighestEventId + 1, numberOfEventsToWrite);

            Console.WriteLine("expected: " + expected.ToLogString());
            var processed = catchup1StatusReports.Concat(catchup2StatusReports).Where(s => !s.IsStartOfBatch).Select(s => s.CurrentEventId).ToArray();

            Console.WriteLine("actual: " + processed.ToLogString());
            processed.ShouldBeEquivalentTo(expected);
        }
Exemple #4
0
        public void Events_committed_to_the_event_store_are_caught_up_by_multiple_independent_read_model_stores()
        {
            var productName = Any.Paragraph(4);

            var projector1 = new Projector <Order.ItemAdded>(() => new ReadModels1DbContext())
            {
                OnUpdate = (work, e) => new ReadModels1DbContext().DisposeAfter(db => UpdateReservedInventory(db, e))
            };
            var projector2 = new Projector <Order.ItemAdded>(() => new ReadModels2DbContext())
            {
                OnUpdate = (work, e) => new ReadModels2DbContext().DisposeAfter(db => UpdateReservedInventory(db, e))
            };
            var numberOfEvents = Any.Int(10, 50);

            using (var disposables = new CompositeDisposable())
                using (var catchup1 = CreateReadModelCatchup <ReadModels1DbContext>(projector1))
                    using (var catchup2 = CreateReadModelCatchup <ReadModels2DbContext>(projector2))
                    {
                        catchup1.Progress.ForEachAsync(p => Console.WriteLine("catchup1: " + p));
                        catchup2.Progress.ForEachAsync(p => Console.WriteLine("catchup2: " + p));

                        Action <string, ThreadStart> startThread =
                            (name, start) =>
                        {
                            var thread = new Thread(() =>
                            {
                                Console.WriteLine($"starting thread ({Thread.CurrentThread.ManagedThreadId})");
                                start();
                                Console.WriteLine($"ended thread ({Thread.CurrentThread.ManagedThreadId})");
                            });
                            thread.Name = name;
                            thread.Start();
                            disposables.Add(Disposable.Create(thread.Abort));
                        };

                        Events.Write(numberOfEvents, i => new Order.ItemAdded
                        {
                            ProductName = productName,
                            Quantity    = 1,
                            AggregateId = Any.Guid()
                        });

                        startThread("catchup1", () =>
                        {
                            catchup1.Run().TimeoutAfter(DefaultTimeout).Wait();
                            catchup1.Dispose();
                        });
                        startThread("catchup2", () =>
                        {
                            catchup2.Run().TimeoutAfter(DefaultTimeout).Wait();
                            catchup2.Dispose();
                        });

                        Console.WriteLine("Waiting on catchups to complete");

                        // wait on both catchups to complete
                        catchup1
                        .Progress
                        .Timeout(DefaultTimeout)
                        .Merge(catchup2.Progress
                               .Timeout(DefaultTimeout))
                        .Where(p => p.IsEndOfBatch)
                        .Take(2)
                        .Timeout(DefaultTimeout)
                        .Wait();
                    }

            Action <DbContext> verify = db =>
            {
                var readModelInfoName = ReadModelInfo.NameForProjector(projector1);

                var readModelInfos = db.Set <ReadModelInfo>();
                Console.WriteLine(new { readModelInfos }.ToLogString());
                readModelInfos
                .Single(i => i.Name == readModelInfoName)
                .CurrentAsOfEventId
                .Should()
                .Be(HighestEventId + numberOfEvents);

                var productInventories = db.Set <ProductInventory>();
                Console.WriteLine(new { productInventories }.ToLogString());
                productInventories
                .Single(pi => pi.ProductName == productName)
                .QuantityReserved
                .Should()
                .Be(numberOfEvents);
            };

            Console.WriteLine("verifying ReadModels1DbContext...");
            new ReadModels1DbContext().DisposeAfter(r => verify(r));

            Console.WriteLine("verifying ReadModels2DbContext...");
            new ReadModels2DbContext().DisposeAfter(r => verify(r));
        }
Exemple #5
0
 private IUpdateProjectionWhen <T> CreateProjector <T>(
     Action <T> action = null)
     where T : IEvent =>
 Projector.Create(action ?? (_ => { })).Named(Any.CamelCaseName(6));