Beispiel #1
0
        public async Task OldSnapshotsAreUpgradedToLatestVersionAndHaveCorrectMetadata()
        {
            // Arrange
            var thingyId         = A <ThingyId>();
            var pingIds          = Many <PingId>();
            var expectedVersion  = pingIds.Count;
            var thingySnapshotV1 = new ThingySnapshotV1(pingIds);

            await StoreSnapshotAsync(thingyId, expectedVersion, thingySnapshotV1).ConfigureAwait(false);

            // Act
            var snapshotContainer = await SnapshotStore.LoadSnapshotAsync <ThingyAggregate, ThingyId, ThingySnapshot>(
                thingyId,
                CancellationToken.None)
                                    .ConfigureAwait(false);

            // Assert
            snapshotContainer.Snapshot.Should().BeOfType <ThingySnapshot>();
            snapshotContainer.Metadata.AggregateId.Should().Be(thingyId.Value);
            snapshotContainer.Metadata.AggregateName.Should().Be("ThingyAggregate");
            snapshotContainer.Metadata.AggregateSequenceNumber.Should().Be(expectedVersion);
            snapshotContainer.Metadata.SnapshotName.Should().Be("thingy");
            snapshotContainer.Metadata.SnapshotVersion.Should().Be(1);
        }
 /// <summary>
 /// Deletes all snapshots matching <paramref name="criteria"/>.
 ///
 /// The <see cref="PersistentActor"/> will be notified about the status of the deletion
 /// via an <see cref="DeleteSnapshotsSuccess"/> or <see cref="DeleteSnapshotsFailure"/> message.
 /// </summary>
 public void DeleteSnapshots(SnapshotSelectionCriteria criteria)
 {
     SnapshotStore.Tell(new DeleteSnapshots(SnapshotterId, criteria));
 }
 /// <summary>
 /// Deletes the snapshot identified by <paramref name="sequenceNr"/>.
 ///
 /// The <see cref="PersistentActor"/> will be notified about the status of the deletion
 /// via an <see cref="DeleteSnapshotSuccess"/> or <see cref="DeleteSnapshotFailure"/> message.
 /// </summary>
 public void DeleteSnapshot(long sequenceNr)
 {
     SnapshotStore.Tell(new DeleteSnapshot(new SnapshotMetadata(SnapshotterId, sequenceNr)));
 }
 /// <summary>
 /// Saves <paramref name="snapshot"/> of current <see cref="ISnapshotter"/> state.
 ///
 /// The <see cref="PersistentActor"/> will be notified about the success or failure of this
 /// via an <see cref="SaveSnapshotSuccess"/> or <see cref="SaveSnapshotFailure"/> message.
 /// </summary>
 public void SaveSnapshot(object snapshot)
 {
     SnapshotStore.Tell(new SaveSnapshot(new SnapshotMetadata(SnapshotterId, SnapshotSequenceNr), snapshot));
 }
 /// <summary>
 /// Instructs the snapshot store to load the specified snapshot and send it via an
 /// <see cref="SnapshotOffer"/> to the running <see cref="PersistentActor"/>.
 /// </summary>
 public void LoadSnapshot(string persistenceId, SnapshotSelectionCriteria criteria, long toSequenceNr)
 {
     SnapshotStore.Tell(new LoadSnapshot(persistenceId, criteria, toSequenceNr));
 }
            public void ReturnNullIfNoSnapshots()
            {
                var streamId = Guid.NewGuid();

                Assert.Null(SnapshotStore.GetSnapshot(typeof(Object), streamId, 10));
            }
Beispiel #7
0
 public void SnapshotStore_should_not_load_a_snapshot_given_non_matching_timestamp_criteria()
 {
     SnapshotStore.Tell(new LoadSnapshot(Pid, new SnapshotSelectionCriteria(long.MaxValue, new DateTime(100000)), long.MaxValue), _senderProbe.Ref);
     _senderProbe.ExpectMsg <LoadSnapshotResult>(result => result.Snapshot == null && result.ToSequenceNr == long.MaxValue);
 }
Beispiel #8
0
 public void SnapshotStore_should_not_load_a_snapshot_given_an_invalid_persistence_id()
 {
     SnapshotStore.Tell(new LoadSnapshot("invalid", SnapshotSelectionCriteria.Latest, long.MaxValue), _senderProbe.Ref);
     _senderProbe.ExpectMsg <LoadSnapshotResult>(result => result.Snapshot == null && result.ToSequenceNr == long.MaxValue);
 }
Beispiel #9
0
        public void it_should_return_null_if_the_snapshot_does_not_exist()
        {
            var shouldbenull = SnapshotStore.GetSnapshot(Guid.NewGuid(), long.MaxValue);

            Assert.Null(shouldbenull);
        }
Beispiel #10
0
 public void DeleteSnapshot(long sequenceNr, DateTime timestamp)
 {
     SnapshotStore.Tell(new DeleteSnapshot(new SnapshotMetadata(SnapshotterId, sequenceNr, timestamp)));
 }
Beispiel #11
0
        private bool Initialized(object message)
        {
            void OnGetReplicationProgresses()
            {
                ReadReplicationProgresses().PipeTo(Sender,
                                                   success: data => new GetReplicationProgressesSuccess(data),
                                                   failure: cause => new GetReplicationProgressesFailure(cause));
            }

            void OnGetReplicationProgress(string sourceLogId)
            {
                var version = clock.VersionVector;

                ReadReplicationProgress(sourceLogId).PipeTo(Sender,
                                                            success: data => new GetReplicationProgressSuccess(sourceLogId, data, version),
                                                            failure: cause => new GetReplicationProgressFailure(cause));
            }

            void OnSetReplicationProgress(SetReplicationProgress m)
            {
                WriteReplicationProgresses(ImmutableDictionary <string, long> .Empty.Add(m.SourceLogId, m.ReplicationProgress))
                .PipeTo(Sender,
                        success: () => new SetReplicationProgressSuccess(m.SourceLogId, m.ReplicationProgress),
                        failure: cause => new SetReplicationProgressFailure(cause));
            }

            void OnReplay(Replay replay)
            {
                if (replay.AggregateId is null)
                {
                    if (!(replay.Subscriber is null))
                    {
                        registry = registry.RegisterDefaultSubscriber(replay.Subscriber);
                    }

                    var from = replay.FromSequenceNr;
                    var iid  = replay.InstanceId;
                    Read(AdjustFromSequenceNr(from), clock.SequenceNr, replay.Max)
                    .PipeTo(Sender,
                            success: data => new ReplaySuccess(data.Events, data.To, iid),
                            failure: cause => new ReplayFailure(cause, from, iid));
                }
                else
                {
                    var emitterAggregateId = replay.AggregateId;
                    if (!(replay.Subscriber is null))
                    {
                        registry = registry.RegisterAggregateSubscriber(replay.Subscriber, emitterAggregateId);
                    }

                    var from = replay.FromSequenceNr;
                    var iid  = replay.InstanceId;
                    Read(AdjustFromSequenceNr(from), clock.SequenceNr, replay.Max, emitterAggregateId)
                    .PipeTo(Sender,
                            success: data => new ReplaySuccess(data.Events, data.To, iid),
                            failure: cause => new ReplayFailure(cause, from, iid));
                }
            }

            void OnReplicationRead(ReplicationRead read)
            {
                if (!(channel is null))
                {
                    channel.Tell(read);
                }

                var from        = read.FromSequenceNr;
                var targetLogId = read.TargetLogId;

                remoteReplicationProgress = remoteReplicationProgress.SetItem(targetLogId, Math.Max(0, from - 1));
                ReplicationRead(from, clock.SequenceNr, read.Max, read.ScanLimit, e => e.IsReplicable(read.CurrentTargetVersionVector, read.Filter))
                .PipeTo(Self, Sender,
                        success: data => new ReplicationReadSuccess(data.Events.ToArray(), from, data.To, targetLogId, null),
                        failure: cause => new ReplicationReadFailure(new ReplicationReadSourceException(cause.Message), targetLogId));
            }

            void OnReplicationReadSuccess(ReplicationReadSuccess success)
            {
                // Post-exclude events using a possibly updated version vector received from the
                // target. This is an optimization to save network bandwidth. If omitted, events
                // are still excluded at target based on the current local version vector at the
                // target (for correctness).
                var targetLogId = success.TargetLogId;
                var currentTargetVersionVector = this.replicaVersionVectors.GetValueOrDefault(targetLogId, VectorTime.Zero);
                var updated = new List <DurableEvent>(success.Events.Count);

                foreach (var e in success.Events)
                {
                    if (!e.IsBefore(currentTargetVersionVector))
                    {
                        updated.Add(e);
                    }
                }
                var reply = new ReplicationReadSuccess(updated, success.FromSequenceNr, success.ReplicationProgress, success.TargetLogId, clock.VersionVector);

                Sender.Tell(reply);
                if (!(channel is null))
                {
                    channel.Tell(reply);
                }
                LogFilterStatistics("source", success.Events, updated);
            }

            void OnReplicationReadFailure(ReplicationReadFailure failure)
            {
                Sender.Tell(failure);
                if (!(channel is null))
                {
                    channel.Tell(failure);
                }
            }

            void OnDelete(Delete delete)
            {
                var actualDeletedToSeqNr = Math.Max(Math.Min(delete.ToSequenceNr, clock.SequenceNr), deletionMetadata.ToSequenceNr);

                if (actualDeletedToSeqNr > deletionMetadata.ToSequenceNr)
                {
                    var updatedDeletionMetadata = new DeletionMetadata(actualDeletedToSeqNr, delete.RemoteLogIds);
                    WriteDeletionMetadata(updatedDeletionMetadata).PipeTo(Self, Sender,
                                                                          success: () => new DeleteSuccess(updatedDeletionMetadata.ToSequenceNr, updatedDeletionMetadata.RemoteLogIds),
                                                                          failure: cause => new DeleteFailure(cause));
                }
            }

            void OnDeleteSuccess(DeleteSuccess success)
            {
                this.deletionMetadata = new DeletionMetadata(success.DeletedTo, success.RemoteLogIds ?? ImmutableHashSet <string> .Empty);
                Self.Tell(new PhysicalDelete());
                Sender.Tell(success);
            }

            void OnPhysicalDelete()
            {
                if (!physicalDeletionRunning)
                {
                    // Becomes Long.MaxValue in case of an empty-set to indicate that all event are replicated as required
                    var replicatedSeqNr = deletionMetadata.RemoteLogIds.IsEmpty ? long.MaxValue : deletionMetadata.RemoteLogIds.Select(id => remoteReplicationProgress.GetValueOrDefault(id, 0)).Min();
                    var deleteTo        = Math.Min(deletionMetadata.ToSequenceNr, replicatedSeqNr);
                    physicalDeletionRunning = true;
                    Delete(deleteTo).PipeTo(Self,
                                            success: data => new PhysicalDeleteSuccess(data),
                                            failure: cause => new PhysicalDeleteFailure(cause));
                }
            }

            void OnPhysicalDeleteSuccess(long deletedTo)
            {
                physicalDeletionRunning = false;
                if (deletionMetadata.ToSequenceNr > deletedTo)
                {
                    scheduler.ScheduleTellOnce(Settings.DeletionRetryDelay, Self, new PhysicalDelete(), Self);
                }
            }

            void OnPhysicalDeleteFailure(Exception cause)
            {
                if (!(cause is PhysicalDeletionNotSupportedException))
                {
                    var delay = Settings.DeletionRetryDelay;
                    Logger.Error(cause, "Physical deletion of events failed. Retry in {0}", delay);
                    physicalDeletionRunning = false;
                    scheduler.ScheduleTellOnce(delay, Self, new PhysicalDelete(), Self);
                }
            }

            void OnLoadSnapshot(LoadSnapshot load)
            {
                var emitterId = load.EmitterId;
                var iid       = load.InstanceId;

                SnapshotStore.Load(emitterId).PipeTo(Sender,
                                                     success: data => new LoadSnapshotSuccess(data, iid),
                                                     failure: cause => new LoadSnapshotFailure(cause, iid));
            }

            void OnSaveSnapshot(SaveSnapshot save)
            {
                var snapshot = save.Snapshot;
                var iid      = save.InstanceId;

                SnapshotStore.Save(snapshot).PipeTo(Sender, save.Initiator,
                                                    success: () => new SaveSnapshotSuccess(snapshot.Metadata, iid),
                                                    failure: cause => new SaveSnapshotFailure(snapshot.Metadata, cause, iid));
            }

            void OnDeleteSnapshots(long lowerSequenceNr)
            {
                SnapshotStore.Delete(lowerSequenceNr).PipeTo(Sender,
                                                             success: () => new DeleteSnapshotsSuccess(),
                                                             failure: cause => new DeleteSnapshotsFailure(cause));
            }

            void OnAdjustEventLogClock()
            {
                clock = clock.AdjustSequenceNrToProcessTime(Id);
                WriteEventLogClockSnapshot(clock).PipeTo(Sender,
                                                         success: () => new AdjustEventLogClockSuccess(clock),
                                                         failure: cause => new AdjustEventLogClockFailure(cause));
            }

            void OnWriteBatchesSuccess(WriteBatchesSuccess success)
            {
                this.clock = success.Clock;
                foreach (var write in success.UpdatedWrites)
                {
                    write.ReplyTo.Tell(new WriteSuccess(write.Events, write.CorrelationId, write.InstanceId), write.Initiator);
                    registry.NotifySubscribers(write.Events, s => !s.Equals(write.ReplyTo));
                }

                if (!(channel is null))
                {
                    channel.Tell(new NotificationChannel.Updated(success.UpdatedEvents));
                }
            }

            void OnWriteBatchesFailure(WriteBatchesFailure failure)
            {
                foreach (var write in failure.Writes)
                {
                    write.ReplyTo.Tell(new WriteFailure(write.Events, failure.Cause, write.CorrelationId, write.InstanceId), write.Initiator);
                }
            }

            void OnWriteReplicatedBatchesSuccess(WriteReplicatedBatchesSuccess success)
            {
                this.clock = success.Clock;
                foreach (var w in success.UpdatedWrites)
                {
                    var builder = ImmutableDictionary.CreateBuilder <string, ReplicationMetadata>();
                    foreach (var(k, v) in w.Metadata)
                    {
                        builder[k] = v.WithVersionVector(success.Clock.VersionVector);
                    }

                    var ws = new ReplicationWriteSuccess(w.Events, builder.ToImmutable(), w.ContinueReplication);
                    registry.NotifySubscribers(w.Events);
                    if (!(channel is null))
                    {
                        channel.Tell(w);
                    }

                    // Write failure of replication progress can be ignored. Using a stale
                    // progress to resume replication will redundantly read events from a
                    // source log but these events will be successfully identified as
                    // duplicates, either at source or latest at target.
                    WriteReplicationProgresses(w.ReplicationProgresses.ToImmutableDictionary()).PipeTo(w.ReplyTo,
                                                                                                       success: () => ws,
                                                                                                       failure: cause =>
                    {
                        Logger.Warning("Writing of replication progress failed: {0}", cause);
                        return(new ReplicationWriteFailure(cause));
                    });
                }

                if (!(channel is null))
                {
                    channel.Tell(new NotificationChannel.Updated(success.UpdatedEvents));
                }
            }

            void OnWriteReplicatedBatchesFailure(WriteReplicatedBatchesFailure failure)
            {
                foreach (var write in failure.Writes)
                {
                    write.ReplyTo.Tell(new ReplicationWriteFailure(failure.Cause));
                }
            }

            switch (message)
            {
            case GetEventLogClock _: Sender.Tell(new GetEventLogClockSuccess(this.clock)); return(true);

            case GetReplicationProgresses _: OnGetReplicationProgresses(); return(true);

            case GetReplicationProgress _: OnGetReplicationProgress(((GetReplicationProgress)message).SourceLogId); return(true);

            case SetReplicationProgress _: OnSetReplicationProgress((SetReplicationProgress)message); return(true);

            case Replay _: OnReplay((Replay)message); return(true);

            case ReplicationRead _: OnReplicationRead((ReplicationRead)message); return(true);

            case ReplicationReadSuccess _: OnReplicationReadSuccess((ReplicationReadSuccess)message); return(true);

            case ReplicationReadFailure _: OnReplicationReadFailure((ReplicationReadFailure)message); return(true);

            case Write _: ProcessWrites(((Write)message).WithReplyToDefault(Sender)); return(true);

            case WriteMany _:
                ProcessWrites(((WriteMany)message).Writes.ToArray());
                Sender.Tell(WriteManyComplete.Instance);
                return(true);

            case WriteBatchesSuccess _: OnWriteBatchesSuccess((WriteBatchesSuccess)message); return(true);

            case WriteBatchesFailure _: OnWriteBatchesFailure((WriteBatchesFailure)message); return(true);

            case ReplicationWrite _: ProcessReplicationWrites(((ReplicationWrite)message).WithReplyToDefault(Sender)); return(true);

            case WriteReplicatedBatchesSuccess _: OnWriteReplicatedBatchesSuccess((WriteReplicatedBatchesSuccess)message); return(true);

            case WriteReplicatedBatchesFailure _: OnWriteReplicatedBatchesFailure((WriteReplicatedBatchesFailure)message); return(true);

            case ReplicationWriteMany _:
                ProcessReplicationWrites(((ReplicationWriteMany)message).Writes.ToArray());
                Sender.Tell(new ReplicationWriteManyComplete());
                return(true);

            case Delete _: OnDelete((Delete)message); return(true);

            case DeleteSuccess _: OnDeleteSuccess((DeleteSuccess)message); return(true);

            case DeleteFailure _: Sender.Tell(message); return(true);

            case PhysicalDelete _: OnPhysicalDelete(); return(true);

            case PhysicalDeleteSuccess _: OnPhysicalDeleteSuccess(((PhysicalDeleteSuccess)message).DeletedTo); return(true);

            case PhysicalDeleteFailure _: OnPhysicalDeleteFailure(((PhysicalDeleteFailure)message).Cause); return(true);

            case LoadSnapshot _: OnLoadSnapshot((LoadSnapshot)message); return(true);

            case SaveSnapshot _: OnSaveSnapshot((SaveSnapshot)message); return(true);

            case DeleteSnapshots _: OnDeleteSnapshots(((DeleteSnapshots)message).LowerSequenceNr); return(true);

            case AdjustEventLogClock _: OnAdjustEventLogClock(); return(true);

            case Terminated _:
                registry = registry.UnregisterSubscriber(((Terminated)message).ActorRef);
                return(true);

            default: return(false);
            }
        }
Beispiel #12
0
        public async Task Delete(Guid aggregateId)
        {
            await SnapshotStore.Delete(aggregateId);

            // Todo: Delete events?
        }
        public void it_should_return_null_if_the_snapshot_does_not_exist()
        {
            var shouldbenull = SnapshotStore.GetSnapshot(Guid.NewGuid());

            Assert.That(shouldbenull, Is.Null);
        }
Beispiel #14
0
 public EventStore(E05.Test.Infrastructure.Bus bus, EventsSerializer eventsSerializer, SnapshotStore snapshotStore)
 {
     Events            = new List <EventDescriptor>();
     _bus              = bus;
     _eventsSerializer = eventsSerializer;
     _snapshotStore    = snapshotStore;
 }