Beispiel #1
0
 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;
 }
Beispiel #2
0
        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;
        }
Beispiel #3
0
 public static void DecrementDoingVersion <TPrimaryKey>(this SnapshotMeta <TPrimaryKey> snapshotMeta) => snapshotMeta.DoingVersion -= 1;
Beispiel #4
0
        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);
                            }
                        }
                    }
                }
            }
        }
Beispiel #5
0
        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);
                    }
                }
            }
        }
Beispiel #6
0
        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));
        }
Beispiel #8
0
        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());
        }
Beispiel #10
0
        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}");
                            }
                        }
                    }
                }
            }
        }
Beispiel #11
0
        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);
        }
Beispiel #12
0
        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));
        }
Beispiel #13
0
        // 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}");
                }
            }
        }