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); } }
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}"); }