コード例 #1
0
        public static void SetReadModelTrackingTo(this DbContext db,
                                                  long toEventId,
                                                  params object[] addForProjectors)
        {
            // forward all existing read model tracking
            foreach (var readModelInfo in db.Set <ReadModelInfo>())
            {
                readModelInfo.CurrentAsOfEventId = toEventId;
            }

            foreach (var projector in addForProjectors)
            {
                var projectorName = ReadModelInfo.NameForProjector(projector);

                db.Set <ReadModelInfo>().AddOrUpdate(
                    i => i.Name,
                    new ReadModelInfo
                {
                    Name = projectorName,
                    CurrentAsOfEventId = toEventId
                });
            }

            db.SaveChanges();
        }
コード例 #2
0
        public async Task Database_command_timeouts_during_catchup_do_not_interrupt_catchup()
        {
            // reset read model tracking to 0
            ReadModelDbContext().DisposeAfter(c =>
            {
                var projectorName = ReadModelInfo.NameForProjector(new Projector <Order.CustomerInfoChanged>());
                c.Set <ReadModelInfo>()
                .SingleOrDefault(i => i.Name == projectorName)
                .IfNotNull()
                .ThenDo(i => { i.CurrentAsOfEventId = 0; });
                c.SaveChanges();
            });

            var exceptions = new Stack <Exception>(Enumerable.Range(1, 2)
                                                   .Select(_ => new InvalidOperationException("Invalid attempt to call IsDBNull when reader is closed.")));

            var count       = 0;
            var flakyEvents = new FlakyEventStream(
                Enumerable.Range(1, 1000)
                .Select(i => new StorableEvent
            {
                AggregateId = Any.Guid(),
                Body        = new Order.CustomerInfoChanged {
                    CustomerName = i.ToString()
                }.ToJson(),
                SequenceNumber = i,
                StreamName     = typeof(Order).Name,
                Timestamp      = DateTimeOffset.Now,
                Type           = typeof(Order.CustomerInfoChanged).Name,
                Id             = i
            }).ToArray(),
                startFlakingOnEnumeratorNumber: 2,
                doSomethingFlaky: i =>
            {
                if (count++ > 50)
                {
                    count = 0;
                    if (exceptions.Any())
                    {
                        throw exceptions.Pop();
                    }
                }
            });

            var names = new HashSet <string>();

            var projector = new Projector <Order.CustomerInfoChanged>
            {
                OnUpdate = (work, e) => names.Add(e.CustomerName)
            };

            using (var catchup = CreateReadModelCatchup(projector))
            {
                await catchup.Run();
            }

            projector.CallCount.Should().Be(1000);
            names.Count.Should().Be(1000);
        }
コード例 #3
0
        public void Rolling_catchup_can_be_run_based_on_event_store_polling()
        {
            var numberOfEvents = 50;

            Console.WriteLine("writing " + numberOfEvents + " starting at " + HighestEventId);

            // start the catchup in polling mode
            Projector <Order.ItemAdded> projector = null;
            var reading = Task.Run(() =>
            {
                projector = new Projector <Order.ItemAdded>(() => new ReadModels1DbContext());
                using (var catchup = CreateReadModelCatchup <ReadModels1DbContext>(projector).PollEventStore())
                {
                    catchup.Progress
                    .Do(s => Console.WriteLine(s))
                    .FirstAsync(s => s.IsEndOfBatch && s.CurrentEventId == numberOfEvents + HighestEventId)
                    .Timeout(DefaultTimeout)
                    .Wait();
                }
            });

            // now start writing a bunch of new events
            var writing = Task.Run(() => Enumerable.Range(1, numberOfEvents).ForEach(_ =>
            {
                // add a little variation into the intervals at which new events are written
                Thread.Sleep(Any.PositiveInt(1000));
                Events.Write(1);
            }));

            writing.Wait();
            reading.Wait();

            using (var db = new ReadModels1DbContext())
            {
                var readModelInfoName = ReadModelInfo.NameForProjector(projector);
                db.Set <ReadModelInfo>()
                .Single(i => i.Name == readModelInfoName)
                .CurrentAsOfEventId
                .Should()
                .Be(HighestEventId + numberOfEvents);
            }
        }
コード例 #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()
                        });

                        // TODO: (Events_committed_to_the_event_store_are_caught_up_by_multiple_independent_read_model_stores) is this leading to intermittent test failures by leaving a dangling app lock?
                        startThread("catchup1", () =>
                        {
                            catchup1.Run();
                            catchup1.Dispose();
                        });
                        startThread("catchup2", () =>
                        {
                            catchup2.Run();
                            catchup2.Dispose();
                        });

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

                        // wait on both catchups to complete
                        catchup1
                        .Progress
                        .Merge(catchup2.Progress)
                        .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));
        }
コード例 #5
0
        public async Task Insertion_of_new_events_during_catchup_does_not_interrupt_catchup()
        {
            var barrier = new Barrier(2);

            // preload some events for the catchup. replay will hit the barrier on the last one.
            var    order    = new Order();
            Action addEvent = () => order.Apply(new AddItem
            {
                Quantity    = 1,
                ProductName = "Penny candy",
                Price       = .01m
            });

            Enumerable.Range(1, 100).ForEach(_ => addEvent());
            var repository = new SqlEventSourcedRepository <Order>(new FakeEventBus());
            await repository.Save(order);

            // queue the catchup on a background task
#pragma warning disable 4014
            // don't await
            Task.Run(() =>
#pragma warning restore 4014
            {
                var projector = new Projector1
                {
                    OnUpdate = (work, e) =>
                    {
                        if (e.SequenceNumber == 10)
                        {
                            Console.WriteLine("pausing read model catchup");
                            barrier.SignalAndWait(MaxWaitTime); //1
                            barrier.SignalAndWait(MaxWaitTime); //2
                            Console.WriteLine("resuming read model catchup");
                        }
                    }
                };

                using (var db = EventStoreDbContext())
                    using (var catchup = CreateReadModelCatchup(projector))
                    {
                        var events = db.Events.Where(e => e.Id > HighestEventId);
                        Console.WriteLine(string.Format("starting read model catchup for {0} events", events.Count()));
                        catchup.Run().Wait();
                        Console.WriteLine("done with read model catchup");
                        barrier.SignalAndWait(MaxWaitTime); //3
                    }
            });

            Console.WriteLine("queued read model catchup task");
            barrier.SignalAndWait(MaxWaitTime); //1

            EventStoreDbContext().DisposeAfter(c =>
            {
                Console.WriteLine("adding one more event, bypassing read model tracking");
                c.Events.Add(new Order.ItemAdded
                {
                    AggregateId    = Guid.NewGuid(),
                    SequenceNumber = 1
                }.ToStorableEvent());
                c.SaveChanges();
                Console.WriteLine("done adding one more event");
            });

            barrier.SignalAndWait(MaxWaitTime); //2
            barrier.SignalAndWait(MaxWaitTime); //3

            // check that everything worked:
            var projector2    = new Projector1();
            var projectorName = ReadModelInfo.NameForProjector(projector2);
            using (var readModels = ReadModelDbContext())
            {
                var readModelInfo = readModels.Set <ReadModelInfo>().Single(i => i.Name == projectorName);

                readModelInfo.CurrentAsOfEventId.Should().Be(HighestEventId + 101);

                using (var catchup = CreateReadModelCatchup(projector2))
                {
                    await catchup.Run();
                }

                readModels.Entry(readModelInfo).Reload();
                readModelInfo.CurrentAsOfEventId.Should().Be(HighestEventId + 102);
            }
        }