コード例 #1
0
        public async Task Getting_and_storing_projections_and_cursors_can_operate_transactionally_via_a_closure()
        {
            BalanceProjection finalProjection = null;

            var catchup = StreamCatchup.Create(stream);
            FetchAndSaveProjection <BalanceProjection> fetchAndSaveProjection = (async(id, callAggregatorPipeline) =>
            {
                using (var transaction = new TransactionScope())
                {
                    // e.g. get the projection / cursor from the store
                    var proj = new BalanceProjection
                    {
                        CursorPosition = 5
                    };

                    proj = await callAggregatorPipeline(proj);

                    finalProjection = proj;

                    // save the projection / cursor back to the store
                    transaction.Complete();
                }
            });

            catchup.Subscribe(new BalanceProjector(), fetchAndSaveProjection);

            store.WriteEvents(streamId, amount: 100m, howMany: 5);

            await catchup.RunSingleBatch();

            finalProjection.Balance.Should().Be(100m);
        }
コード例 #2
0
        public async Task One_stream_can_transparently_delegate_to_another()
        {
            var upstream = NEventStoreStream.AllEvents(store);

            store.WriteEvents(i => new AccountOpened(), 100);
            var projection = new Projection <int, string>();

            var dependentStream = Stream.Create <int, string>(
                async q =>
            {
                var mapped = upstream.Map(e => new[] { e.Count() });
                var batch  = await mapped.Fetch(q);
                return(batch);
            });

            var catchup = StreamCatchup.Create(dependentStream, batchSize: 50);

            FetchAndSaveProjection <Projection <int, string> > manageProjection = async(id, aggregate) =>
            {
                await aggregate(projection);
            };

            catchup.Subscribe(async(p, b) =>
            {
                p.Value += b.Sum();
                return(p);
            }, manageProjection);

            await catchup.RunSingleBatch();

            Console.WriteLine(projection.ToLogString());

            projection.Value.Should().Be(50);
            projection.CursorPosition.Should().Be("50");
        }
コード例 #3
0
 /// <summary>
 /// Subscribes the specified aggregator to a catchup.
 /// </summary>
 /// <typeparam name="TProjection">The type of the projection.</typeparam>
 /// <typeparam name="TData">The type of the stream's data.</typeparam>
 /// <typeparam name="TCursor">The type of the cursor.</typeparam>
 /// <param name="catchup">The catchup.</param>
 /// <param name="aggregator">The aggregator.</param>
 /// <returns>A disposable that, when disposed, unsubscribes the aggregator.</returns>
 public static IDisposable Subscribe <TProjection, TData, TCursor>(
     this IStreamCatchup <TData, TCursor> catchup,
     IStreamAggregator <TProjection, TData> aggregator,
     FetchAndSaveProjection <TProjection> manageProjection,
     HandleAggregatorError <TProjection> onError = null)
 {
     return(catchup.SubscribeAggregator(aggregator, manageProjection, onError));
 }
コード例 #4
0
        /// <summary>
        /// Creates a multiple-stream catchup.
        /// </summary>
        /// <typeparam name="TData">The type of the stream's data.</typeparam>
        /// <typeparam name="TUpstreamCursor">The type of the upstream cursor.</typeparam>
        /// <typeparam name="TDownstreamCursor">The type of the downstream cursor.</typeparam>
        /// <param name="stream">The stream.</param>
        /// <param name="batchSize">The number of items to retrieve from the stream per batch.</param>
        public static IStreamCatchup <TData, TUpstreamCursor> All <TData, TUpstreamCursor, TDownstreamCursor>(
            IStream <IStream <TData, TDownstreamCursor>, TUpstreamCursor> stream,
            FetchAndSaveProjection <ICursor <TUpstreamCursor> > manageCursor,
            int?batchSize = null)
        {
            var upstreamCatchup = new SingleStreamCatchup <IStream <TData, TDownstreamCursor>, TUpstreamCursor>(stream, batchSize: batchSize);

            return(new MultiStreamCatchup <TData, TUpstreamCursor, TDownstreamCursor>(
                       upstreamCatchup,
                       manageCursor));
        }
コード例 #5
0
 /// <summary>
 /// Distributes a stream catchup the among one or more partitions using a specified distributor.
 /// </summary>
 /// <remarks>If no distributor is provided, then distribution is done in-process.</remarks>
 public static IStreamCatchup <TData, TCursor> DistributeAmong <TData, TCursor, TPartition>(
     this IPartitionedStream <TData, TCursor, TPartition> streams,
     IEnumerable <IStreamQueryPartition <TPartition> > partitions,
     int?batchSize = null,
     FetchAndSaveProjection <ICursor <TCursor> > cursorPerPartition = null,
     IDistributor <IStreamQueryPartition <TPartition> > distributor = null)
 {
     return(new DistributedStreamCatchup <TData, TCursor, TPartition>(
                streams,
                partitions,
                batchSize,
                cursorPerPartition,
                distributor));
 }
コード例 #6
0
        public static FetchAndSaveProjection <TProjection> Catch <TProjection>(
            this FetchAndSaveProjection <TProjection> fetchAndSaveProjection,
            HandleAggregatorError <TProjection> onError)
        {
            return(async(id, aggregate) =>
            {
                Exception innerException = null;

                try
                {
                    await fetchAndSaveProjection(id, async projection =>
                    {
                        var resultingProjection = default(TProjection);

                        try
                        {
                            resultingProjection = await aggregate(projection);
                        }
                        catch (Exception exception)
                        {
                            var error = CheckErrorHandler(onError, exception, projection);

                            if (!error.ShouldContinue)
                            {
                                throw;
                            }

                            innerException = exception;

                            return error.Projection;
                        }

                        return resultingProjection;
                    });
                }
                catch (Exception exception)
                {
                    if (exception != innerException)
                    {
                        var error = CheckErrorHandler(onError, exception);

                        if (!error.ShouldContinue)
                        {
                            throw;
                        }
                    }
                }
            });
        }
コード例 #7
0
        public IDisposable SubscribeAggregator <TProjection>(
            IStreamAggregator <TProjection, TData> aggregator,
            FetchAndSaveProjection <TProjection> fetchAndSaveProjection,
            HandleAggregatorError <TProjection> onError)
        {
            var added = aggregatorSubscriptions.TryAdd(typeof(TProjection),
                                                       new AggregatorSubscription <TProjection, TData>(aggregator,
                                                                                                       fetchAndSaveProjection,
                                                                                                       onError));

            if (!added)
            {
                throw new InvalidOperationException(string.Format("Aggregator for projection of type {0} is already subscribed.", typeof(TProjection)));
            }

            return(Disposable.Create(() =>
            {
                IAggregatorSubscription _;
                aggregatorSubscriptions.TryRemove(typeof(TProjection), out _);
            }));
        }
        public DistributedStreamCatchup(
            IPartitionedStream <TData, TCursor, TPartition> partitionedStream,
            IEnumerable <IStreamQueryPartition <TPartition> > partitions,
            int?batchSize = null,
            FetchAndSaveProjection <ICursor <TCursor> > manageCursor       = null,
            IDistributor <IStreamQueryPartition <TPartition> > distributor = null) : base(batchSize)
        {
            if (partitionedStream == null)
            {
                throw new ArgumentNullException("partitionedStream");
            }
            if (partitions == null)
            {
                throw new ArgumentNullException("partitions");
            }

            this.partitionedStream = partitionedStream;

            this.distributor = distributor ?? partitions.DistributeQueriesInProcess();

            manageCursor = manageCursor ?? ((id, aggregate) => aggregate(null));

            this.distributor
#if DEBUG
            .Trace()     // TODO: (DistributedStreamCatchup) figure out a way to let people Trace this distributor
#endif
            .OnReceive(async lease =>
            {
                await manageCursor(lease.ResourceName, async cursor =>
                {
                    var catchup = new SingleStreamCatchup <TData, TCursor>(
                        await partitionedStream.GetStream(lease.Resource),
                        initialCursor: cursor,
                        batchSize: batchSize,
                        subscriptions: new ConcurrentDictionary <Type, IAggregatorSubscription>(aggregatorSubscriptions));

                    return(await catchup.RunSingleBatch());
                });
            });
        }
コード例 #9
0
        /// <summary>
        /// Initializes a new instance of the <see cref="MultiStreamCatchup{TData, TUpstreamCursor, TDownstreamCursor}"/> class.
        /// </summary>
        /// <param name="upstreamCatchup">The upstream catchup.</param>
        /// <exception cref="ArgumentNullException">
        /// upstreamCatchup
        /// or
        /// manageCursor
        /// </exception>
        public MultiStreamCatchup(
            IStreamCatchup <IStream <TData, TDownstreamCursor>, TUpstreamCursor> upstreamCatchup,
            FetchAndSaveProjection <ICursor <TUpstreamCursor> > manageCursor)
        {
            if (upstreamCatchup == null)
            {
                throw new ArgumentNullException("upstreamCatchup");
            }
            if (manageCursor == null)
            {
                throw new ArgumentNullException("manageCursor");
            }
            this.upstreamCatchup = upstreamCatchup;

            upstreamCatchup.Subscribe(
                async(cursor, streams) =>
            {
                // ths upstream cursor is not passed here because the downstream streams have their own independent cursors
                await Task.WhenAll(streams.Select(stream => RunSingleBatch(stream)));

                return(cursor);
            },
                manageCursor);
        }
コード例 #10
0
        public AggregatorSubscription(
            IStreamAggregator <TProjection, TData> aggregator,
            FetchAndSaveProjection <TProjection> fetchAndSaveProjection = null,
            HandleAggregatorError <TProjection> onError = null)
        {
            if (aggregator == null)
            {
                throw new ArgumentNullException("aggregator");
            }

            OnError = onError ?? (error => { });

            if (onError != null)
            {
                fetchAndSaveProjection = fetchAndSaveProjection.Catch(onError);
            }

            FetchAndSaveProjection = fetchAndSaveProjection ??
                                     (async(streamId, aggregate) =>
            {
                await aggregate(Activator.CreateInstance <TProjection>());
            });
            Aggregator = aggregator;
        }