Exemplo n.º 1
0
        public async Task When_using_Update_then_failed_writes_are_logged_to_EventHandlingErrors()
        {
            // preload some events for the catchup. replay will hit the barrier on the last one.
            var order       = new Order();
            var productName = Any.Paragraph(4);

            order.Apply(new AddItem
            {
                Quantity    = 1,
                ProductName = productName,
                Price       = .01m
            });
            var repository = new SqlEventSourcedRepository <Order>(new FakeEventBus());
            await repository.Save(order);

            Projector1 projector = null;

            projector = new Projector1
            {
                OnUpdate = (_, e) =>
                {
                    using (var work = projector.Update())
                    {
                        var db = work.Resource <ReadModelDbContext>();
                        // do something that will trigger a db exception when the UnitOfWork is committed
                        var inventory = db.Set <ProductInventory>();
                        inventory.Add(new ProductInventory
                        {
                            ProductName      = e.ProductName,
                            QuantityReserved = e.Quantity
                        });
                        inventory.Add(new ProductInventory
                        {
                            ProductName      = e.ProductName,
                            QuantityReserved = e.Quantity
                        });
                        work.VoteCommit();
                    }
                }
            };

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

            // assert
            using (var db = new ReadModelDbContext())
            {
                var error = db.Set <EventHandlingError>().Single(e => e.AggregateId == order.Id);
                error.Error.Should()
                .Contain(
                    string.Format(
                        "Violation of PRIMARY KEY constraint 'PK_dbo.ProductInventories'. Cannot insert duplicate key in object 'dbo.ProductInventories'. The duplicate key value is ({0})",
                        productName));
            }
        }
Exemplo n.º 2
0
        public async Task When_using_Update_then_failed_writes_do_not_interrupt_catchup()
        {
            // preload some events for the catchup. replay will hit the barrier on the last one.
            var    order       = new Order();
            var    productName = Any.Paragraph(4);
            Action addEvent    = () => order.Apply(new AddItem
            {
                Quantity    = 1,
                ProductName = productName,
                Price       = .01m
            });

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

            var        count     = 0;
            Projector1 projector = null;

            projector = new Projector1
            {
                OnUpdate = (_, e) =>
                {
                    using (var work = projector.Update())
                    {
                        var db = work.Resource <ReadModelDbContext>();
                        if (count++ == 15)
                        {
                            // do something that will trigger a db exception when the UnitOfWork is committed
                            var inventory = db.Set <ProductInventory>();
                            inventory.Add(new ProductInventory
                            {
                                ProductName      = e.ProductName,
                                QuantityReserved = e.Quantity
                            });
                            inventory.Add(new ProductInventory
                            {
                                ProductName      = e.ProductName,
                                QuantityReserved = e.Quantity
                            });
                        }
                        work.VoteCommit();
                    }
                }
            };

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

            // assert
            count.Should().Be(30);
        }
Exemplo n.º 3
0
        public async Task When_not_using_Update_then_failed_writes_do_not_interrupt_catchup()
        {
            // arrange
            // preload some events for the catchup. replay will hit the barrier on the last one.
            var    order       = new Order();
            var    productName = Any.Paragraph(3);
            Action addEvent    = () => order.Apply(new AddItem
            {
                Quantity    = 1,
                ProductName = productName,
                Price       = .01m
            });

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

            var count     = 0;
            var projector = new Projector1
            {
                OnUpdate = (work, e) =>
                {
                    using (var db = ReadModelDbContext())
                    {
                        // throw one exception in the middle
                        if (count++ == 15)
                        {
                            throw new Exception("drat!");
                        }
                        db.SaveChanges();
                    }
                }
            };

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

            // assert
            count.Should().Be(30);
        }
Exemplo n.º 4
0
        public async Task ReadModelCatchup_only_queries_events_since_the_last_consumed_event_id()
        {
            var bus        = new FakeEventBus();
            var repository = new SqlEventSourcedRepository <Order>(bus);

            // save the order with no projectors running
            var order = new Order();

            order.Apply(new AddItem
            {
                Price       = 1m,
                ProductName = "Widget"
            });
            await repository.Save(order);

            // subscribe one projector for catchup
            var projector1 = new Projector1();

            using (var catchup = CreateReadModelCatchup(projector1))
            {
                catchup.Progress.ForEachAsync(s => Console.WriteLine(s));
                await catchup.Run();
            }

            order.Apply(new AddItem
            {
                Price       = 1m,
                ProductName = "Widget"
            });
            await repository.Save(order);

            // subscribe both projectors
            var projector2 = new Projector2();

            using (var catchup = CreateReadModelCatchup(projector1, projector2))
            {
                catchup.Progress.ForEachAsync(s => Console.WriteLine(s));
                await catchup.Run();
            }

            projector1.CallCount.Should().Be(2, "A given event should only be passed to a given projector once");
            projector2.CallCount.Should().Be(2, "A projector should be passed events it has not previously seen.");
        }
Exemplo n.º 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);
            }
        }