public static void IncrementDoingVersion <TPrimaryKey>(this SnapshotMeta <TPrimaryKey> snapshotMeta, Type grainType) { if (snapshotMeta.DoingVersion != snapshotMeta.Version) { throw new SnapshotException(snapshotMeta.ActorId.ToString(), grainType, snapshotMeta.DoingVersion, snapshotMeta.Version); } snapshotMeta.DoingVersion += 1; }
public static void ForceUpdateVersion <TPrimaryKey>(this SnapshotMeta <TPrimaryKey> snapshotMeta, EventMeta eventMeta, Type grainType) { if (snapshotMeta.Version + 1 != eventMeta.Version) { throw new EventVersionException(snapshotMeta.ActorId.ToString(), grainType, eventMeta.Version, snapshotMeta.Version); } snapshotMeta.DoingVersion = eventMeta.Version; snapshotMeta.Version = eventMeta.Version; }
public static void DecrementDoingVersion <TPrimaryKey>(this SnapshotMeta <TPrimaryKey> snapshotMeta) => snapshotMeta.DoingVersion -= 1;
public void Save(Type projectionType, IEvent @event, EventOrigin eventOrigin) { 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)); } string projectionName = projectionType.GetContractId(); var handlerInstance = handlerFactory.Create(projectionType); var projection = handlerInstance.Current as IProjectionDefinition; if (projection != null) { var projectionIds = projection.GetProjectionIds(@event); foreach (var projectionId in projectionIds) { ReadResult <ProjectionVersions> result = GetProjectionVersions(projectionName); if (result.IsSuccess) { foreach (ProjectionVersion version in result.Data) { if (version.Status == ProjectionStatus.Building || version.Status == ProjectionStatus.Live) { 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); } } else if (version.Status == ProjectionStatus.NotPresent) { var commit = new ProjectionCommit(projectionId, version, @event, 1, eventOrigin, DateTime.UtcNow); projectionStore.Save(commit); } } } } } }
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); } } } }
ProjectionStream LoadProjectionStream(Type projectionType, ProjectionVersion version, IBlobId projectionId, SnapshotMeta snapshotMeta) { Func <ISnapshot> loadSnapshot = () => snapshotStore.Load(version.ProjectionName, projectionId, version); List <ProjectionCommit> projectionCommits = new List <ProjectionCommit>(); int snapshotMarker = snapshotMeta.Revision; while (true) { snapshotMarker++; var loadedCommits = projectionStore.Load(version, projectionId, snapshotMarker).ToList(); projectionCommits.AddRange(loadedCommits); if (projectionType.IsSnapshotable()) { if (snapshotStrategy.ShouldCreateSnapshot(projectionCommits, snapshotMeta.Revision)) { ProjectionStream checkpointStream = new ProjectionStream(projectionId, projectionCommits, loadSnapshot); var projectionState = checkpointStream.RestoreFromHistory(projectionType).State; ISnapshot newSnapshot = new Snapshot(projectionId, version.ProjectionName, projectionState, snapshotMeta.Revision + 1); snapshotStore.Save(newSnapshot, version); loadSnapshot = () => newSnapshot; projectionCommits.Clear(); log.Debug(() => $"Snapshot created for projection `{version.ProjectionName}` with id={projectionId} where ({loadedCommits.Count}) were zipped. Snapshot: `{newSnapshot.GetType().Name}`"); } } else { loadSnapshot = () => new NoSnapshot(projectionId, version.ProjectionName); } if (loadedCommits.Count < snapshotStrategy.EventsInSnapshot) { break; } if (loadedCommits.Count > snapshotStrategy.EventsInSnapshot * 1.5) { log.Warn(() => $"Potential memory leak. The system will be down fairly soon. The projection `{version.ProjectionName}` with id={projectionId} loads a lot of projection commits ({loadedCommits.Count}) which puts a lot of CPU and RAM pressure. You can resolve this by configuring the snapshot settings`."); } } ProjectionStream stream = new ProjectionStream(projectionId, projectionCommits, loadSnapshot); return(stream); }
ProjectionStream LoadProjectionStream(Type projectionType, ProjectionVersion version, IBlobId projectionId, SnapshotMeta snapshotMeta) { Func <ISnapshot> loadSnapshot = () => projectionType.IsSnapshotable() ? snapshotStore.Load(version.ProjectionName, projectionId, version) : new NoSnapshot(projectionId, version.ProjectionName); return(LoadProjectionStream(projectionType, version, projectionId, snapshotMeta, loadSnapshot)); }
public async Task <SnapshotMeta> LoadMetaAsync(string projectionName, IBlobId id, ProjectionVersion version) { var snapshot = await LoadAsync(projectionName, id, version).ConfigureAwait(false); return(SnapshotMeta.From(snapshot)); }
ProjectionStream LoadProjectionStream(Type projectionType, IBlobId projectionId) { string projectionName = projectionType.GetContractId(); ReadResult <ProjectionVersions> result = GetProjectionVersions(projectionName); if (result.IsSuccess) { ProjectionVersion liveVersion = result.Data.GetLive(); if (liveVersion is null) { log.Warn(() => $"Unable to find projection `live` version. ProjectionId:{projectionId} ProjectionName:{projectionName} ProjectionType:{projectionType.Name}{Environment.NewLine}AvailableVersions:{Environment.NewLine}{result.Data.ToString()}"); return(ProjectionStream.Empty()); } ISnapshot snapshot = projectionType.IsSnapshotable() ? snapshotStore.Load(projectionName, projectionId, liveVersion) : new NoSnapshot(projectionId, projectionName); return(LoadProjectionStream(projectionType, liveVersion, projectionId, SnapshotMeta.From(snapshot), () => snapshot)); } return(ProjectionStream.Empty()); }
public async Task SaveAsync(Type projectionType, IEvent @event, EventOrigin eventOrigin) { 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)); } string projectionName = projectionType.GetContractId(); var handlerInstance = handlerFactory.Create(projectionType); var projection = handlerInstance.Current as IProjectionDefinition; if (projection != null) { var projectionIds = projection.GetProjectionIds(@event); foreach (var projectionId in projectionIds) { using (log.BeginScope(s => s.AddScope("cronus_projection_id", projectionId))) { ReadResult <ProjectionVersions> result = await GetProjectionVersionsAsync(projectionName).ConfigureAwait(false); if (result.IsSuccess) { foreach (ProjectionVersion version in result.Data) { using (log.BeginScope(s => s.AddScope("cronus_projection_version", version))) { if (ShouldSaveEventForVersion(version)) { try { SnapshotMeta snapshotMeta = null; if (projectionType.IsSnapshotable()) { snapshotMeta = await snapshotStore.LoadMetaAsync(projectionName, projectionId, version).ConfigureAwait(false); } else { snapshotMeta = new NoSnapshot(projectionId, projectionName).GetMeta(); } int snapshotMarker = snapshotMeta.Revision + 2; var commit = new ProjectionCommit(projectionId, version, @event, snapshotMarker, eventOrigin, DateTime.UtcNow); await projectionStore.SaveAsync(commit).ConfigureAwait(false); } catch (Exception ex) when(LogMessageWhenFailToUpdateProjection(ex, version)) { } } } } if (result.HasError) { log.Error(() => "Failed to update projection because the projection version failed to load. Please replay the projection to restore the state. Self-heal hint!" + Environment.NewLine + result.Error + Environment.NewLine + $"\tProjectionName:{projectionName}" + Environment.NewLine + $"\tEvent:{@event}"); } } } } } }
private async Task <ProjectionStream> LoadProjectionStreamAsync(ProjectionVersion version, IBlobId projectionId, SnapshotMeta snapshotMeta, Func <ISnapshot> loadSnapshot) { List <ProjectionCommit> projectionCommits = new List <ProjectionCommit>(); int snapshotMarker = snapshotMeta.Revision; bool shouldLoadMore = true; while (shouldLoadMore) { snapshotMarker++; var loadedCommits = projectionStore.LoadAsync(version, projectionId, snapshotMarker).ConfigureAwait(false); await foreach (var commit in loadedCommits) { projectionCommits.Add(commit); } shouldLoadMore = await projectionStore.HasSnapshotMarkerAsync(version, projectionId, snapshotMarker + 1).ConfigureAwait(false); } ProjectionStream stream = new ProjectionStream(projectionId, projectionCommits, loadSnapshot); return(stream); }
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}"); } } }