public override async Task <Empty> MergeIndexes(Empty request, ServerCallContext context)
        {
            var mergeResultSource = new TaskCompletionSource <string>();

            var user = context.GetHttpContext().User;

            if (!await _authorizationProvider.CheckAccessAsync(user, MergeIndexesOperation, context.CancellationToken)
                .ConfigureAwait(false))
            {
                throw RpcExceptions.AccessDenied();
            }

            var correlationId = Guid.NewGuid();

            _publisher.Publish(new ClientMessage.MergeIndexes(new CallbackEnvelope(OnMessage), correlationId, user));

            await mergeResultSource.Task.ConfigureAwait(false);

            return(EmptyResult);

            void OnMessage(Message message)
            {
                var completed = message as ClientMessage.MergeIndexesResponse;

                if (completed is null)
                {
                    mergeResultSource.TrySetException(
                        RpcExceptions.UnknownMessage <ClientMessage.MergeIndexesResponse>(message));
                }
                else
                {
                    mergeResultSource.SetResult(completed.CorrelationId.ToString());
                }
            }
        }
        public override async Task <DeleteResp> Delete(DeleteReq request, ServerCallContext context)
        {
            var options = request.Options;

            var user = context.GetHttpContext().User;

            if (!await _authorizationProvider.CheckAccessAsync(user, DeleteOperation, context.CancellationToken).ConfigureAwait(false))
            {
                throw RpcExceptions.AccessDenied();
            }
            var deleteSource = new TaskCompletionSource <bool>();

            var envelope = new CallbackEnvelope(OnMessage);

            _publisher.Publish(new UserManagementMessage.Delete(envelope, user, options.LoginName));

            await deleteSource.Task.ConfigureAwait(false);

            return(new DeleteResp());

            void OnMessage(Message message)
            {
                if (HandleErrors(options.LoginName, message, deleteSource))
                {
                    return;
                }

                deleteSource.TrySetResult(true);
            }
        }
Beispiel #3
0
            private void ReadPage(Position startPosition, ulong readCount = 0)
            {
                var correlationId = Guid.NewGuid();

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

                _bus.Publish(new ClientMessage.FilteredReadAllEventsBackward(
                                 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.FilteredReadAllEventsBackwardCompleted completed))
                    {
                        _channel.Writer.TryComplete(
                            RpcExceptions.UnknownMessage <ClientMessage.FilteredReadAllEventsBackwardCompleted>(message));
                        return;
                    }

                    switch (completed.Result)
                    {
                    case FilteredReadAllResult.Success:
                        var nextPosition = completed.NextPos;

                        foreach (var @event in completed.Events)
                        {
                            if (readCount >= _maxCount)
                            {
                                await _channel.Writer.WriteAsync(new ReadResp {
                                    AllStreamPosition = new () {
                                        NextPosition = new() {
                                            CommitPosition  = (ulong)nextPosition.CommitPosition,
                                            PreparePosition = (ulong)nextPosition.PreparePosition
                                        },
                                        LastPosition = new() {
                                            CommitPosition  = (ulong)completed.CurrentPos.CommitPosition,
                                            PreparePosition = (ulong)completed.CurrentPos.PreparePosition
                                        }
                                    }
                                }, ct).ConfigureAwait(false);

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

                            nextPosition = @event.OriginalPosition ?? TFPos.Invalid;
                            readCount++;
                        }
Beispiel #4
0
        public override async Task Details(DetailsReq request, IServerStreamWriter <DetailsResp> responseStream,
                                           ServerCallContext context)
        {
            var options = request.Options;

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

            var detailsSource = new TaskCompletionSource <UserManagementMessage.UserData[]>();

            var envelope = new CallbackEnvelope(OnMessage);

            _queue.Publish(string.IsNullOrWhiteSpace(options?.LoginName)
                                ? (Message) new UserManagementMessage.GetAll(envelope, user)
                                : new UserManagementMessage.Get(envelope, user, options.LoginName));

            var details = await detailsSource.Task.ConfigureAwait(false);

            foreach (var detail in details)
            {
                await responseStream.WriteAsync(new DetailsResp {
                    UserDetails = new DetailsResp.Types.UserDetails {
                        Disabled    = detail.Disabled,
                        Groups      = { detail.Groups },
                        FullName    = detail.FullName,
                        LoginName   = detail.LoginName,
                        LastUpdated = detail.DateLastUpdated.HasValue
                                                        ? new DetailsResp.Types.UserDetails.Types.DateTime
                        {
                            TicksSinceEpoch = detail.DateLastUpdated.Value.UtcDateTime.ToTicksSinceEpoch()
                        }
                                                        : null
                    }
                }).ConfigureAwait(false);
            }

            void OnMessage(Message message)
            {
                if (HandleErrors(options?.LoginName, message, detailsSource))
                {
                    return;
                }

                switch (message)
                {
                case UserManagementMessage.UserDetailsResult userDetails:
                    detailsSource.TrySetResult(new[] { userDetails.Data });
                    break;

                case UserManagementMessage.AllUserDetailsResult allUserDetails:
                    detailsSource.TrySetResult(allUserDetails.Data);
                    break;

                default:
                    detailsSource.TrySetException(RpcExceptions.UnknownError(1));
                    break;
                }
            }
        }
        public override async Task <DeleteResp> Delete(DeleteReq request, ServerCallContext context)
        {
            var createPersistentSubscriptionSource = new TaskCompletionSource <DeleteResp>();
            var correlationId = Guid.NewGuid();

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

            _queue.Publish(new ClientMessage.DeletePersistentSubscription(
                               correlationId,
                               correlationId,
                               new CallbackEnvelope(HandleDeletePersistentSubscriptionCompleted),
                               request.Options.StreamName,
                               request.Options.GroupName,
                               user));

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

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

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

                switch (completed.Result)
                {
                case DeletePersistentSubscriptionResult.Success:
                    createPersistentSubscriptionSource.TrySetResult(new DeleteResp());
                    return;

                case DeletePersistentSubscriptionResult.Fail:
                    createPersistentSubscriptionSource.TrySetException(RpcExceptions.PersistentSubscriptionFailed(request.Options.StreamName, request.Options.GroupName, completed.Reason));
                    return;

                case DeletePersistentSubscriptionResult.DoesNotExist:
                    createPersistentSubscriptionSource.TrySetException(RpcExceptions.PersistentSubscriptionDoesNotExist(request.Options.StreamName, request.Options.GroupName));
                    return;

                case DeletePersistentSubscriptionResult.AccessDenied:
                    createPersistentSubscriptionSource.TrySetException(RpcExceptions.AccessDenied());
                    return;

                default:
                    createPersistentSubscriptionSource.TrySetException(RpcExceptions.UnknownError(completed.Result));
                    return;
                }
            }
        }
Beispiel #6
0
        public override async Task <ClusterInfo> Read(Empty request, ServerCallContext context)
        {
            var user = context.GetHttpContext().User;

            if (!await _authorizationProvider.CheckAccessAsync(user, ReadOperation, context.CancellationToken).ConfigureAwait(false))
            {
                throw RpcExceptions.AccessDenied();
            }
            var tcs = new TaskCompletionSource <ClusterInfo>();

            _bus.Publish(new GossipMessage.ClientGossip(new CallbackEnvelope(msg => GossipResponse(msg, tcs))));;
            return(await tcs.Task.ConfigureAwait(false));
        }
        public override async Task <Empty> ResignNode(Empty request, ServerCallContext context)
        {
            var user = context.GetHttpContext().User;

            if (!await _authorizationProvider.CheckAccessAsync(user, ResignOperation, context.CancellationToken)
                .ConfigureAwait(false))
            {
                throw RpcExceptions.AccessDenied();
            }

            _publisher.Publish(new ClientMessage.ResignNode());
            return(EmptyResult);
        }
Beispiel #8
0
            private void ReadPage(StreamRevision startRevision, ulong readCount = 0)
            {
                Guid correlationId = Guid.NewGuid();

                _bus.Publish(new ClientMessage.ReadStreamEventsForward(
                                 correlationId, correlationId, new ContinuationEnvelope(OnMessage, _semaphore, _cancellationToken),
                                 _streamName, startRevision.ToInt64(), (int)Math.Min(ReadBatchSize, _maxCount), _resolveLinks,
                                 _requiresLeader, null, _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.ReadStreamEventsForwardCompleted completed))
                    {
                        _channel.Writer.TryComplete(
                            RpcExceptions.UnknownMessage <ClientMessage.ReadStreamEventsForwardCompleted>(message));
                        return;
                    }

                    switch (completed.Result)
                    {
                    case ReadStreamResult.Success:
                        var nextStreamPosition = (ulong)completed.NextEventNumber;

                        foreach (var @event in completed.Events)
                        {
                            if (readCount >= _maxCount)
                            {
                                await _channel.Writer.WriteAsync(new ReadResp {
                                    StreamPosition = new() {
                                        LastStreamPosition = (ulong)completed.LastEventNumber,
                                        NextStreamPosition = nextStreamPosition
                                    }
                                }, ct).ConfigureAwait(false);

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

                            nextStreamPosition = (ulong)@event.OriginalEvent.EventNumber;
                            readCount++;
                        }
Beispiel #9
0
        public override async Task <Empty> RestartSubsystem(Empty request, ServerCallContext context)
        {
            var restartSubsystemSource = new TaskCompletionSource <Empty>();

            var user = context.GetHttpContext().User;

            if (!await _authorizationProvider.CheckAccessAsync(user,
                                                               RestartOperation, context.CancellationToken).ConfigureAwait(false))
            {
                throw RpcExceptions.AccessDenied();
            }

            _publisher.Publish(new SubscriptionMessage.PersistentSubscriptionsRestart(
                                   new CallbackEnvelope(HandleRestartSubsystemCompleted)));
            return(await restartSubsystemSource.Task.ConfigureAwait(false));

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

                switch (message)
                {
                case SubscriptionMessage.PersistentSubscriptionsRestarting _:
                    restartSubsystemSource.TrySetResult(new Empty());
                    return;

                case SubscriptionMessage.InvalidPersistentSubscriptionsRestart fail:
                    restartSubsystemSource.TrySetException(
                        RpcExceptions.PersistentSubscriptionFailed("", "",
                                                                   $"Persistent Subscriptions cannot be restarted as it is in the wrong state."));
                    return;

                default:
                    restartSubsystemSource.TrySetException(
                        RpcExceptions.UnknownMessage <SubscriptionMessage.PersistentSubscriptionsRestarting>(message));
                    return;
                }
            }
        }
Beispiel #10
0
        private static bool HandleErrors <T>(string loginName, Message message, TaskCompletionSource <T> source)
        {
            if (!(message is ResponseMessage response))
            {
                source.TrySetException(
                    RpcExceptions.UnknownMessage <ResponseMessage>(message));
                return(true);
            }

            if (response.Success)
            {
                return(false);
            }
            source.TrySetException(response.Error switch {
                Error.Unauthorized => RpcExceptions.AccessDenied(),
                Error.NotFound => RpcExceptions.LoginNotFound(loginName),
                Error.Conflict => RpcExceptions.LoginConflict(loginName),
                Error.TryAgain => RpcExceptions.LoginTryAgain(loginName),
                _ => RpcExceptions.UnknownError(response.Error)
            });
Beispiel #11
0
        public override async Task <TombstoneResp> Tombstone(TombstoneReq request, ServerCallContext context)
        {
            var options    = request.Options;
            var streamName = options.StreamIdentifier;

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

            var requiresLeader = GetRequiresLeader(context.RequestHeaders);

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

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

            var position = await DeleteInternal(streamName, expectedVersion, user, true, requiresLeader,
                                                context.CancellationToken).ConfigureAwait(false);

            return(position.HasValue
                                ? new TombstoneResp {
                Position = new TombstoneResp.Types.Position {
                    CommitPosition = position.Value.CommitPosition,
                    PreparePosition = position.Value.PreparePosition
                }
            }
                                : new TombstoneResp {
                NoPosition = new Empty()
            });
        }
        public override async Task <Empty> RestartPersistentSubscriptions(Empty request, ServerCallContext context)
        {
            var restart  = new TaskCompletionSource <bool>();
            var envelope = new CallbackEnvelope(OnMessage);

            var user = context.GetHttpContext().User;

            if (!await _authorizationProvider.CheckAccessAsync(user, RestartPersistentSubscriptionsOperation,
                                                               context.CancellationToken)
                .ConfigureAwait(false))
            {
                throw RpcExceptions.AccessDenied();
            }

            _publisher.Publish(new SubscriptionMessage.PersistentSubscriptionsRestart(envelope));

            await restart.Task.ConfigureAwait(false);

            return(new Empty());

            void OnMessage(Message message)
            {
                switch (message)
                {
                case SubscriptionMessage.PersistentSubscriptionsRestarting _:
                    restart.TrySetResult(true);
                    break;

                case SubscriptionMessage.InvalidPersistentSubscriptionsRestart _:
                    restart.TrySetResult(true);
                    break;

                default:
                    restart.TrySetException(
                        RpcExceptions
                        .UnknownMessage <SubscriptionMessage.PersistentSubscriptionsRestarting>(message));
                    break;
                }
            }
        }
        public override async Task <ChangePasswordResp> ChangePassword(ChangePasswordReq request,
                                                                       ServerCallContext context)
        {
            var options = request.Options;

            var user = context.GetHttpContext().User;
            var changePasswordOperation = ChangePasswordOperation;

            if (user?.Identity?.Name != null)
            {
                changePasswordOperation =
                    changePasswordOperation.WithParameter(
                        Plugins.Authorization.Operations.Users.Parameters.User(user.Identity.Name));
            }
            if (!await _authorizationProvider.CheckAccessAsync(user, changePasswordOperation, context.CancellationToken).ConfigureAwait(false))
            {
                throw RpcExceptions.AccessDenied();
            }
            var changePasswordSource = new TaskCompletionSource <bool>();

            var envelope = new CallbackEnvelope(OnMessage);

            _publisher.Publish(new UserManagementMessage.ChangePassword(envelope, user, options.LoginName,
                                                                        options.CurrentPassword,
                                                                        options.NewPassword));

            await changePasswordSource.Task.ConfigureAwait(false);

            return(new ChangePasswordResp());

            void OnMessage(Message message)
            {
                if (HandleErrors(options.LoginName, message, changePasswordSource))
                {
                    return;
                }

                changePasswordSource.TrySetResult(true);
            }
        }
        public override async Task <ScavengeResp> StopScavenge(StopScavengeReq request, ServerCallContext context)
        {
            var scavengeResultSource = new TaskCompletionSource <(string, ScavengeResp.Types.ScavengeResult)>();

            var user = context.GetHttpContext().User;

            if (!await _authorizationProvider.CheckAccessAsync(user, StopOperation, context.CancellationToken).ConfigureAwait(false))
            {
                throw RpcExceptions.AccessDenied();
            }
            _publisher.Publish(new ClientMessage.StopDatabaseScavenge(new CallbackEnvelope(OnMessage), Guid.NewGuid(), user,
                                                                      request.Options.ScavengeId));

            var(scavengeId, scavengeResult) = await scavengeResultSource.Task.ConfigureAwait(false);

            return(new ScavengeResp {
                ScavengeId = scavengeId,
                ScavengeResult = scavengeResult
            });

            void OnMessage(Message message) => HandleScavengeDatabaseResponse(message, scavengeResultSource);
        }
Beispiel #15
0
            private async Task Receive(ChannelWriter <BatchAppendResp> writer, ClaimsPrincipal user, bool requiresLeader,
                                       CancellationToken cancellationToken)
            {
                var pendingWrites = new ConcurrentDictionary <Guid, ClientWriteRequest>();

                try {
                    await foreach (var request in _requestStream.ReadAllAsync(cancellationToken))
                    {
                        try {
                            var correlationId = Uuid.FromDto(request.CorrelationId).ToGuid();

                            if (request.Options != null)
                            {
                                TimeSpan timeout = Min(GetRequestedTimeout(request.Options), _writeTimeout);

                                if (!await _authorizationProvider.CheckAccessAsync(user, WriteOperation.WithParameter(
                                                                                       Plugins.Authorization.Operations.Streams.Parameters.StreamId(
                                                                                           request.Options.StreamIdentifier)), cancellationToken).ConfigureAwait(false))
                                {
                                    await writer.WriteAsync(new BatchAppendResp {
                                        CorrelationId    = request.CorrelationId,
                                        StreamIdentifier = request.Options.StreamIdentifier,
                                        Error            = Status.AccessDenied
                                    }, cancellationToken).ConfigureAwait(false);

                                    continue;
                                }

                                if (request.Options.StreamIdentifier == null)
                                {
                                    await writer.WriteAsync(new BatchAppendResp {
                                        CorrelationId    = request.CorrelationId,
                                        StreamIdentifier = request.Options.StreamIdentifier,
                                        Error            = Status.BadRequest(
                                            $"Required field {nameof(request.Options.StreamIdentifier)} not set.")
                                    }, cancellationToken).ConfigureAwait(false);

                                    continue;
                                }

                                if (Max(timeout, TimeSpan.Zero) == TimeSpan.Zero)
                                {
                                    await writer.WriteAsync(new BatchAppendResp {
                                        CorrelationId    = request.CorrelationId,
                                        StreamIdentifier = request.Options.StreamIdentifier,
                                        Error            = Status.Timeout
                                    }, cancellationToken).ConfigureAwait(false);

                                    continue;
                                }

                                pendingWrites.AddOrUpdate(correlationId,
                                                          c => FromOptions(c, request.Options, timeout, cancellationToken),
                                                          (_, writeRequest) => writeRequest);
                            }

                            if (!pendingWrites.TryGetValue(correlationId, out var clientWriteRequest))
                            {
                                continue;
                            }

                            clientWriteRequest.AddEvents(request.ProposedMessages.Select(FromProposedMessage));

                            if (clientWriteRequest.Size > _maxAppendSize)
                            {
                                pendingWrites.TryRemove(correlationId, out _);
                                await writer.WriteAsync(new BatchAppendResp {
                                    CorrelationId    = request.CorrelationId,
                                    StreamIdentifier = clientWriteRequest.StreamId,
                                    Error            = Status.MaximumAppendSizeExceeded((uint)_maxAppendSize)
                                }, cancellationToken).ConfigureAwait(false);
                            }

                            if (!request.IsFinal)
                            {
                                continue;
                            }

                            if (!pendingWrites.TryRemove(correlationId, out _))
                            {
                                continue;
                            }

                            Interlocked.Increment(ref _pending);

                            _publisher.Publish(ToInternalMessage(clientWriteRequest, new CallbackEnvelope(message => {
                                try {
                                    writer.TryWrite(ConvertMessage(message));
                                } catch (Exception ex) {
                                    writer.TryComplete(ex);
                                }
                            }), requiresLeader, user, cancellationToken));

                            BatchAppendResp ConvertMessage(Message message)
                            {
                                var batchAppendResp = message switch {
                                    ClientMessage.NotHandled notHandled => new BatchAppendResp {
                                        Error = new Status {
                                            Details = Any.Pack(new Empty()),
                                            Message = (notHandled.Reason, notHandled.AdditionalInfo) switch {
                                                (NotHandledReason.NotReady, _) => "Server Is Not Ready",
                                                (NotHandledReason.TooBusy, _) => "Server Is Busy",
                                                (NotHandledReason.NotLeader or NotHandledReason.IsReadOnly,
                                                 LeaderInfo leaderInfo) =>
                                                throw RpcExceptions.LeaderInfo(leaderInfo.HttpAddress,
                                                                               leaderInfo.HttpPort),
                                                      (NotHandledReason.NotLeader or NotHandledReason.IsReadOnly, _) =>
                                                      "No leader info available in response",
                                                      _ => $"Unknown {nameof(NotHandledReason)} ({(int)notHandled.Reason})"
                                            }
                                        }
                                    },
                                    ClientMessage.WriteEventsCompleted completed => completed.Result switch {
                                        OperationResult.Success => new BatchAppendResp {
                                            Success = BatchAppendResp.Types.Success.Completed(completed.CommitPosition,
                                                                                              completed.PreparePosition, completed.LastEventNumber),
                                        },
                                        OperationResult.WrongExpectedVersion => new BatchAppendResp {
                                            Error = Status.WrongExpectedVersion(
                                                StreamRevision.FromInt64(completed.CurrentVersion),
                                                clientWriteRequest.ExpectedVersion)
                                        },
                                        OperationResult.AccessDenied => new BatchAppendResp
                                        {
                                            Error = Status.AccessDenied
                                        },
                                        OperationResult.StreamDeleted => new BatchAppendResp {
                                            Error = Status.StreamDeleted(clientWriteRequest.StreamId)
                                        },
                                        OperationResult.CommitTimeout or
                                        OperationResult.ForwardTimeout or
                                        OperationResult.PrepareTimeout => new BatchAppendResp
                                        {
                                            Error = Status.Timeout
                                        },
                                        _ => new BatchAppendResp {
                                            Error = Status.Unknown
                                        }
                                    },
                                    _ => new BatchAppendResp {
                                        Error = new Status {
                                            Details = Any.Pack(new Empty()),
                                            Message =
                                                $"Envelope callback expected either {nameof(ClientMessage.WriteEventsCompleted)} or {nameof(ClientMessage.NotHandled)}, received {message.GetType().Name} instead"
                                        }
                                    }
                                };

                                batchAppendResp.CorrelationId    = Uuid.FromGuid(correlationId).ToDto();
                                batchAppendResp.StreamIdentifier = new StreamIdentifier {
                                    StreamName = ByteString.CopyFromUtf8(clientWriteRequest.StreamId)
                                };
                                return(batchAppendResp);
                            }
                        } catch (Exception ex) {
                            await writer.WriteAsync(new BatchAppendResp {
                                CorrelationId    = request.CorrelationId,
                                StreamIdentifier = request.Options.StreamIdentifier,
                                Error            = Status.BadRequest(ex.Message)
                            }, cancellationToken).ConfigureAwait(false);
                        }
                    }
Beispiel #16
0
        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;
                }
            }
        }
Beispiel #17
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.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;
                }
            }
        }
    }
            private void ReadPage(StreamRevision startRevision, ulong readCount = 0)
            {
                Guid correlationId = Guid.NewGuid();

                _bus.Publish(new ClientMessage.ReadStreamEventsForward(
                                 correlationId, correlationId, new ContinuationEnvelope(OnMessage, _semaphore, _cancellationToken),
                                 _streamName, startRevision.ToInt64(), (int)Math.Min(ReadBatchSize, _maxCount), _resolveLinks,
                                 _requiresLeader, null, _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.ReadStreamEventsForwardCompleted completed))
                    {
                        _channel.Writer.TryComplete(
                            RpcExceptions.UnknownMessage <ClientMessage.ReadStreamEventsForwardCompleted>(message));
                        return;
                    }

                    switch (completed.Result)
                    {
                    case ReadStreamResult.Success:
                        if (readCount == 0 && _compatibility >= 1)
                        {
                            if (completed.Events.Length == 0)
                            {
                                var firstStreamPosition = StreamRevision.FromInt64(completed.NextEventNumber);
                                if (startRevision != firstStreamPosition)
                                {
                                    await _channel.Writer.WriteAsync(new() {
                                        FirstStreamPosition = firstStreamPosition
                                    }, ct).ConfigureAwait(false);
                                }
                            }
                        }

                        foreach (var @event in completed.Events)
                        {
                            if (readCount >= _maxCount)
                            {
                                break;
                            }
                            await _channel.Writer.WriteAsync(new() {
                                Event = ConvertToReadEvent(_uuidOption, @event)
                            }, ct).ConfigureAwait(false);

                            readCount++;
                        }

                        if (!completed.IsEndOfStream && readCount < _maxCount)
                        {
                            ReadPage(StreamRevision.FromInt64(completed.NextEventNumber), readCount);
                            return;
                        }

                        if (_compatibility >= 1)
                        {
                            await _channel.Writer.WriteAsync(new() {
                                LastStreamPosition = StreamRevision.FromInt64(completed.LastEventNumber)
                            }, ct).ConfigureAwait(false);
                        }

                        _channel.Writer.TryComplete();
                        return;

                    case ReadStreamResult.NoStream:
                        await _channel.Writer.WriteAsync(new() {
                            StreamNotFound = new() {
                                StreamIdentifier = _streamName
                            }
                        }, _cancellationToken).ConfigureAwait(false);

                        _channel.Writer.TryComplete();
                        return;

                    case ReadStreamResult.StreamDeleted:
                        _channel.Writer.TryComplete(RpcExceptions.StreamDeleted(_streamName));
                        return;

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

                    default:
                        _channel.Writer.TryComplete(RpcExceptions.UnknownError(completed.Result));
                        return;
                    }
                }
            }
            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;
                    }
                }
            }
Beispiel #20
0
        public override async Task <CreateResp> Create(CreateReq request, ServerCallContext context)
        {
            var createPersistentSubscriptionSource = new TaskCompletionSource <CreateResp>();
            var settings      = request.Options.Settings;
            var correlationId = Guid.NewGuid();

            var user = context.GetHttpContext().User;

            if (!await _authorizationProvider.CheckAccessAsync(user,
                                                               CreateOperation, context.CancellationToken).ConfigureAwait(false))
            {
                throw AccessDenied();
            }
            _publisher.Publish(new ClientMessage.CreatePersistentSubscription(
                                   correlationId,
                                   correlationId,
                                   new CallbackEnvelope(HandleCreatePersistentSubscriptionCompleted),
                                   request.Options.StreamIdentifier,
                                   request.Options.GroupName,
                                   settings.ResolveLinks,
                                   new StreamRevision(settings.Revision).ToInt64(),
                                   (int)TimeSpan.FromTicks(settings.MessageTimeout).TotalMilliseconds,
                                   settings.ExtraStatistics,
                                   settings.MaxRetryCount,
                                   settings.HistoryBufferSize,
                                   settings.LiveBufferSize,
                                   settings.ReadBatchSize,
                                   (int)TimeSpan.FromTicks(settings.CheckpointAfter).TotalMilliseconds,
                                   settings.MinCheckpointCount,
                                   settings.MaxCheckpointCount,
                                   settings.MaxSubscriberCount,
                                   settings.NamedConsumerStrategy.ToString(),
                                   user));

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

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

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

                switch (completed.Result)
                {
                case CreatePersistentSubscriptionResult.Success:
                    createPersistentSubscriptionSource.TrySetResult(new CreateResp());
                    return;

                case CreatePersistentSubscriptionResult.Fail:
                    createPersistentSubscriptionSource.TrySetException(RpcExceptions.PersistentSubscriptionFailed(request.Options.StreamIdentifier, request.Options.GroupName, completed.Reason));
                    return;

                case CreatePersistentSubscriptionResult.AlreadyExists:
                    createPersistentSubscriptionSource.TrySetException(RpcExceptions.PersistentSubscriptionExists(request.Options.StreamIdentifier, request.Options.GroupName));
                    return;

                case CreatePersistentSubscriptionResult.AccessDenied:
                    createPersistentSubscriptionSource.TrySetException(RpcExceptions.AccessDenied());
                    return;

                default:
                    createPersistentSubscriptionSource.TrySetException(RpcExceptions.UnknownError(completed.Result));
                    return;
                }
            }
        }
            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);
                }
            }
            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;
                    }
                }
            }
        public override async Task Read(IAsyncStreamReader <ReadReq> requestStream,
                                        IServerStreamWriter <ReadResp> responseStream, ServerCallContext context)
        {
            if (!await requestStream.MoveNext().ConfigureAwait(false))
            {
                return;
            }

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

            var options = requestStream.Current.Options;
            var user    = context.GetHttpContext().User;

            string streamId = null;

            switch (options.StreamOptionCase)
            {
            case ReadReq.Types.Options.StreamOptionOneofCase.StreamIdentifier:
                streamId = options.StreamIdentifier;
                break;

            case ReadReq.Types.Options.StreamOptionOneofCase.All:
                streamId = SystemStreams.AllStream;
                break;

            default:
                throw new InvalidOperationException();
            }

            if (!await _authorizationProvider.CheckAccessAsync(user,
                                                               ProcessMessagesOperation.WithParameter(Plugins.Authorization.Operations.Subscriptions.Parameters.StreamId(streamId)), context.CancellationToken).ConfigureAwait(false))
            {
                throw RpcExceptions.AccessDenied();
            }
            var connectionName =
                context.RequestHeaders.FirstOrDefault(x => x.Key == Constants.Headers.ConnectionName)?.Value ??
                "<unknown>";
            var correlationId   = Guid.NewGuid();
            var uuidOptionsCase = options.UuidOption.ContentCase;

            await using var enumerator = new PersistentStreamSubscriptionEnumerator(correlationId, connectionName,
                                                                                    _publisher, streamId, options.GroupName, options.BufferSize, user, context.CancellationToken);

            var subscriptionId = await enumerator.Started.ConfigureAwait(false);

            var read = ValueTask.CompletedTask;
            var cts  = new CancellationTokenSource();

            try {
                read = PumpRequestStream(cts.Token);

                await responseStream.WriteAsync(new ReadResp {
                    SubscriptionConfirmation = new ReadResp.Types.SubscriptionConfirmation {
                        SubscriptionId = subscriptionId
                    }
                }).ConfigureAwait(false);

                while (await enumerator.MoveNextAsync().ConfigureAwait(false))
                {
                    await responseStream.WriteAsync(new ReadResp {
                        Event = ConvertToReadEvent(enumerator.Current)
                    }).ConfigureAwait(false);
                }
            } catch (IOException) {
                Log.Information("Subscription {correlationId} to {subscriptionId} disposed. The request stream was closed.", correlationId, subscriptionId);
            } finally {
                // make sure we stop reading the request stream before leaving this method
                cts.Cancel();
                await read.ConfigureAwait(false);
            }

            async ValueTask PumpRequestStream(CancellationToken token)
            {
                try {
                    await requestStream.ForEachAsync(HandleAckNack, token).ConfigureAwait(false);
                } catch {
                }
            }

            ValueTask HandleAckNack(ReadReq request)
            {
                _publisher.Publish(request.ContentCase switch {
                    ReadReq.ContentOneofCase.Ack => new ClientMessage.PersistentSubscriptionAckEvents(
                        correlationId, correlationId, new NoopEnvelope(), subscriptionId,
                        request.Ack.Ids.Select(id => Uuid.FromDto(id).ToGuid()).ToArray(), user),
                    ReadReq.ContentOneofCase.Nack =>
                    new ClientMessage.PersistentSubscriptionNackEvents(
                        correlationId, correlationId, new NoopEnvelope(), subscriptionId,
                        request.Nack.Reason, request.Nack.Action switch {
                        ReadReq.Types.Nack.Types.Action.Unknown => NakAction.Unknown,
                        ReadReq.Types.Nack.Types.Action.Park => NakAction.Park,
                        ReadReq.Types.Nack.Types.Action.Retry => NakAction.Retry,
                        ReadReq.Types.Nack.Types.Action.Skip => NakAction.Skip,
                        ReadReq.Types.Nack.Types.Action.Stop => NakAction.Stop,
                        _ => throw RpcExceptions.InvalidArgument(request.Nack.Action)
                    },
                        request.Nack.Ids.Select(id => Uuid.FromDto(id).ToGuid()).ToArray(), user),
                    _ => throw RpcExceptions.InvalidArgument(request.ContentCase)
                });
Beispiel #24
0
        public override async Task <DeleteResp> Delete(DeleteReq request, ServerCallContext context)
        {
            var createPersistentSubscriptionSource = new TaskCompletionSource <DeleteResp>();
            var correlationId = Guid.NewGuid();

            var user = context.GetHttpContext().User;

            if (!await _authorizationProvider.CheckAccessAsync(user,
                                                               DeleteOperation, context.CancellationToken).ConfigureAwait(false))
            {
                throw RpcExceptions.AccessDenied();
            }

            string streamId = null;

            switch (request.Options.StreamOptionCase)
            {
            case StreamOptionOneofCase.StreamIdentifier:
            {
                streamId = request.Options.StreamIdentifier;
                _publisher.Publish(new ClientMessage.DeletePersistentSubscriptionToStream(
                                       correlationId,
                                       correlationId,
                                       new CallbackEnvelope(HandleDeletePersistentSubscriptionCompleted),
                                       streamId,
                                       request.Options.GroupName,
                                       user));
                break;
            }

            case StreamOptionOneofCase.All:
                streamId = SystemStreams.AllStream;
                _publisher.Publish(new ClientMessage.DeletePersistentSubscriptionToAll(
                                       correlationId,
                                       correlationId,
                                       new CallbackEnvelope(HandleDeletePersistentSubscriptionCompleted),
                                       request.Options.GroupName,
                                       user));
                break;

            default:
                throw new InvalidOperationException();
            }

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

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

                if (streamId != SystemStreams.AllStream)
                {
                    if (message is ClientMessage.DeletePersistentSubscriptionToStreamCompleted completed)
                    {
                        switch (completed.Result)
                        {
                        case DeletePersistentSubscriptionToStreamResult.Success:
                            createPersistentSubscriptionSource.TrySetResult(new DeleteResp());
                            return;

                        case DeletePersistentSubscriptionToStreamResult.Fail:
                            createPersistentSubscriptionSource.TrySetException(
                                RpcExceptions.PersistentSubscriptionFailed(streamId, request.Options.GroupName,
                                                                           completed.Reason));
                            return;

                        case DeletePersistentSubscriptionToStreamResult.DoesNotExist:
                            createPersistentSubscriptionSource.TrySetException(
                                RpcExceptions.PersistentSubscriptionDoesNotExist(streamId,
                                                                                 request.Options.GroupName));
                            return;

                        case DeletePersistentSubscriptionToStreamResult.AccessDenied:
                            createPersistentSubscriptionSource.TrySetException(RpcExceptions.AccessDenied());
                            return;

                        default:
                            createPersistentSubscriptionSource.TrySetException(
                                RpcExceptions.UnknownError(completed.Result));
                            return;
                        }
                    }
                    createPersistentSubscriptionSource.TrySetException(
                        RpcExceptions.UnknownMessage <ClientMessage.DeletePersistentSubscriptionToStreamCompleted>(message));
                }
                else
                {
                    if (message is ClientMessage.DeletePersistentSubscriptionToAllCompleted completedAll)
                    {
                        switch (completedAll.Result)
                        {
                        case DeletePersistentSubscriptionToAllResult.Success:
                            createPersistentSubscriptionSource.TrySetResult(new DeleteResp());
                            return;

                        case DeletePersistentSubscriptionToAllResult.Fail:
                            createPersistentSubscriptionSource.TrySetException(
                                RpcExceptions.PersistentSubscriptionFailed(streamId, request.Options.GroupName,
                                                                           completedAll.Reason));
                            return;

                        case DeletePersistentSubscriptionToAllResult.DoesNotExist:
                            createPersistentSubscriptionSource.TrySetException(
                                RpcExceptions.PersistentSubscriptionDoesNotExist(streamId,
                                                                                 request.Options.GroupName));
                            return;

                        case DeletePersistentSubscriptionToAllResult.AccessDenied:
                            createPersistentSubscriptionSource.TrySetException(RpcExceptions.AccessDenied());
                            return;

                        default:
                            createPersistentSubscriptionSource.TrySetException(
                                RpcExceptions.UnknownError(completedAll.Result));
                            return;
                        }
                    }
                    createPersistentSubscriptionSource.TrySetException(
                        RpcExceptions.UnknownMessage <ClientMessage.DeletePersistentSubscriptionToAllCompleted>(message));
                }
            }
        }
            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;
                    }
                }
            }
Beispiel #26
0
        public override async Task Details(DetailsReq request, IServerStreamWriter <DetailsResp> responseStream,
                                           ServerCallContext context)
        {
            var options = request.Options;

            var user          = context.GetHttpContext().User;
            var readOperation = ReadOperation;

            if (user?.Identity?.Name != null)
            {
                readOperation =
                    readOperation.WithParameter(
                        Plugins.Authorization.Operations.Users.Parameters.User(user.Identity.Name));
            }
            if (!await _authorizationProvider.CheckAccessAsync(user, readOperation, context.CancellationToken).ConfigureAwait(false))
            {
                throw AccessDenied();
            }
            var detailsSource = new TaskCompletionSource <UserManagementMessage.UserData[]>();

            var envelope = new CallbackEnvelope(OnMessage);

            _publisher.Publish(string.IsNullOrWhiteSpace(options?.LoginName)
                                ? (Message) new UserManagementMessage.GetAll(envelope, user)
                                : new UserManagementMessage.Get(envelope, user, options.LoginName));

            var details = await detailsSource.Task.ConfigureAwait(false);

            foreach (var detail in details)
            {
                await responseStream.WriteAsync(new DetailsResp {
                    UserDetails = new DetailsResp.Types.UserDetails {
                        Disabled    = detail.Disabled,
                        Groups      = { detail.Groups },
                        FullName    = detail.FullName,
                        LoginName   = detail.LoginName,
                        LastUpdated = detail.DateLastUpdated.HasValue
                                                        ? new DetailsResp.Types.UserDetails.Types.DateTime
                        {
                            TicksSinceEpoch = detail.DateLastUpdated.Value.UtcDateTime.ToTicksSinceEpoch()
                        }
                                                        : null
                    }
                }).ConfigureAwait(false);
            }

            void OnMessage(Message message)
            {
                if (HandleErrors(options?.LoginName, message, detailsSource))
                {
                    return;
                }

                switch (message)
                {
                case UserManagementMessage.UserDetailsResult userDetails:
                    detailsSource.TrySetResult(new[] { userDetails.Data });
                    break;

                case UserManagementMessage.AllUserDetailsResult allUserDetails:
                    detailsSource.TrySetResult(allUserDetails.Data);
                    break;

                default:
                    detailsSource.TrySetException(RpcExceptions.UnknownError(1));
                    break;
                }
            }
        }
Beispiel #27
0
        public override async Task Read(
            ReadReq request,
            IServerStreamWriter <ReadResp> responseStream,
            ServerCallContext context)
        {
            var options           = request.Options;
            var countOptionsCase  = options.CountOptionCase;
            var streamOptionsCase = options.StreamOptionCase;
            var readDirection     = options.ReadDirection;
            var filterOptionsCase = options.FilterOptionCase;

            var user           = context.GetHttpContext().User;
            var requiresLeader = GetRequiresLeader(context.RequestHeaders);

            var op = streamOptionsCase switch {
                StreamOptionOneofCase.Stream => ReadOperation.WithParameter(
                    Plugins.Authorization.Operations.Streams.Parameters.StreamId(
                        request.Options.Stream.StreamIdentifier)),
                StreamOptionOneofCase.All => ReadOperation.WithParameter(
                    Plugins.Authorization.Operations.Streams.Parameters.StreamId(SystemStreams.AllStream)),
                _ => throw RpcExceptions.InvalidArgument(streamOptionsCase)
            };

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

            await using var enumerator =
                            (streamOptionsCase, countOptionsCase, readDirection, filterOptionsCase) switch {
                            (StreamOptionOneofCase.Stream,
                             CountOptionOneofCase.Count,
                             ReadDirection.Forwards,
                             FilterOptionOneofCase.NoFilter) => (IAsyncEnumerator <ReadResp>)
                            new Enumerators.ReadStreamForwards(
                                _publisher,
                                request.Options.Stream.StreamIdentifier,
                                request.Options.Stream.ToStreamRevision(),
                                request.Options.Count,
                                request.Options.ResolveLinks,
                                user,
                                requiresLeader,
                                context.Deadline,
                                options.UuidOption,
                                context.CancellationToken),
                            (StreamOptionOneofCase.Stream,
                             CountOptionOneofCase.Count,
                             ReadDirection.Backwards,
                             FilterOptionOneofCase.NoFilter) => new Enumerators.ReadStreamBackwards(
                                _publisher,
                                request.Options.Stream.StreamIdentifier,
                                request.Options.Stream.ToStreamRevision(),
                                request.Options.Count,
                                request.Options.ResolveLinks,
                                user,
                                requiresLeader,
                                context.Deadline,
                                options.UuidOption,
                                context.CancellationToken),
                            (StreamOptionOneofCase.All,
                             CountOptionOneofCase.Count,
                             ReadDirection.Forwards,
                             FilterOptionOneofCase.NoFilter) => new Enumerators.ReadAllForwards(
                                _publisher,
                                request.Options.All.ToPosition(),
                                request.Options.Count,
                                request.Options.ResolveLinks,
                                user,
                                requiresLeader,
                                context.Deadline,
                                options.UuidOption,
                                context.CancellationToken),
                            (StreamOptionOneofCase.All,
                             CountOptionOneofCase.Count,
                             ReadDirection.Forwards,
                             FilterOptionOneofCase.Filter) => new Enumerators.ReadAllForwardsFiltered(
                                _publisher,
                                request.Options.All.ToPosition(),
                                request.Options.Count,
                                request.Options.ResolveLinks,
                                ConvertToEventFilter(true, request.Options.Filter),
                                user,
                                requiresLeader,
                                _readIndex,
                                request.Options.Filter.WindowCase switch {
                    ReadReq.Types.Options.Types.FilterOptions.WindowOneofCase.Count => null,
                    ReadReq.Types.Options.Types.FilterOptions.WindowOneofCase.Max => request.Options.Filter
                    .Max,
                    _ => throw RpcExceptions.InvalidArgument(request.Options.Filter.WindowCase)
                },
Beispiel #28
0
        public override async Task <ReplayParkedResp> ReplayParked(ReplayParkedReq request, ServerCallContext context)
        {
            var replayParkedMessagesSource = new TaskCompletionSource <ReplayParkedResp>();
            var correlationId = Guid.NewGuid();

            var user = context.GetHttpContext().User;

            if (!await _authorizationProvider.CheckAccessAsync(user,
                                                               ReplayParkedOperation, context.CancellationToken).ConfigureAwait(false))
            {
                throw RpcExceptions.AccessDenied();
            }

            string streamId = request.Options.StreamOptionCase switch {
                ReplayParkedReq.Types.Options.StreamOptionOneofCase.All => "$all",
                ReplayParkedReq.Types.Options.StreamOptionOneofCase.StreamIdentifier => request.Options.StreamIdentifier,
                _ => throw new InvalidOperationException()
            };

            long?stopAt = request.Options.StopAtOptionCase switch {
                ReplayParkedReq.Types.Options.StopAtOptionOneofCase.StopAt => request.Options.StopAt,
                ReplayParkedReq.Types.Options.StopAtOptionOneofCase.NoLimit => null,
                _ => throw new InvalidOperationException()
            };

            _publisher.Publish(new ClientMessage.ReplayParkedMessages(
                                   correlationId,
                                   correlationId,
                                   new CallbackEnvelope(HandleReplayParkedMessagesCompleted),
                                   streamId,
                                   request.Options.GroupName,
                                   stopAt,
                                   user));
            return(await replayParkedMessagesSource.Task.ConfigureAwait(false));

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

                if (message is ClientMessage.ReplayMessagesReceived completed)
                {
                    switch (completed.Result)
                    {
                    case ClientMessage.ReplayMessagesReceived.ReplayMessagesReceivedResult.Success:
                        replayParkedMessagesSource.TrySetResult(new ReplayParkedResp());
                        return;

                    case ClientMessage.ReplayMessagesReceived.ReplayMessagesReceivedResult.DoesNotExist:
                        replayParkedMessagesSource.TrySetException(
                            RpcExceptions.PersistentSubscriptionDoesNotExist(streamId,
                                                                             request.Options.GroupName));
                        return;

                    case ClientMessage.ReplayMessagesReceived.ReplayMessagesReceivedResult.AccessDenied:
                        replayParkedMessagesSource.TrySetException(
                            RpcExceptions.AccessDenied());
                        return;

                    case ClientMessage.ReplayMessagesReceived.ReplayMessagesReceivedResult.Fail:
                        replayParkedMessagesSource.TrySetException(
                            RpcExceptions.PersistentSubscriptionFailed(streamId, request.Options.GroupName,
                                                                       completed.Reason));
                        return;

                    default:
                        replayParkedMessagesSource.TrySetException(
                            RpcExceptions.UnknownError(completed.Result));
                        return;
                    }
                }
                replayParkedMessagesSource.TrySetException(
                    RpcExceptions.UnknownMessage <ClientMessage.ReplayMessagesReceived>(
                        message));
            }
        }
    }
}
Beispiel #29
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;
                }
            }
        }
    }
Beispiel #30
0
        public override async Task <UpdateResp> Update(UpdateReq request, ServerCallContext context)
        {
            var updatePersistentSubscriptionSource = new TaskCompletionSource <UpdateResp>();
            var settings      = request.Options.Settings;
            var correlationId = Guid.NewGuid();

            var user = context.GetHttpContext().User;

            if (!await _authorizationProvider.CheckAccessAsync(user,
                                                               UpdateOperation, context.CancellationToken).ConfigureAwait(false))
            {
                throw RpcExceptions.AccessDenied();
            }

            string streamId = null;

            switch (request.Options.StreamOptionCase)
            {
            case StreamOptionOneofCase.Stream:
            case StreamOptionOneofCase.None:                     /*for backwards compatibility*/
            {
                StreamRevision startRevision;

                if (request.Options.StreamOptionCase == StreamOptionOneofCase.Stream)
                {
                    streamId      = request.Options.Stream.StreamIdentifier;
                    startRevision = request.Options.Stream.RevisionOptionCase switch {
                        RevisionOptionOneofCase.Revision => new StreamRevision(request.Options.Stream.Revision),
                        RevisionOptionOneofCase.Start => StreamRevision.Start,
                        RevisionOptionOneofCase.End => StreamRevision.End,
                        _ => throw RpcExceptions.InvalidArgument(request.Options.Stream.RevisionOptionCase)
                    };
                }
                else                             /*for backwards compatibility*/
                {
                                                #pragma warning disable 612
                    streamId      = request.Options.StreamIdentifier;
                    startRevision = new StreamRevision(request.Options.Settings.Revision);
                                                #pragma warning restore 612
                }

                _publisher.Publish(new ClientMessage.UpdatePersistentSubscriptionToStream(
                                       correlationId,
                                       correlationId,
                                       new CallbackEnvelope(HandleUpdatePersistentSubscriptionCompleted),
                                       streamId,
                                       request.Options.GroupName,
                                       settings.ResolveLinks,
                                       startRevision.ToInt64(),
                                       settings.MessageTimeoutCase switch {
                        UpdateReq.Types.Settings.MessageTimeoutOneofCase.MessageTimeoutMs => settings.MessageTimeoutMs,
                        UpdateReq.Types.Settings.MessageTimeoutOneofCase.MessageTimeoutTicks => (int)TimeSpan
                        .FromTicks(settings.MessageTimeoutTicks).TotalMilliseconds,
                        _ => 0
                    },
                                       settings.ExtraStatistics,
                                       settings.MaxRetryCount,
                                       settings.HistoryBufferSize,
                                       settings.LiveBufferSize,
                                       settings.ReadBatchSize,
                                       settings.CheckpointAfterCase switch {
                        UpdateReq.Types.Settings.CheckpointAfterOneofCase.CheckpointAfterMs => settings.CheckpointAfterMs,
                        UpdateReq.Types.Settings.CheckpointAfterOneofCase.CheckpointAfterTicks => (int)TimeSpan
                        .FromTicks(settings.CheckpointAfterTicks).TotalMilliseconds,
                        _ => 0
                    },