Beispiel #1
0
 public IEnumerable <IAggregateRootEvent> GetAggregateHistory(Guid id)
 {
     lock (_lockObject)
     {
         return(SingleAggregateInstanceEventStreamMutator.MutateCompleteAggregateHistory(_migrationFactories, _events.Where(e => e.AggregateRootId == id).ToList())
                .ToList());;
     }
 }
        public void When_there_are_no_migrations_mutation_takes_less_than_a_millisecond()
        {
            var eventMigrations = Seq.Create <IEventMigration>().ToArray();

            TimeAsserter.Execute(
                maxAverage: 1.Milliseconds(),
                iterations: 10,
                description: "load aggregate in isolated scope",
                timeFormat: "ss\\.fff",
                action: () => { SingleAggregateInstanceEventStreamMutator.MutateCompleteAggregateHistory(eventMigrations, _history); });
        }
Beispiel #3
0
        public void SaveEvents(IEnumerable <IAggregateRootEvent> events)
        {
            _usageGuard.AssertNoContextChangeOccurred(this);
            _schemaManager.SetupSchemaIfDatabaseUnInitialized();
            events = events.ToList();
            var updatedAggregates = events.Select(@event => @event.AggregateRootId).Distinct();

            _aggregatesWithEventsAddedByThisInstance.AddRange(updatedAggregates);
            _eventWriter.Insert(events.Cast <AggregateRootEvent>());
            //todo: move this to the event store session.
            foreach (var aggregateId in updatedAggregates)
            {
                var completeAggregateHistory = _cache.GetCopy(aggregateId).Events.Concat(events.Where(@event => @event.AggregateRootId == aggregateId)).Cast <AggregateRootEvent>().ToArray();
                SingleAggregateInstanceEventStreamMutator.AssertMigrationsAreIdempotent(_migrationFactories, completeAggregateHistory);
            }
        }
        public void With_four_migrations_that_change_nothing_mutation_takes_less_than_10_milliseconds()
        {
            var eventMigrations = Seq.Create <IEventMigration>(
                Before <E3> .Insert <E1>(),
                Before <E5> .Insert <E1>(),
                Before <E7> .Insert <E1>(),
                Before <E9> .Insert <E1>()
                ).ToArray();

            var maxAverage = 10.Milliseconds().AdjustRuntimeForNCrunch(boost: 6);

            TimeAsserter.Execute(
                maxTotal: maxAverage,
                maxTries: 10,
                description: "load aggregate in isolated scope",
                timeFormat: "ss\\.fff",
                action: () => { SingleAggregateInstanceEventStreamMutator.MutateCompleteAggregateHistory(eventMigrations, _history); });
        }
        public void With_four_migrations_mutation_that_all_actually_changes_things_migration_takes_less_than_15_milliseconds()
        {
            var eventMigrations = Seq.Create <IEventMigration>(
                Before <E2> .Insert <E3>(),
                Before <E4> .Insert <E5>(),
                Before <E6> .Insert <E7>(),
                Before <E8> .Insert <E9>()
                ).ToArray();

            var maxAverage = TestEnvironmentPerformance.AdjustRuntime(15.Milliseconds(), boost: 2);

            TimeAsserter.Execute(
                maxTotal: maxAverage,
                description: "load aggregate in isolated scope",
                maxTries: 10,
                timeFormat: "ss\\.fff",
                action: () => { SingleAggregateInstanceEventStreamMutator.MutateCompleteAggregateHistory(eventMigrations, _history); });
        }
Beispiel #6
0
        private IEnumerable <IAggregateRootEvent> GetAggregateHistoryInternal(Guid aggregateId, bool takeWriteLock)
        {
            _usageGuard.AssertNoContextChangeOccurred(this);
            _schemaManager.SetupSchemaIfDatabaseUnInitialized();
            lock (AggregateLockManager.GetAggregateLockObject(aggregateId))
            {
                var cachedAggregateHistory = _cache.GetCopy(aggregateId);

                var newEventsFromDatabase = _eventReader.GetAggregateHistory(
                    aggregateId: aggregateId,
                    startAfterInsertedVersion: cachedAggregateHistory.MaxSeenInsertedVersion,
                    takeWriteLock: takeWriteLock);

                var containsRefactoringEvents = newEventsFromDatabase.Where(IsRefactoringEvent).Any();
                if (containsRefactoringEvents && cachedAggregateHistory.MaxSeenInsertedVersion > 0)
                {
                    _cache.Remove(aggregateId);
                    return(GetAggregateHistoryInternal(aggregateId: aggregateId, takeWriteLock: takeWriteLock));
                }

                var currentHistory = cachedAggregateHistory.Events.Count == 0
                                                   ? SingleAggregateInstanceEventStreamMutator.MutateCompleteAggregateHistory(_migrationFactories, newEventsFromDatabase)
                                                   : cachedAggregateHistory.Events.Concat(newEventsFromDatabase).ToList();

                //Should within a transaction a process write events, read them, then fail to commit we will have cached events that are not persisted unless we refuse to cache them here.
                if (!_aggregatesWithEventsAddedByThisInstance.Contains(aggregateId))
                {
                    var maxSeenInsertedVersion = newEventsFromDatabase.Any()
                                                     ? newEventsFromDatabase.Max(@event => @event.InsertedVersion)
                                                     : cachedAggregateHistory.MaxSeenInsertedVersion;

                    _cache.Store(
                        aggregateId,
                        new SqlServerEventStoreEventsCache.Entry(events: currentHistory, maxSeenInsertedVersion: maxSeenInsertedVersion));
                }

                return(currentHistory);
            }
        }
Beispiel #7
0
        public void PersistMigrations()
        {
            this.Log().Warn($"Starting to persist migrations");

            long migratedAggregates = 0;
            long updatedAggregates  = 0;
            long newEventCount      = 0;
            var  logInterval        = 1.Minutes();
            var  lastLogTime        = DateTime.Now;

            const int recoverableErrorRetriesToMake = 5;

            var aggregateIdsInCreationOrder = StreamAggregateIdsInCreationOrder().ToList();

            foreach (var aggregateId in aggregateIdsInCreationOrder)
            {
                try
                {
                    var succeeded = false;
                    int retries   = 0;
                    while (!succeeded)
                    {
                        try
                        {
                            //todo: Look at batching the inserting of events in a way that let's us avoid taking a lock for a long time as we do now. This might be a problem in production.
                            using (var transaction = new TransactionScope(TransactionScopeOption.Required, scopeTimeout: 10.Minutes()))
                            {
                                lock (AggregateLockManager.GetAggregateLockObject(aggregateId))
                                {
                                    var updatedThisAggregate = false;
                                    var original             = _eventReader.GetAggregateHistory(aggregateId: aggregateId, takeWriteLock: true).ToList();

                                    var startInsertingWithVersion = original.Max(@event => @event.InsertedVersion) + 1;

                                    var updatedAggregatesBeforeMigrationOfThisAggregate = updatedAggregates;

                                    SingleAggregateInstanceEventStreamMutator.MutateCompleteAggregateHistory(
                                        _migrationFactories,
                                        original,
                                        newEvents =>
                                    {
                                        //Make sure we don't try to insert into an occupied InsertedVersion
                                        newEvents.ForEach(@event => @event.InsertedVersion = startInsertingWithVersion++);
                                        //Save all new events so they get an InsertionOrder for the next refactoring to work with in case it acts relative to any of these events
                                        _eventWriter.InsertRefactoringEvents(newEvents);
                                        updatedAggregates    = updatedAggregatesBeforeMigrationOfThisAggregate + 1;
                                        newEventCount       += newEvents.Count();
                                        updatedThisAggregate = true;
                                    });

                                    if (updatedThisAggregate)
                                    {
                                        _eventWriter.FixManualVersions(aggregateId);
                                    }

                                    transaction.Complete();
                                    _cache.Remove(aggregateId);
                                }
                                migratedAggregates++;
                                succeeded = true;
                            }
                        }
                        catch (Exception e) when(IsRecoverableSqlException(e) && ++retries <= recoverableErrorRetriesToMake)
                        {
                            this.Log().Warn($"Failed to persist migrations for aggregate: {aggregateId}. Exception appers to be recoverable so running retry {retries} out of {recoverableErrorRetriesToMake}", e);
                        }
                    }
                }
                catch (Exception exception)
                {
                    this.Log().Error($"Failed to persist migrations for aggregate: {aggregateId}", exception: exception);
                }

                if (logInterval < DateTime.Now - lastLogTime)
                {
                    lastLogTime = DateTime.Now;
                    Func <int> percentDone = () => (int)(((double)migratedAggregates / aggregateIdsInCreationOrder.Count) * 100);
                    this.Log().Info($"{percentDone()}% done. Inspected: {migratedAggregates} / {aggregateIdsInCreationOrder.Count}, Updated: {updatedAggregates}, New Events: {newEventCount}");
                }
            }

            this.Log().Warn($"Done persisting migrations.");
            this.Log().Info($"Inspected: {migratedAggregates} , Updated: {updatedAggregates}, New Events: {newEventCount}");
        }
        private static void RunScenarioWithEventStoreType
            (MigrationScenario scenario, Type eventStoreType, WindsorContainer container, IList <IEventMigration> migrations, int indexOfScenarioInBatch)
        {
            var startingMigrations = migrations.ToList();

            migrations.Clear();

            var timeSource = container.Resolve <DummyTimeSource>();

            IReadOnlyList <IAggregateRootEvent> eventsInStoreAtStart;

            using (container.BeginScope()) //Why is this needed? It fails without it but I do not understand why...
            {
                var eventStore = container.Resolve <IEventStore>();
                eventsInStoreAtStart = eventStore.ListAllEventsForTestingPurposesAbsolutelyNotUsableForARealEventStoreOfAnySize();
            }

            Console.WriteLine($"\n########Running Scenario {indexOfScenarioInBatch}");

            var original = TestAggregate.FromEvents(DummyTimeSource.Now, scenario.AggregateId, scenario.OriginalHistory).History.ToList();

            Console.WriteLine($"Original History: ");
            original.ForEach(e => Console.WriteLine($"      {e}"));
            Console.WriteLine();

            var initialAggregate = TestAggregate.FromEvents(timeSource, scenario.AggregateId, scenario.OriginalHistory);
            var expected         = TestAggregate.FromEvents(timeSource, scenario.AggregateId, scenario.ExpectedHistory).History.ToList();
            var expectedCompleteEventstoreStream = eventsInStoreAtStart.Concat(expected).ToList();

            Console.WriteLine($"Expected History: ");
            expected.ForEach(e => Console.WriteLine($"      {e}"));
            Console.WriteLine();

            var initialAggregate2 = TestAggregate.FromEvents(timeSource, scenario.AggregateId, scenario.OriginalHistory);

            timeSource.UtcNow += 1.Hours();//Bump clock to ensure that times will be be wrong unless the time from the original events are used..

            Console.WriteLine("Doing pure in memory ");
            IReadOnlyList <IAggregateRootEvent> otherHistory = SingleAggregateInstanceEventStreamMutator.MutateCompleteAggregateHistory(
                scenario.Migrations,
                initialAggregate2.History.Cast <AggregateRootEvent>().ToList());

            AssertStreamsAreIdentical(expected, otherHistory, $"Direct call to SingleAggregateInstanceEventStreamMutator.MutateCompleteAggregateHistory");

            container.ExecuteUnitOfWorkInIsolatedScope(() => container.Resolve <IEventStoreSession>().Save(initialAggregate));
            migrations.AddRange(startingMigrations);
            var migratedHistory = container.ExecuteUnitOfWorkInIsolatedScope(() => container.Resolve <IEventStoreSession>().Get <TestAggregate>(initialAggregate.Id)).History;

            AssertStreamsAreIdentical(expected, migratedHistory, "Loaded un-cached aggregate");

            var migratedCachedHistory = container.ExecuteUnitOfWorkInIsolatedScope(() => container.Resolve <IEventStoreSession>().Get <TestAggregate>(initialAggregate.Id)).History;

            AssertStreamsAreIdentical(expected, migratedCachedHistory, "Loaded cached aggregate");


            Console.WriteLine("  Streaming all events in store");
            var streamedEvents = container.ExecuteUnitOfWorkInIsolatedScope(() => container.Resolve <IEventStore>().ListAllEventsForTestingPurposesAbsolutelyNotUsableForARealEventStoreOfAnySize().ToList());

            AssertStreamsAreIdentical(expectedCompleteEventstoreStream, streamedEvents, "Streaming all events in store");

            Console.WriteLine("  Persisting migrations");
            using (container.BeginScope())
            {
                container.Resolve <IEventStore>().PersistMigrations();
            }

            migratedHistory = container.ExecuteUnitOfWorkInIsolatedScope(() => container.Resolve <IEventStoreSession>().Get <TestAggregate>(initialAggregate.Id)).History;
            AssertStreamsAreIdentical(expected, migratedHistory, "Loaded aggregate");

            Console.WriteLine("Streaming all events in store");
            streamedEvents = container.ExecuteUnitOfWorkInIsolatedScope(() => container.Resolve <IEventStore>().ListAllEventsForTestingPurposesAbsolutelyNotUsableForARealEventStoreOfAnySize().ToList());
            AssertStreamsAreIdentical(expectedCompleteEventstoreStream, streamedEvents, "Streaming all events in store");


            Console.WriteLine("  Disable all migrations so that none are used when reading from the event stores");
            migrations.Clear();

            migratedHistory = container.ExecuteUnitOfWorkInIsolatedScope(() => container.Resolve <IEventStoreSession>().Get <TestAggregate>(initialAggregate.Id)).History;
            AssertStreamsAreIdentical(expected, migratedHistory, "loaded aggregate");

            Console.WriteLine("Streaming all events in store");
            streamedEvents = container.ExecuteUnitOfWorkInIsolatedScope(() => container.Resolve <IEventStore>().ListAllEventsForTestingPurposesAbsolutelyNotUsableForARealEventStoreOfAnySize().ToList());
            AssertStreamsAreIdentical(expectedCompleteEventstoreStream, streamedEvents, "Streaming all events in store");

            if (eventStoreType == typeof(SqlServerEventStore))
            {
                Console.WriteLine("Clearing sql server eventstore cache");
                container.ExecuteUnitOfWorkInIsolatedScope(() => ((SqlServerEventStore)container.Resolve <IEventStore>()).ClearCache());
                migratedHistory = container.ExecuteUnitOfWorkInIsolatedScope(() => container.Resolve <IEventStoreSession>().Get <TestAggregate>(initialAggregate.Id)).History;
                AssertStreamsAreIdentical(expected, migratedHistory, "Loaded aggregate");

                Console.WriteLine("Streaming all events in store");
                streamedEvents = container.ExecuteUnitOfWorkInIsolatedScope(() => container.Resolve <IEventStore>().ListAllEventsForTestingPurposesAbsolutelyNotUsableForARealEventStoreOfAnySize().ToList());
                AssertStreamsAreIdentical(expectedCompleteEventstoreStream, streamedEvents, "Streaming all events in store");
            }
        }