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); } }
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++; }
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; } } }
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); }
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++; }
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; } } }
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) });
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); }
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); } }
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; } } }
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; } } }
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) });
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; } } }
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; } } }
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) },
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)); } } } }
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; } } } }
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 },