示例#1
0
        public ReaderPipe(
            IEventReader reader, ICheckpointStore checkpointStore, Func <PrepareContext, ValueTask> send
            )
        {
            ILog log = LogProvider.GetCurrentClassLogger();

            _pipe = Pipe.New <ReaderContext>(
                cfg => {
                cfg.UseConcurrencyLimit(1);

                cfg.UseRetry(
                    retry => {
                    retry.Incremental(
                        100,
                        TimeSpan.Zero,
                        TimeSpan.FromMilliseconds(100)
                        );
                    retry.ConnectRetryObserver(new LoggingRetryObserver());
                }
                    );
                cfg.UseLog();

                cfg.UseExecuteAsync(Reader);
            }
                );

            async Task Reader(ReaderContext ctx)
            {
                try {
                    var start = await checkpointStore.LoadCheckpoint(ctx.CancellationToken).ConfigureAwait(false);

                    log.Info("Reading from {Position}", start);

                    await reader.ReadEvents(
                        start,
                        async read => {
                        ReplicationMetrics.ReadingPosition.Set(read.Position.EventPosition);

                        await send(new PrepareContext(read, ctx.CancellationToken)).ConfigureAwait(false);
                    },
                        ctx.CancellationToken
                        ).ConfigureAwait(false);
                }
                catch (OperationCanceledException) {
                    // it's ok
                }
                finally {
                    log.Info("Reader stopped");
                }
            }
        }
        public async Task Replicate(
            IEventReader reader, IEventWriter writer, ICheckpointStore checkpointStore,
            CancellationToken cancellationToken,
            Func <EventRead, bool> filter,
            Func <EventRead, ValueTask <EventWrite> > transform
            )
        {
            var cts       = new CancellationTokenSource();
            var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cts.Token);
            var start     = await checkpointStore.LoadCheckpoint();

            var retryPolicy = Policy
                              .Handle <Exception>(ex => !(ex is OperationCanceledException))
                              .RetryForeverAsync(ex => Log.Warn(ex, "Writer error: {Error}, retrying", ex.Message));

            var channel = Channel.CreateBounded <EventRead>(Capacity)
                          .Source(reader.ReadEvents(start, linkedCts.Token));

            if (filter != null)
            {
                channel = channel.Filter(x => Try(x, filter));
            }

            Func <EventRead, ValueTask <EventWrite> > transformFunction;

            if (transform != null)
            {
                transformFunction = TryTransform;
            }
            else
            {
                transformFunction = DefaultTransform;
            }

            var resultChannel = channel
                                .PipeAsync(5, transformFunction, Capacity, false, linkedCts.Token)
                                .PipeAsync(WriteEvent, Capacity, true, linkedCts.Token)
                                .Batch(1024, true, true);

            var lastPosition = new Position();

            try {
                await resultChannel.ReadUntilCancelledAsync(linkedCts.Token, StoreCheckpoint, true);
            }
            catch (Exception e) {
                Log.Fatal(e, "Unable to proceed: {Error}", e.Message);
            }
            finally {
                Log.Info("Last recorded position: {Position}", lastPosition);
            }

            async ValueTask <Position> WriteEvent(EventWrite write)
            {
                await retryPolicy.ExecuteAsync(() => writer.WriteEvent(write));

                return(write.SourcePosition);
            }

            T Try <T>(EventRead evt, Func <EventRead, T> func)
            {
                try {
                    return(func(evt));
                }
                catch (Exception e) {
                    Log.Error(e, "Error in the pipeline: {Error}", e.Message);
                    cts.Cancel();
                    throw;
                }
            }

            async ValueTask <EventWrite> TryTransform(EventRead evt) => await Try(evt, transform);

            ValueTask StoreCheckpoint(List <Position> positions)
            {
                lastPosition = positions.Last();
                return(checkpointStore.StoreCheckpoint(lastPosition));
            }
        }