public void Save(Type projectionType, IEvent @event, EventOrigin eventOrigin, ProjectionVersion version) { if (ReferenceEquals(null, projectionType)) { throw new ArgumentNullException(nameof(projectionType)); } if (ReferenceEquals(null, @event)) { throw new ArgumentNullException(nameof(@event)); } if (ReferenceEquals(null, eventOrigin)) { throw new ArgumentNullException(nameof(eventOrigin)); } if (ReferenceEquals(null, version)) { throw new ArgumentNullException(nameof(version)); } if ((version.Status == ProjectionStatus.Building || version.Status == ProjectionStatus.Live) == false) { throw new ArgumentException("Invalid version. Only versions in `Building` and `Live` status are eligable for persistence.", nameof(version)); } string projectionName = projectionType.GetContractId(); if (projectionName.Equals(version.ProjectionName, StringComparison.OrdinalIgnoreCase) == false) { throw new ArgumentException($"Invalid version. The version `{version}` does not match projection `{projectionName}`", nameof(version)); } var handlerInstance = handlerFactory.Create(projectionType); var projection = handlerInstance.Current as IProjectionDefinition; if (projection != null) { var projectionIds = projection.GetProjectionIds(@event); foreach (var projectionId in projectionIds) { try { SnapshotMeta snapshotMeta = null; if (projectionType.IsSnapshotable()) { snapshotMeta = snapshotStore.LoadMeta(projectionName, projectionId, version); } else { snapshotMeta = new NoSnapshot(projectionId, projectionName).GetMeta(); } ProjectionStream projectionStream = LoadProjectionStream(projectionType, version, projectionId, snapshotMeta); int snapshotMarker = snapshotStrategy.GetSnapshotMarker(projectionStream.Commits, snapshotMeta.Revision); var commit = new ProjectionCommit(projectionId, version, @event, snapshotMarker, eventOrigin, DateTime.UtcNow); projectionStore.Save(commit); } catch (Exception ex) { log.ErrorException("Failed to persist event." + Environment.NewLine + $"\tProjectionVersion:{version}" + Environment.NewLine + $"\tEvent:{@event}", ex); } } } }
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)); } }
public ProjectionVersions(ProjectionVersion seed) : this(new[] { seed }) { }
public Task InitializeAsync(ProjectionVersion version) { return(Task.CompletedTask); }
private async Task <SnapshotMeta> GetSnapshotMeta(Type projectionType, string projectionName, IBlobId projectionId, ProjectionVersion version) { SnapshotMeta snapshotMeta; if (projectionType.IsSnapshotable()) { snapshotMeta = await snapshotStore.LoadMetaAsync(projectionName, projectionId, version).ConfigureAwait(false); } else { snapshotMeta = new NoSnapshot(projectionId, projectionName).GetMeta(); } return(snapshotMeta); }
private bool ShouldSaveEventForVersion(ProjectionVersion version) { return(version.Status == ProjectionStatus.Building || version.Status == ProjectionStatus.Replaying || version.Status == ProjectionStatus.Rebuilding || version.Status == ProjectionStatus.Live); }
private async Task <ProjectionStream> LoadProjectionStreamAsync(Type projectionType, ProjectionVersion version, IBlobId projectionId, SnapshotMeta snapshotMeta) { ISnapshot loadedSnapshot = await snapshotStore.LoadAsync(version.ProjectionName, projectionId, version).ConfigureAwait(false); Func <ISnapshot> loadSnapshot = () => projectionType.IsSnapshotable() ? loadedSnapshot : new NoSnapshot(projectionId, version.ProjectionName); return(await LoadProjectionStreamAsync(version, projectionId, snapshotMeta, loadSnapshot).ConfigureAwait(false)); }
// Used by replay projections only public async Task SaveAsync(Type projectionType, IEvent @event, EventOrigin eventOrigin, ProjectionVersion version) { if (ReferenceEquals(null, projectionType)) { throw new ArgumentNullException(nameof(projectionType)); } if (ReferenceEquals(null, @event)) { throw new ArgumentNullException(nameof(@event)); } if (ReferenceEquals(null, eventOrigin)) { throw new ArgumentNullException(nameof(eventOrigin)); } if (ReferenceEquals(null, version)) { throw new ArgumentNullException(nameof(version)); } if (ShouldSaveEventForVersion(version) == false) { throw new ArgumentException("Invalid version. Only versions in `Building` and `Live` status are eligable for persistence.", nameof(version)); } string projectionName = projectionType.GetContractId(); if (projectionName.Equals(version.ProjectionName, StringComparison.OrdinalIgnoreCase) == false) { throw new ArgumentException($"Invalid version. The version `{version}` does not match projection `{projectionName}`", nameof(version)); } var handlerInstance = handlerFactory.Create(projectionType); var projection = handlerInstance.Current as IProjectionDefinition; if (projection != null) { var projectionIds = projection.GetProjectionIds(@event); foreach (var projectionId in projectionIds) { try { SnapshotMeta snapshotMeta = await GetSnapshotMeta(projectionType, projectionName, projectionId, version).ConfigureAwait(false); int snapshotMarker = snapshotMeta.Revision == 0 ? 1 : snapshotMeta.Revision + 2; var commit = new ProjectionCommit(projectionId, version, @event, snapshotMarker, eventOrigin, DateTime.UtcNow); await projectionStore.SaveAsync(commit).ConfigureAwait(false); } catch (Exception ex) { log.ErrorException(ex, () => "Failed to persist event." + Environment.NewLine + $"\tProjectionVersion:{version}" + Environment.NewLine + $"\tEvent:{@event}"); } } } else if (handlerInstance.Current is IAmEventSourcedProjection eventSourcedProjection) { try { var projectionId = Urn.Parse($"urn:cronus:{projectionName}"); var commit = new ProjectionCommit(projectionId, version, @event, 1, eventOrigin, DateTime.UtcNow); // Snapshotting is disable till we test it => hardcoded 1 await projectionStore.SaveAsync(commit).ConfigureAwait(false); } catch (Exception ex) { log.ErrorException(ex, () => "Failed to persist event." + Environment.NewLine + $"\tProjectionVersion:{version}" + Environment.NewLine + $"\tEvent:{@event}"); } } }