コード例 #1
0
                public CatchupAllSubscription(Guid subscriptionId,
                                              IPublisher bus,
                                              Position position,
                                              bool resolveLinks,
                                              ClaimsPrincipal user,
                                              bool requiresLeader,
                                              IReadIndex readIndex,
                                              CancellationToken cancellationToken)
                {
                    if (bus == null)
                    {
                        throw new ArgumentNullException(nameof(bus));
                    }

                    if (readIndex == null)
                    {
                        throw new ArgumentNullException(nameof(readIndex));
                    }

                    _subscriptionId = subscriptionId;
                    _bus            = bus;
                    _nextPosition   = position == Position.End
                                                ? Position.FromInt64(readIndex.LastIndexedPosition, readIndex.LastIndexedPosition)
                                                : position;
                    _startPosition       = position == Position.End ? Position.Start : position;
                    _resolveLinks        = resolveLinks;
                    _user                = user;
                    _requiresLeader      = requiresLeader;
                    _disposedTokenSource = new CancellationTokenSource();
                    _buffer              = new ConcurrentQueue <ResolvedEvent>();
                    _tokenRegistration   = cancellationToken.Register(_disposedTokenSource.Dispose);
                    _currentPosition     = _startPosition;
                    Log.Information("Catch-up subscription {subscriptionId} to $all running...", _subscriptionId);
                }
コード例 #2
0
                public async ValueTask <bool> MoveNextAsync()
                {
ReadLoop:
                    if (_disposedTokenSource.IsCancellationRequested)
                    {
                        return(false);
                    }

                    if (_buffer.TryDequeue(out var current))
                    {
                        _current         = current;
                        _currentPosition = Position.FromInt64(current.OriginalPosition.Value.CommitPosition,
                                                              current.OriginalPosition.Value.PreparePosition);
                        return(true);
                    }

                    var correlationId = Guid.NewGuid();

                    var readNextSource = new TaskCompletionSource <bool>();

                    var(commitPosition, preparePosition) = _nextPosition.ToInt64();

                    Log.Verbose(
                        "Catch-up subscription {subscriptionId} to $all reading next page starting from {nextPosition}.",
                        _subscriptionId, _nextPosition);

                    _bus.Publish(new ClientMessage.ReadAllEventsForward(
                                     correlationId, correlationId, new CallbackEnvelope(OnMessage), commitPosition, preparePosition,
                                     ReadBatchSize, _resolveLinks, _requiresLeader, default, _user));
コード例 #3
0
                public CatchupAllSubscription(Guid subscriptionId,
                                              IPublisher bus,
                                              Position position,
                                              bool resolveLinks,
                                              IEventFilter eventFilter,
                                              ClaimsPrincipal user,
                                              bool requiresLeader,
                                              IReadIndex readIndex,
                                              uint?maxWindowSize,
                                              uint checkpointIntervalMultiplier,
                                              Func <Position, Task> checkpointReached,
                                              CancellationToken cancellationToken)
                {
                    if (bus == null)
                    {
                        throw new ArgumentNullException(nameof(bus));
                    }

                    if (eventFilter == null)
                    {
                        throw new ArgumentNullException(nameof(eventFilter));
                    }

                    if (readIndex == null)
                    {
                        throw new ArgumentNullException(nameof(readIndex));
                    }

                    if (checkpointReached == null)
                    {
                        throw new ArgumentNullException(nameof(checkpointReached));
                    }

                    if (checkpointIntervalMultiplier == 0)
                    {
                        throw new ArgumentOutOfRangeException(nameof(checkpointIntervalMultiplier));
                    }

                    _subscriptionId = subscriptionId;
                    _bus            = bus;
                    _nextPosition   = position == Position.End
                                                ? Position.FromInt64(readIndex.LastIndexedPosition, readIndex.LastIndexedPosition)
                                                : position;
                    _startPosition       = position == Position.End ? Position.Start : position;
                    _resolveLinks        = resolveLinks;
                    _eventFilter         = eventFilter;
                    _user                = user;
                    _requiresLeader      = requiresLeader;
                    _checkpointReached   = checkpointReached;
                    _maxWindowSize       = maxWindowSize ?? ReadBatchSize;
                    _checkpointInterval  = checkpointIntervalMultiplier * _maxWindowSize;
                    _disposedTokenSource = new CancellationTokenSource();
                    _buffer              = new ConcurrentQueueWrapper <(ResolvedEvent, Position?)>();

                    _tokenRegistration         = cancellationToken.Register(_disposedTokenSource.Dispose);
                    _currentPosition           = _startPosition;
                    _checkpointIntervalCounter = 0;
                    Log.Information("Catch-up subscription {subscriptionId} to $all:{eventFilter} running...",
                                    _subscriptionId, _eventFilter);
                }
コード例 #4
0
        private static ReadResp.Types.ReadEvent.Types.RecordedEvent ConvertToRecordedEvent(
            ReadReq.Types.Options.Types.UUIDOption uuidOption, EventRecord e, long?commitPosition,
            long?preparePosition)
        {
            if (e == null)
            {
                return(null);
            }
            var position = Position.FromInt64(commitPosition ?? -1, preparePosition ?? -1);

            return(new ReadResp.Types.ReadEvent.Types.RecordedEvent {
                Id = uuidOption.ContentCase switch {
                    ReadReq.Types.Options.Types.UUIDOption.ContentOneofCase.String => new UUID {
                        String = e.EventId.ToString()
                    },
                    _ => Uuid.FromGuid(e.EventId).ToDto()
                },
コード例 #5
0
 private void Subscribe(Position?startPosition)
 {
     if (startPosition == Position.End)
     {
         GoLive(Position.End);
     }
     else if (startPosition == null)
     {
         CatchUp(Position.Start);
     }
     else
     {
         var(commitPosition, preparePosition) = startPosition.Value.ToInt64();
         var indexResult =
             _readIndex.ReadAllEventsForward(new TFPos(commitPosition, preparePosition), 1);
         CatchUp(Position.FromInt64(indexResult.NextPos.CommitPosition,
                                    indexResult.NextPos.PreparePosition));
     }
 }
コード例 #6
0
            public AllSubscription(IPublisher bus,
                                   Position?startPosition,
                                   bool resolveLinks,
                                   ClaimsPrincipal user,
                                   bool requiresLeader,
                                   IReadIndex readIndex,
                                   CancellationToken cancellationToken)
            {
                if (bus == null)
                {
                    throw new ArgumentNullException(nameof(bus));
                }

                if (readIndex == null)
                {
                    throw new ArgumentNullException(nameof(readIndex));
                }

                _subscriptionId      = Guid.NewGuid();
                _bus                 = bus;
                _resolveLinks        = resolveLinks;
                _user                = user;
                _requiresLeader      = requiresLeader;
                _cancellationToken   = cancellationToken;
                _subscriptionStarted = new TaskCompletionSource <bool>();
                _channel             = Channel.CreateBounded <ResolvedEvent>(BoundedChannelOptions);
                _semaphore           = new SemaphoreSlim(1, 1);

                SubscriptionId = _subscriptionId.ToString();

                var startPositionExclusive = startPosition == Position.End
                                        ? Position.FromInt64(readIndex.LastIndexedPosition, readIndex.LastIndexedPosition)
                                        : startPosition ?? Position.Start;

                _startPositionExclusive = new TFPos((long)startPositionExclusive.CommitPosition,
                                                    (long)startPositionExclusive.PreparePosition);

                Subscribe(startPositionExclusive, startPosition != Position.End);
            }
コード例 #7
0
            public AllSubscription(IPublisher bus,
                                   Position?startPosition,
                                   bool resolveLinks,
                                   ClaimsPrincipal user,
                                   bool requiresLeader,
                                   IReadIndex readIndex,
                                   CancellationToken cancellationToken)
            {
                if (bus == null)
                {
                    throw new ArgumentNullException(nameof(bus));
                }

                if (readIndex == null)
                {
                    throw new ArgumentNullException(nameof(readIndex));
                }

                _subscriptionId      = Guid.NewGuid();
                _bus                 = bus;
                _resolveLinks        = resolveLinks;
                _user                = user;
                _requiresLeader      = requiresLeader;
                _readIndex           = readIndex;
                _cancellationToken   = cancellationToken;
                _subscriptionStarted = new TaskCompletionSource <bool>();
                _subscriptionStarted.SetResult(true);

                _inner = startPosition == Position.End
                                        ? (IStreamSubscription) new LiveStreamSubscription(_subscriptionId, _bus,
                                                                                           Position.FromInt64(_readIndex.LastIndexedPosition, _readIndex.LastIndexedPosition),
                                                                                           _resolveLinks, _user, _requiresLeader, _cancellationToken)
                                        : new CatchupAllSubscription(_subscriptionId, bus, startPosition ?? Position.Start, resolveLinks,
                                                                     user, _requiresLeader, readIndex, cancellationToken);
            }
コード例 #8
0
        public override async Task <AppendResp> Append(
            IAsyncStreamReader <AppendReq> requestStream,
            ServerCallContext context)
        {
            if (!await requestStream.MoveNext().ConfigureAwait(false))
            {
                throw new InvalidOperationException();
            }

            if (requestStream.Current.ContentCase != AppendReq.ContentOneofCase.Options)
            {
                throw new InvalidOperationException();
            }

            var options         = requestStream.Current.Options;
            var streamName      = options.StreamName;
            var expectedVersion = options.ExpectedStreamRevisionCase switch {
                AppendReq.Types.Options.ExpectedStreamRevisionOneofCase.Revision => new StreamRevision(
                    options.Revision).ToInt64(),
                AppendReq.Types.Options.ExpectedStreamRevisionOneofCase.Any => AnyStreamRevision.Any.ToInt64(),
                AppendReq.Types.Options.ExpectedStreamRevisionOneofCase.StreamExists => AnyStreamRevision.StreamExists.ToInt64(),
                AppendReq.Types.Options.ExpectedStreamRevisionOneofCase.NoStream => AnyStreamRevision.NoStream.ToInt64(),
                _ => throw new InvalidOperationException()
            };

            var user = await GetUser(_authenticationProvider, context.RequestHeaders).ConfigureAwait(false);

            var correlationId = Guid.NewGuid();             // TODO: JPB use request id?

            var events = new List <Event>();

            var size = 0;

            while (await requestStream.MoveNext().ConfigureAwait(false))
            {
                if (requestStream.Current.ContentCase != AppendReq.ContentOneofCase.ProposedMessage)
                {
                    throw new InvalidOperationException();
                }

                var proposedMessage = requestStream.Current.ProposedMessage;
                var data            = proposedMessage.Data.ToByteArray();
                size += data.Length;

                if (size > _maxAppendSize)
                {
                    throw RpcExceptions.MaxAppendSizeExceeded(_maxAppendSize);
                }

                if (!proposedMessage.Metadata.TryGetValue(Constants.Metadata.Type, out var eventType))
                {
                    throw RpcExceptions.RequiredMetadataPropertyMissing(Constants.Metadata.Type);
                }

                if (!proposedMessage.Metadata.TryGetValue(Constants.Metadata.IsJson, out var isJson))
                {
                    throw RpcExceptions.RequiredMetadataPropertyMissing(Constants.Metadata.IsJson);
                }

                events.Add(new Event(
                               Uuid.FromDto(proposedMessage.Id).ToGuid(),
                               eventType,
                               bool.Parse(isJson),
                               data,
                               proposedMessage.CustomMetadata.ToByteArray()));
            }

            var appendResponseSource = new TaskCompletionSource <AppendResp>();

            var envelope = new CallbackEnvelope(HandleWriteEventsCompleted);

            _queue.Publish(new ClientMessage.WriteEvents(
                               correlationId,
                               correlationId,
                               envelope,
                               true,
                               streamName,
                               expectedVersion,
                               events.ToArray(),
                               user));

            return(await appendResponseSource.Task.ConfigureAwait(false));

            void HandleWriteEventsCompleted(Message message)
            {
                if (message is ClientMessage.NotHandled notHandled && RpcExceptions.TryHandleNotHandled(notHandled, out var ex))
                {
                    appendResponseSource.TrySetException(ex);
                    return;
                }

                if (!(message is ClientMessage.WriteEventsCompleted completed))
                {
                    appendResponseSource.TrySetException(
                        RpcExceptions.UnknownMessage <ClientMessage.WriteEventsCompleted>(message));
                    return;
                }

                switch (completed.Result)
                {
                case OperationResult.Success:
                    var response = new AppendResp();

                    if (completed.LastEventNumber == -1)
                    {
                        response.NoStream = new AppendResp.Types.Empty();
                    }
                    else
                    {
                        response.CurrentRevision = StreamRevision.FromInt64(completed.LastEventNumber);
                    }

                    if (completed.CommitPosition == -1)
                    {
                        response.Empty = new AppendResp.Types.Empty();
                    }
                    else
                    {
                        var position = Position.FromInt64(completed.CommitPosition, completed.PreparePosition);
                        response.Position = new AppendResp.Types.Position {
                            CommitPosition  = position.CommitPosition,
                            PreparePosition = position.PreparePosition
                        };
                    }

                    appendResponseSource.TrySetResult(response);
                    return;

                case OperationResult.PrepareTimeout:
                case OperationResult.CommitTimeout:
                case OperationResult.ForwardTimeout:
                    appendResponseSource.TrySetException(RpcExceptions.Timeout());
                    return;

                case OperationResult.WrongExpectedVersion:
                    appendResponseSource.TrySetException(RpcExceptions.WrongExpectedVersion(
                                                             streamName,
                                                             expectedVersion,
                                                             completed.CurrentVersion));
                    return;

                case OperationResult.StreamDeleted:
                    appendResponseSource.TrySetException(RpcExceptions.StreamDeleted(streamName));
                    return;

                case OperationResult.InvalidTransaction:
                    appendResponseSource.TrySetException(RpcExceptions.InvalidTransaction());
                    return;

                case OperationResult.AccessDenied:
                    appendResponseSource.TrySetException(RpcExceptions.AccessDenied());
                    return;

                default:
                    appendResponseSource.TrySetException(RpcExceptions.UnknownError(completed.Result));
                    return;
                }
            }
        }
    }
コード例 #9
0
            public AllSubscriptionFiltered(IPublisher bus,
                                           Position?startPosition,
                                           bool resolveLinks,
                                           IEventFilter eventFilter,
                                           ClaimsPrincipal user,
                                           bool requiresLeader,
                                           IReadIndex readIndex,
                                           uint?maxSearchWindow,
                                           uint checkpointIntervalMultiplier,
                                           Func <Position, Task> checkpointReached,
                                           CancellationToken cancellationToken)
            {
                if (bus == null)
                {
                    throw new ArgumentNullException(nameof(bus));
                }

                if (eventFilter == null)
                {
                    throw new ArgumentNullException(nameof(eventFilter));
                }

                if (readIndex == null)
                {
                    throw new ArgumentNullException(nameof(readIndex));
                }

                if (checkpointReached == null)
                {
                    throw new ArgumentNullException(nameof(checkpointReached));
                }

                if (checkpointIntervalMultiplier == 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(checkpointIntervalMultiplier));
                }

                _subscriptionId  = Guid.NewGuid();
                _bus             = bus;
                _resolveLinks    = resolveLinks;
                _eventFilter     = eventFilter;
                _user            = user;
                _requiresLeader  = requiresLeader;
                _readIndex       = readIndex;
                _maxSearchWindow = maxSearchWindow;
                _checkpointIntervalMultiplier = checkpointIntervalMultiplier;
                _checkpointReached            = checkpointReached;
                _cancellationToken            = cancellationToken;
                _subscriptionStarted          = new TaskCompletionSource <bool>();
                _subscriptionStarted.SetResult(true);

                _inner = startPosition == Position.End
                                        ? (IStreamSubscription) new LiveStreamSubscription(_subscriptionId, _bus,
                                                                                           Position.FromInt64(_readIndex.LastIndexedPosition, _readIndex.LastIndexedPosition),
                                                                                           _resolveLinks, _eventFilter, _user, _requiresLeader, _maxSearchWindow,
                                                                                           _checkpointIntervalMultiplier, checkpointReached, _cancellationToken)
                                        : new CatchupAllSubscription(_subscriptionId, bus, startPosition ?? Position.Start, resolveLinks,
                                                                     _eventFilter, user, _requiresLeader, readIndex, _maxSearchWindow, _checkpointIntervalMultiplier, checkpointReached,
                                                                     cancellationToken);
            }
コード例 #10
0
ファイル: Streams.Delete.cs プロジェクト: matsnd/EventStore
        private async Task <Position?> DeleteInternal(string streamName, long expectedVersion,
                                                      ClaimsPrincipal user, bool hardDelete, bool requiresLeader)
        {
            var correlationId        = Guid.NewGuid();      // TODO: JPB use request id?
            var deleteResponseSource = new TaskCompletionSource <Position?>();

            var envelope = new CallbackEnvelope(HandleStreamDeletedCompleted);

            _queue.Publish(new ClientMessage.DeleteStream(
                               correlationId,
                               correlationId,
                               envelope,
                               requiresLeader,
                               streamName,
                               expectedVersion,
                               hardDelete,
                               user));

            return(await deleteResponseSource.Task.ConfigureAwait(false));

            void HandleStreamDeletedCompleted(Message message)
            {
                if (message is ClientMessage.NotHandled notHandled &&
                    RpcExceptions.TryHandleNotHandled(notHandled, out var ex))
                {
                    deleteResponseSource.TrySetException(ex);
                    return;
                }

                if (!(message is ClientMessage.DeleteStreamCompleted completed))
                {
                    deleteResponseSource.TrySetException(
                        RpcExceptions.UnknownMessage <ClientMessage.DeleteStreamCompleted>(message));
                    return;
                }

                switch (completed.Result)
                {
                case OperationResult.Success:
                    deleteResponseSource.TrySetResult(completed.CommitPosition == -1
                                                        ? default
                                                        : Position.FromInt64(completed.CommitPosition, completed.PreparePosition));

                    return;

                case OperationResult.PrepareTimeout:
                case OperationResult.CommitTimeout:
                case OperationResult.ForwardTimeout:
                    deleteResponseSource.TrySetException(RpcExceptions.Timeout());
                    return;

                case OperationResult.WrongExpectedVersion:
                    deleteResponseSource.TrySetException(RpcExceptions.WrongExpectedVersion(streamName,
                                                                                            expectedVersion));
                    return;

                case OperationResult.StreamDeleted:
                    deleteResponseSource.TrySetException(RpcExceptions.StreamDeleted(streamName));
                    return;

                case OperationResult.InvalidTransaction:
                    deleteResponseSource.TrySetException(RpcExceptions.InvalidTransaction());
                    return;

                case OperationResult.AccessDenied:
                    deleteResponseSource.TrySetException(RpcExceptions.AccessDenied());
                    return;

                default:
                    deleteResponseSource.TrySetException(RpcExceptions.UnknownError(completed.Result));
                    return;
                }
            }
        }
コード例 #11
0
ファイル: Streams.Append.cs プロジェクト: arthis/EventStore-1
        public override async Task <AppendResp> Append(
            IAsyncStreamReader <AppendReq> requestStream,
            ServerCallContext context)
        {
            if (!await requestStream.MoveNext().ConfigureAwait(false))
            {
                throw new InvalidOperationException();
            }

            if (requestStream.Current.ContentCase != AppendReq.ContentOneofCase.Options)
            {
                throw new InvalidOperationException();
            }

            var options    = requestStream.Current.Options;
            var streamName = options.StreamIdentifier;

            var expectedVersion = options.ExpectedStreamRevisionCase switch {
                AppendReq.Types.Options.ExpectedStreamRevisionOneofCase.Revision => new StreamRevision(
                    options.Revision).ToInt64(),
                AppendReq.Types.Options.ExpectedStreamRevisionOneofCase.Any => AnyStreamRevision.Any.ToInt64(),
                AppendReq.Types.Options.ExpectedStreamRevisionOneofCase.StreamExists => AnyStreamRevision.StreamExists.ToInt64(),
                AppendReq.Types.Options.ExpectedStreamRevisionOneofCase.NoStream => AnyStreamRevision.NoStream.ToInt64(),
                _ => throw new InvalidOperationException()
            };

            var requiresLeader = GetRequiresLeader(context.RequestHeaders);

            var user = context.GetHttpContext().User;
            var op   = WriteOperation.WithParameter(Plugins.Authorization.Operations.Streams.Parameters.StreamId(streamName));

            if (!await _provider.CheckAccessAsync(user, op, context.CancellationToken).ConfigureAwait(false))
            {
                throw AccessDenied();
            }

            var correlationId = Guid.NewGuid();             // TODO: JPB use request id?

            var events = new List <Event>();

            var size = 0;

            while (await requestStream.MoveNext().ConfigureAwait(false))
            {
                if (requestStream.Current.ContentCase != AppendReq.ContentOneofCase.ProposedMessage)
                {
                    throw new InvalidOperationException();
                }

                var proposedMessage = requestStream.Current.ProposedMessage;
                var data            = proposedMessage.Data.ToByteArray();
                var metadata        = proposedMessage.CustomMetadata.ToByteArray();

                if (!proposedMessage.Metadata.TryGetValue(Constants.Metadata.Type, out var eventType))
                {
                    throw RpcExceptions.RequiredMetadataPropertyMissing(Constants.Metadata.Type);
                }

                if (!proposedMessage.Metadata.TryGetValue(Constants.Metadata.ContentType, out var contentType))
                {
                    throw RpcExceptions.RequiredMetadataPropertyMissing(Constants.Metadata.ContentType);
                }

                size += Event.SizeOnDisk(eventType, data, metadata);

                if (size > _maxAppendSize)
                {
                    throw RpcExceptions.MaxAppendSizeExceeded(_maxAppendSize);
                }

                events.Add(new Event(
                               Uuid.FromDto(proposedMessage.Id).ToGuid(),
                               eventType,
                               contentType == Constants.Metadata.ContentTypes.ApplicationJson,
                               data,
                               metadata));
            }

            var appendResponseSource = new TaskCompletionSource <AppendResp>();

            var envelope = new CallbackEnvelope(HandleWriteEventsCompleted);

            _publisher.Publish(new ClientMessage.WriteEvents(
                                   correlationId,
                                   correlationId,
                                   envelope,
                                   requiresLeader,
                                   streamName,
                                   expectedVersion,
                                   events.ToArray(),
                                   user,
                                   cancellationToken: context.CancellationToken));

            return(await appendResponseSource.Task.ConfigureAwait(false));

            void HandleWriteEventsCompleted(Message message)
            {
                if (message is ClientMessage.NotHandled notHandled && RpcExceptions.TryHandleNotHandled(notHandled, out var ex))
                {
                    appendResponseSource.TrySetException(ex);
                    return;
                }

                if (!(message is ClientMessage.WriteEventsCompleted completed))
                {
                    appendResponseSource.TrySetException(
                        RpcExceptions.UnknownMessage <ClientMessage.WriteEventsCompleted>(message));
                    return;
                }

                var response = new AppendResp();

                switch (completed.Result)
                {
                case OperationResult.Success:
                    response.Success = new AppendResp.Types.Success();
                    if (completed.LastEventNumber == -1)
                    {
                        response.Success.NoStream = new Empty();
                    }
                    else
                    {
                        response.Success.CurrentRevision = StreamRevision.FromInt64(completed.LastEventNumber);
                    }

                    if (completed.CommitPosition == -1)
                    {
                        response.Success.NoPosition = new Empty();
                    }
                    else
                    {
                        var position = Position.FromInt64(completed.CommitPosition, completed.PreparePosition);
                        response.Success.Position = new AppendResp.Types.Position {
                            CommitPosition  = position.CommitPosition,
                            PreparePosition = position.PreparePosition
                        };
                    }

                    appendResponseSource.TrySetResult(response);
                    return;

                case OperationResult.PrepareTimeout:
                case OperationResult.CommitTimeout:
                case OperationResult.ForwardTimeout:
                    appendResponseSource.TrySetException(RpcExceptions.Timeout());
                    return;

                case OperationResult.WrongExpectedVersion:
                    response.WrongExpectedVersion = new AppendResp.Types.WrongExpectedVersion();

                    switch (options.ExpectedStreamRevisionCase)
                    {
                    case AppendReq.Types.Options.ExpectedStreamRevisionOneofCase.Any:
                        response.WrongExpectedVersion.ExpectedAny = new Empty();
                        response.WrongExpectedVersion.Any2060     = new Empty();
                        break;

                    case AppendReq.Types.Options.ExpectedStreamRevisionOneofCase.StreamExists:
                        response.WrongExpectedVersion.ExpectedStreamExists = new Empty();
                        response.WrongExpectedVersion.StreamExists2060     = new Empty();
                        break;

                    case AppendReq.Types.Options.ExpectedStreamRevisionOneofCase.NoStream:
                        response.WrongExpectedVersion.ExpectedNoStream     = new Empty();
                        response.WrongExpectedVersion.ExpectedRevision2060 = ulong.MaxValue;
                        break;

                    case AppendReq.Types.Options.ExpectedStreamRevisionOneofCase.Revision:
                        response.WrongExpectedVersion.ExpectedRevision =
                            StreamRevision.FromInt64(expectedVersion);
                        response.WrongExpectedVersion.ExpectedRevision2060 =
                            StreamRevision.FromInt64(expectedVersion);
                        break;
                    }

                    if (completed.CurrentVersion == -1)
                    {
                        response.WrongExpectedVersion.CurrentNoStream = new Empty();
                        response.WrongExpectedVersion.NoStream2060    = new Empty();
                    }
                    else
                    {
                        response.WrongExpectedVersion.CurrentRevision =
                            StreamRevision.FromInt64(completed.CurrentVersion);
                        response.WrongExpectedVersion.CurrentRevision2060 =
                            StreamRevision.FromInt64(completed.CurrentVersion);
                    }

                    appendResponseSource.TrySetResult(response);
                    return;

                case OperationResult.StreamDeleted:
                    appendResponseSource.TrySetException(RpcExceptions.StreamDeleted(streamName));
                    return;

                case OperationResult.InvalidTransaction:
                    appendResponseSource.TrySetException(RpcExceptions.InvalidTransaction());
                    return;

                case OperationResult.AccessDenied:
                    appendResponseSource.TrySetException(RpcExceptions.AccessDenied());
                    return;

                default:
                    appendResponseSource.TrySetException(RpcExceptions.UnknownError(completed.Result));
                    return;
                }
            }
        }
    }
コード例 #12
0
            private void ReadPage(Position startPosition, ulong readCount = 0)
            {
                var correlationId = Guid.NewGuid();

                var(commitPosition, preparePosition) = startPosition.ToInt64();

                _bus.Publish(new ClientMessage.FilteredReadAllEventsForward(
                                 correlationId, correlationId, new ContinuationEnvelope(OnMessage, _semaphore, _cancellationToken),
                                 commitPosition, preparePosition, (int)Math.Min(ReadBatchSize, _maxCount), _resolveLinks,
                                 _requiresLeader, (int)_maxSearchWindow, null, _eventFilter, _user, expires: _deadline));

                async Task OnMessage(Message message, CancellationToken ct)
                {
                    if (message is ClientMessage.NotHandled notHandled &&
                        RpcExceptions.TryHandleNotHandled(notHandled, out var ex))
                    {
                        _channel.Writer.TryComplete(ex);
                        return;
                    }

                    if (!(message is ClientMessage.FilteredReadAllEventsForwardCompleted completed))
                    {
                        _channel.Writer.TryComplete(
                            RpcExceptions.UnknownMessage <ClientMessage.FilteredReadAllEventsForwardCompleted>(message));
                        return;
                    }

                    switch (completed.Result)
                    {
                    case FilteredReadAllResult.Success:
                        foreach (var @event in completed.Events)
                        {
                            if (readCount >= _maxCount)
                            {
                                _channel.Writer.TryComplete();
                                return;
                            }
                            await _channel.Writer.WriteAsync(new ReadResp {
                                Event = ConvertToReadEvent(_uuidOption, @event)
                            }, _cancellationToken).ConfigureAwait(false);

                            readCount++;
                        }

                        if (completed.IsEndOfStream)
                        {
                            _channel.Writer.TryComplete();
                            return;
                        }

                        ReadPage(Position.FromInt64(
                                     completed.NextPos.CommitPosition,
                                     completed.NextPos.PreparePosition), readCount);
                        return;

                    case FilteredReadAllResult.AccessDenied:
                        _channel.Writer.TryComplete(RpcExceptions.AccessDenied());
                        return;

                    default:
                        _channel.Writer.TryComplete(RpcExceptions.UnknownError(completed.Result));
                        return;
                    }
                }
            }
コード例 #13
0
            private void GoLive(Position startPosition)
            {
                var liveEvents            = Channel.CreateBounded <ResolvedEvent>(BoundedChannelOptions);
                var caughtUpSource        = new TaskCompletionSource <Position>();
                var liveMessagesCancelled = 0;

                Log.Information(
                    "Live subscription {subscriptionId} to $all running from {position}...",
                    _subscriptionId, startPosition);

                _bus.Publish(new ClientMessage.SubscribeToStream(Guid.NewGuid(), _subscriptionId,
                                                                 new ContinuationEnvelope(OnSubscriptionMessage, _semaphore, _cancellationToken), _subscriptionId,
                                                                 string.Empty, _resolveLinks, _user));

                Task.Factory.StartNew(PumpLiveMessages, _cancellationToken);

                async Task PumpLiveMessages()
                {
                    var position = await caughtUpSource.Task.ConfigureAwait(false);

                    await _channel.Writer.WriteAsync(new ReadResp {
                        Checkpoint = new ReadResp.Types.Checkpoint {
                            CommitPosition  = position.CommitPosition,
                            PreparePosition = position.PreparePosition
                        }
                    }, _cancellationToken).ConfigureAwait(false);

                    await foreach (var @event in liveEvents.Reader.ReadAllAsync(_cancellationToken)
                                   .ConfigureAwait(false))
                    {
                        await _channel.Writer.WriteAsync(new ReadResp {
                            Event = ConvertToReadEvent(_uuidOption, @event)
                        }, _cancellationToken).ConfigureAwait(false);
                    }
                }

                async Task OnSubscriptionMessage(Message message, CancellationToken cancellationToken)
                {
                    if (message is ClientMessage.NotHandled notHandled &&
                        RpcExceptions.TryHandleNotHandled(notHandled, out var ex))
                    {
                        Fail(ex);
                        return;
                    }

                    switch (message)
                    {
                    case ClientMessage.SubscriptionConfirmation confirmed:
                        await ConfirmSubscription().ConfigureAwait(false);

                        var caughtUp = new TFPos(confirmed.LastIndexedPosition,
                                                 confirmed.LastIndexedPosition);
                        Log.Verbose(
                            "Live subscription {subscriptionId} to $all confirmed at {position}.",
                            _subscriptionId, caughtUp);

                        if (startPosition != Position.End)
                        {
                            ReadHistoricalEvents(startPosition);
                        }
                        else
                        {
                            NotifyCaughtUp(startPosition);
                        }

                        void NotifyCaughtUp(Position position)
                        {
                            Log.Verbose(
                                "Live subscription {subscriptionId} to $all caught up at {position} because the end of stream was reached.",
                                _subscriptionId, position);
                            caughtUpSource.TrySetResult(position);
                        }

                        async Task OnHistoricalEventsMessage(Message message, CancellationToken ct)
                        {
                            if (message is ClientMessage.NotHandled notHandled &&
                                RpcExceptions.TryHandleNotHandled(notHandled, out var ex))
                            {
                                Fail(ex);
                                return;
                            }

                            if (!(message is ClientMessage.ReadAllEventsForwardCompleted completed))
                            {
                                Fail(
                                    RpcExceptions.UnknownMessage <ClientMessage.ReadAllEventsForwardCompleted>(
                                        message));
                                return;
                            }

                            switch (completed.Result)
                            {
                            case ReadAllResult.Success:
                                if (completed.Events.Length == 0 && completed.IsEndOfStream)
                                {
                                    NotifyCaughtUp(Position.FromInt64(completed.CurrentPos.CommitPosition,
                                                                      completed.CurrentPos.PreparePosition));
                                    return;
                                }

                                foreach (var @event in completed.Events)
                                {
                                    var position = @event.OriginalPosition.Value;

                                    if (position > caughtUp)
                                    {
                                        NotifyCaughtUp(Position.FromInt64(position.CommitPosition,
                                                                          position.PreparePosition));
                                        return;
                                    }

                                    await _channel.Writer.WriteAsync(new ReadResp {
                                        Event = ConvertToReadEvent(_uuidOption, @event)
                                    }, _cancellationToken)
                                    .ConfigureAwait(false);
                                }

                                ReadHistoricalEvents(Position.FromInt64(
                                                         completed.NextPos.CommitPosition,
                                                         completed.NextPos.PreparePosition));
                                return;

                            case ReadAllResult.AccessDenied:
                                Fail(RpcExceptions.AccessDenied());
                                return;

                            default:
                                Fail(RpcExceptions.UnknownError(completed.Result));
                                return;
                            }
                        }

                        void ReadHistoricalEvents(Position fromPosition)
                        {
                            if (fromPosition == Position.End)
                            {
                                throw new ArgumentOutOfRangeException(nameof(fromPosition));
                            }

                            Log.Verbose(
                                "Live subscription {subscriptionId} to $all loading any missed events starting from {position}.",
                                _subscriptionId, fromPosition);

                            ReadPage(fromPosition, OnHistoricalEventsMessage);
                        }

                        return;

                    case ClientMessage.SubscriptionDropped dropped:
                        switch (dropped.Reason)
                        {
                        case SubscriptionDropReason.AccessDenied:
                            Fail(RpcExceptions.AccessDenied());
                            return;

                        case SubscriptionDropReason.Unsubscribed:
                            return;

                        default:
                            Fail(RpcExceptions.UnknownError(dropped.Reason));
                            return;
                        }

                    case ClientMessage.StreamEventAppeared appeared: {
                        if (liveMessagesCancelled == 1)
                        {
                            return;
                        }

                        using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(1));
                        try {
                            Log.Verbose(
                                "Live subscription {subscriptionId} to $all enqueuing live message {position}.",
                                _subscriptionId, appeared.Event.OriginalPosition);

                            await liveEvents.Writer.WriteAsync(appeared.Event, cts.Token)
                            .ConfigureAwait(false);
                        } catch (Exception e) {
                            if (Interlocked.Exchange(ref liveMessagesCancelled, 1) != 0)
                            {
                                return;
                            }

                            Log.Verbose(
                                e,
                                "Live subscription {subscriptionId} to $all timed out at {position}; unsubscribing...",
                                _subscriptionId, appeared.Event.OriginalPosition.GetValueOrDefault());

                            Unsubscribe();

                            liveEvents.Writer.Complete();

                            CatchUp(Position.FromInt64(appeared.Event.OriginalPosition.Value.CommitPosition,
                                                       appeared.Event.OriginalPosition.Value.PreparePosition));
                        }

                        return;
                    }

                    default:
                        Fail(
                            RpcExceptions.UnknownMessage <ClientMessage.SubscriptionConfirmation>(message));
                        return;
                    }
                }

                void Fail(Exception exception)
                {
                    this.Fail(exception);
                    caughtUpSource.TrySetException(exception);
                }
            }
コード例 #14
0
            private void CatchUp(Position startPosition)
            {
                Log.Information(
                    "Catch-up subscription {subscriptionId} to $all@{position} running...",
                    _subscriptionId, startPosition);

                ReadPage(startPosition, OnMessage);

                async Task OnMessage(Message message, CancellationToken ct)
                {
                    if (message is ClientMessage.NotHandled notHandled &&
                        RpcExceptions.TryHandleNotHandled(notHandled, out var ex))
                    {
                        Fail(ex);
                        return;
                    }

                    if (!(message is ClientMessage.ReadAllEventsForwardCompleted completed))
                    {
                        Fail(
                            RpcExceptions.UnknownMessage <ClientMessage.ReadAllEventsForwardCompleted>(message));
                        return;
                    }

                    switch (completed.Result)
                    {
                    case ReadAllResult.Success:
                        await ConfirmSubscription().ConfigureAwait(false);

                        var position = Position.FromInt64(completed.CurrentPos.CommitPosition,
                                                          completed.CurrentPos.PreparePosition);

                        foreach (var @event in completed.Events)
                        {
                            position = Position.FromInt64(
                                @event.OriginalPosition.Value.CommitPosition,
                                @event.OriginalPosition.Value.PreparePosition);

                            Log.Verbose(
                                "Catch-up subscription {subscriptionId} to $all received event {position}.",
                                _subscriptionId, position);

                            await _channel.Writer.WriteAsync(new ReadResp {
                                Event = ConvertToReadEvent(_uuidOption, @event)
                            }, ct).ConfigureAwait(false);
                        }

                        var nextPosition = Position.FromInt64(completed.NextPos.CommitPosition,
                                                              completed.NextPos.PreparePosition);

                        if (completed.IsEndOfStream)
                        {
                            GoLive(nextPosition);
                            return;
                        }

                        ReadPage(nextPosition, OnMessage);
                        return;

                    case ReadAllResult.AccessDenied:
                        Fail(RpcExceptions.AccessDenied());
                        return;

                    default:
                        Fail(RpcExceptions.UnknownError(completed.Result));
                        return;
                    }
                }
            }
コード例 #15
0
            public AllSubscriptionFiltered(IPublisher bus,
                                           Position?startPosition,
                                           bool resolveLinks,
                                           IEventFilter eventFilter,
                                           ClaimsPrincipal user,
                                           bool requiresLeader,
                                           IReadIndex readIndex,
                                           uint?maxSearchWindow,
                                           uint checkpointIntervalMultiplier,
                                           Func <Position, Task> checkpointReached,
                                           CancellationToken cancellationToken)
            {
                if (bus == null)
                {
                    throw new ArgumentNullException(nameof(bus));
                }

                if (eventFilter == null)
                {
                    throw new ArgumentNullException(nameof(eventFilter));
                }

                if (readIndex == null)
                {
                    throw new ArgumentNullException(nameof(readIndex));
                }

                if (checkpointReached == null)
                {
                    throw new ArgumentNullException(nameof(checkpointReached));
                }

                if (checkpointIntervalMultiplier == 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(checkpointIntervalMultiplier));
                }

                _subscriptionId      = Guid.NewGuid();
                _bus                 = bus;
                _resolveLinks        = resolveLinks;
                _eventFilter         = eventFilter;
                _user                = user;
                _requiresLeader      = requiresLeader;
                _maxSearchWindow     = maxSearchWindow ?? ReadBatchSize;
                _checkpointReached   = checkpointReached;
                _cancellationToken   = cancellationToken;
                _subscriptionStarted = new TaskCompletionSource <bool>();
                _channel             = Channel.CreateBounded <(ResolvedEvent?, Position?)>(BoundedChannelOptions);
                _checkpointInterval  = checkpointIntervalMultiplier * _maxSearchWindow;
                _semaphore           = new SemaphoreSlim(1, 1);
                _lastCheckpoint      = Position.Start;

                SubscriptionId = _subscriptionId.ToString();

                var lastIndexedPosition    = readIndex.LastIndexedPosition;
                var startPositionExclusive = startPosition == Position.End
                                        ? Position.FromInt64(lastIndexedPosition, lastIndexedPosition)
                                        : startPosition ?? Position.Start;

                _startPositionExclusive = new TFPos((long)startPositionExclusive.CommitPosition,
                                                    (long)startPositionExclusive.PreparePosition);

                Subscribe(startPositionExclusive, startPosition != Position.End);
            }
コード例 #16
0
            private void CatchUp(Position startPosition)
            {
                Log.Information(
                    "Catch-up subscription {subscriptionId} to $all:{eventFilter}@{position} running...",
                    _subscriptionId, _eventFilter, startPosition);

                ReadPage(startPosition, OnMessage);

                async Task OnMessage(Message message, CancellationToken ct)
                {
                    if (message is ClientMessage.NotHandled notHandled &&
                        RpcExceptions.TryHandleNotHandled(notHandled, out var ex))
                    {
                        Fail(ex);
                        return;
                    }

                    if (!(message is ClientMessage.FilteredReadAllEventsForwardCompleted completed))
                    {
                        Fail(RpcExceptions.UnknownMessage <ClientMessage.FilteredReadAllEventsForwardCompleted>(
                                 message));
                        return;
                    }

                    switch (completed.Result)
                    {
                    case FilteredReadAllResult.Success:
                        ConfirmSubscription();

                        var position = Position.FromInt64(completed.CurrentPos.CommitPosition,
                                                          completed.CurrentPos.PreparePosition);
                        foreach (var @event in completed.Events)
                        {
                            position = Position.FromInt64(
                                @event.OriginalPosition.Value.CommitPosition,
                                @event.OriginalPosition.Value.PreparePosition);

                            Log.Verbose(
                                "Catch-up subscription {subscriptionId} to $all:{eventFilter} received event {position}.",
                                _subscriptionId, _eventFilter, position);

                            await _channel.Writer.WriteAsync((@event, new Position?()), ct).ConfigureAwait(false);
                        }

                        _checkpointIntervalCounter += completed.ConsideredEventsCount;
                        Log.Verbose(
                            "Catch-up subscription {subscriptionId} to $all:{eventFilter} considered {consideredEventsCount}, interval: {checkpointInterval}, counter: {checkpointIntervalCounter}.",
                            _subscriptionId, _eventFilter, completed.ConsideredEventsCount, _checkpointInterval,
                            _checkpointIntervalCounter);

                        var nextPosition = Position.FromInt64(completed.NextPos.CommitPosition,
                                                              completed.NextPos.PreparePosition);

                        if (completed.IsEndOfStream)
                        {
                            GoLive(nextPosition);
                            return;
                        }

                        if (_checkpointIntervalCounter >= _checkpointInterval)
                        {
                            _checkpointIntervalCounter %= _checkpointInterval;
                            Log.Verbose(
                                "Catch-up subscription {subscriptionId} to $all:{eventFilter} reached checkpoint at {position}, interval: {checkpointInterval}, counter: {checkpointIntervalCounter}.",
                                _subscriptionId, _eventFilter, nextPosition, _checkpointInterval,
                                _checkpointIntervalCounter);

                            await _channel.Writer.WriteAsync((new ResolvedEvent?(), position), ct)
                            .ConfigureAwait(false);
                        }

                        ReadPage(nextPosition, OnMessage);

                        return;

                    case FilteredReadAllResult.AccessDenied:
                        Fail(RpcExceptions.AccessDenied());
                        return;

                    default:
                        Fail(RpcExceptions.UnknownError(completed.Result));
                        return;
                    }
                }
            }