public async Task empty_snapshot_is_not_persisted()
        {
            var nullSnapshot = new SnapshotInfo("empty", 0, null, string.Empty);
            await _snapshots.AddAsync("empty", nullSnapshot);

            var tape = new Recorder();
            await Store.ReadForwardAsync("empty", 0, tape);

            Assert.True(tape.IsEmpty);
        }
Example #2
0
        public async Task should_load_snapshots()
        {
            await _snapshots.AddAsync("sequence_1/Sum", new SnapshotInfo(
                                          "sequence_1", 9, new Sum {
                Total = 45
            }, "1"
                                          )).ConfigureAwait(false);

            var sequence = await CreateStream("sequence_1").ConfigureAwait(false);

            var result = await sequence
                         .Fold()
                         .WithCache(_snapshots)
                         .RunAsync <Sum>().ConfigureAwait(false);

            Assert.Equal(45 + 10, result.Total);
        }
Example #3
0
 public static Task AddAsync(
     this ISnapshotStore snapshots,
     string snapshotPartitionId,
     SnapshotInfo snapshot
     )
 {
     return(snapshots.AddAsync(snapshotPartitionId, snapshot, CancellationToken.None));
 }
Example #4
0
        public async Task SaveAsync <T>(
            T aggregate,
            string operationId,
            Action <IHeadersAccessor> headers,
            CancellationToken cancellationToken
            ) where T : IAggregate
        {
            var persister = (IEventSourcedAggregate)aggregate;
            var changeSet = persister.GetChangeSet();

            if (changeSet.IsEmpty() && !PersistEmptyChangeset)
            {
                return;
            }

            var stream = GetStream(aggregate);

            if (!stream.IsWritable)
            {
                throw new AggregateReadOnlyException();
            }

            if (aggregate is IInvariantsChecker checker)
            {
                var check = checker.CheckInvariants();
                if (check.IsInvalid)
                {
                    // checks for idempotency
                    if (await stream.ContainsOperationAsync(operationId).ConfigureAwait(false))
                    {
                        // already stored, we just need to skip and avoid exception
                        return;
                    }
                }
                check.ThrowIfInvalid();
            }

            headers?.Invoke(changeSet);

            var chunk = await stream.AppendAsync(changeSet, operationId, cancellationToken).ConfigureAwait(false);

            //remember to check if the chunk was really persisted or skipped because of operationId idempotency.
            if (chunk != null)
            {
                persister.Persisted(changeSet);

                if (_snapshots != null && aggregate is ISnaphottable snaphottable)
                {
                    //we need to await, it's responsibility of the snapshot provider to clone & store state (sync or async)
                    await _snapshots.AddAsync(aggregate.Id, snaphottable.GetSnapshot(), cancellationToken).ConfigureAwait(false);
                }
            }
            else
            {
                Clear();
                //persister.ClearUncommitted();
            }
        }
Example #5
0
        public async Task <TResult> RunAsync <TResult>(IPayloadProcessor processor, CancellationToken cancellationToken)
            where TResult : new()
        {
            long    startIndex = 1;
            TResult state      = default(TResult);
            string  snapshotId = this._source.Id + "/" + typeof(TResult).Name;

            if (_snapshots != null)
            {
                var si = await LoadSnapshot(snapshotId, cancellationToken).ConfigureAwait(false);

                if (si != null)
                {
                    state = (TResult)si.Payload;
                    if (_upToIndex.HasValue && si.SourceVersion == _upToIndex.Value)
                    {
                        return(state);
                    }

                    startIndex = si.SourceVersion + 1;
                }
            }

            if (state == null)
            {
                state = new TResult();
            }

            var reducer = new Reducer <TResult>(state, _onMissing, processor);
            await _source.ReadAsync(reducer, startIndex, _upToIndex ?? long.MaxValue, cancellationToken)
            .ConfigureAwait(false);

            if (_snapshots != null && reducer.LastIndex > 0)
            {
                await _snapshots.AddAsync(snapshotId, new SnapshotInfo(
                                              _source.Id,
                                              reducer.LastIndex,
                                              state,
                                              "1"
                                              )).ConfigureAwait(false);
            }

            return(state);
        }
Example #6
0
        public async Task Save <T>(
            T aggregate,
            string operationId,
            Action <IHeadersAccessor> headers,
            CancellationToken cancellationToken
            ) where T : IAggregate
        {
            var persister = (IEventSourcedAggregate)aggregate;
            var changeSet = persister.GetChangeSet();

            if (changeSet.IsEmpty() && !PersistEmptyChangeset)
            {
                return;
            }

            if (aggregate is IInvariantsChecker checker)
            {
                var check = checker.CheckInvariants();
                check.ThrowIfInvalid();
            }

            var stream = GetStream(aggregate);

            if (!stream.IsWritable)
            {
                throw new AggregateReadOnlyException();
            }

            headers?.Invoke(changeSet);

            await stream.AppendAsync(changeSet, operationId, cancellationToken).ConfigureAwait(false);

            persister.Persisted(changeSet);

            if (_snapshots != null && aggregate is ISnaphottable snaphottable)
            {
                //we need to await, it's responsibility of the snapshot provider to clone & store state (sync or async)
                await _snapshots.AddAsync(aggregate.Id, snaphottable.GetSnapshot(), cancellationToken).ConfigureAwait(false);
            }
        }