public ReplayResult Rebuild(ProjectionVersion version, DateTime rebuildUntil) { if (ReferenceEquals(null, version)) { throw new ArgumentNullException(nameof(version)); } if (index.Status.IsNotPresent()) { RebuildIndex(); //TODO (2) } Type projectionType = version.ProjectionName.GetTypeByContract(); try { if (IsVersionTrackerMissing() && IsNotSystemProjection(projectionType)) { return(ReplayResult.RetryLater($"Projection `{version}` still don't have present index.")); //WHEN TO RETRY AGAIN } if (HasReplayTimeout(rebuildUntil)) { return(ReplayResult.Timeout($"Rebuild of projection `{version}` has expired. Version:{version} Deadline:{rebuildUntil}.")); } var allVersions = GetAllVersions(version); if (allVersions.IsOutdatad(version)) { return(new ReplayResult($"Version `{version}` is outdated. There is a newer one which is already live.")); } if (allVersions.IsCanceled(version)) { return(new ReplayResult($"Version `{version}` was canceled.")); } if (index.Status.IsNotPresent()) { return(ReplayResult.RetryLater($"Projection `{version}` still don't have present index.")); //WHEN TO RETRY AGAIN } DateTime startRebuildTimestamp = DateTime.UtcNow; int progressCounter = 0; log.Info(() => $"Start rebuilding projection `{version.ProjectionName}` for version {version}. Deadline is {rebuildUntil}"); Dictionary <int, string> processedAggregates = new Dictionary <int, string>(); projectionStoreInitializer.Initialize(version); var projectionHandledEventTypes = GetInvolvedEvents(projectionType); foreach (var eventType in projectionHandledEventTypes) { log.Info(() => $"Rebuilding projection `{version.ProjectionName}` for version {version} using eventType `{eventType}`. Deadline is {rebuildUntil}"); IEnumerable <IndexRecord> indexRecords = index.EnumerateRecords(eventType); foreach (IndexRecord indexRecord in indexRecords) { // TODO: (5) Decorator pattern which will give us the tracking progressCounter++; if (progressCounter % 1000 == 0) { log.Info(() => $"Rebuilding projection {version.ProjectionName} => PROGRESS:{progressCounter} Version:{version} EventType:{eventType} Deadline:{rebuildUntil} Total minutes working:{(DateTime.UtcNow - startRebuildTimestamp).TotalMinutes}. logId:{Guid.NewGuid().ToString()} ProcessedAggregatesSize:{processedAggregates.Count}"); } int aggreagteRootIdHash = indexRecord.AggregateRootId.GetHashCode(); if (processedAggregates.ContainsKey(aggreagteRootIdHash)) { continue; } processedAggregates.Add(aggreagteRootIdHash, null); string mess = Encoding.UTF8.GetString(indexRecord.AggregateRootId); IAggregateRootId arId = GetAggregateRootId(mess); EventStream stream = eventStore.Load(arId); foreach (AggregateCommit arCommit in stream.Commits) { for (int i = 0; i < arCommit.Events.Count; i++) { IEvent theEvent = arCommit.Events[i].Unwrap(); if (projectionHandledEventTypes.Contains(theEvent.GetType().GetContractId())) // filter out the events which are not part of the projection { var origin = new EventOrigin(mess, arCommit.Revision, i, arCommit.Timestamp); projectionWriter.Save(projectionType, theEvent, origin, version); } } } } } log.Info(() => $"Finish rebuilding projection `{projectionType.Name}` for version {version}. Deadline was {rebuildUntil}"); return(new ReplayResult()); } catch (Exception ex) { string message = $"Unable to replay projection. Version:{version} ProjectionType:{projectionType.FullName}"; log.ErrorException(message, ex); return(new ReplayResult(message + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace)); } }