public override Task <Empty> LeaderIsResigning(LeaderIsResigningRequest request, ServerCallContext context) { _bus.Publish(new ElectionMessage.LeaderIsResigning( Uuid.FromDto(request.LeaderId).ToGuid(), new IPEndPoint(IPAddress.Parse(request.LeaderInternalHttp.Address), (int)request.LeaderInternalHttp.Port))); return(EmptyResult); }
public override Task <Empty> Prepare(PrepareRequest request, ServerCallContext context) { _bus.Publish(new ElectionMessage.Prepare( Uuid.FromDto(request.ServerId).ToGuid(), new IPEndPoint(IPAddress.Parse(request.ServerInternalHttp.Address), (int)request.ServerInternalHttp.Port), request.View)); return(EmptyResult); }
public override async Task <Empty> Prepare(PrepareRequest request, ServerCallContext context) { var user = context.GetHttpContext().User; if (!await _authorizationProvider.CheckAccessAsync(user, PrepareOperation, context.CancellationToken).ConfigureAwait(false)) { throw AccessDenied(); } _bus.Publish(new ElectionMessage.Prepare( Uuid.FromDto(request.ServerId).ToGuid(), new IPEndPoint(IPAddress.Parse(request.ServerInternalHttp.Address), (int)request.ServerInternalHttp.Port), request.View)); return(EmptyResult); }
public override async Task <Empty> ViewChangeProof(ViewChangeProofRequest request, ServerCallContext context) { var user = context.GetHttpContext().User; if (!await _authorizationProvider.CheckAccessAsync(user, ViewChangeProofOperation, context.CancellationToken).ConfigureAwait(false)) { throw AccessDenied(); } _bus.Publish(new ElectionMessage.ViewChangeProof( Uuid.FromDto(request.ServerId).ToGuid(), new DnsEndPoint(request.ServerHttp.Address, (int)request.ServerHttp.Port), request.InstalledView)); return(EmptyResult); }
public override async Task <Empty> LeaderIsResigning(LeaderIsResigningRequest request, ServerCallContext context) { var user = context.GetHttpContext().User; if (!await _authorizationProvider.CheckAccessAsync(user, MasterIsResigningOperation, context.CancellationToken).ConfigureAwait(false)) { throw AccessDenied(); } _bus.Publish(new ElectionMessage.LeaderIsResigning( Uuid.FromDto(request.LeaderId).ToGuid(), new DnsEndPoint(request.LeaderHttp.Address, (int)request.LeaderHttp.Port))); return(EmptyResult); }
public override Task <Empty> PrepareOk(PrepareOkRequest request, ServerCallContext context) { _bus.Publish(new ElectionMessage.PrepareOk( request.View, Uuid.FromDto(request.ServerId).ToGuid(), new IPEndPoint(IPAddress.Parse(request.ServerInternalHttp.Address), (int)request.ServerInternalHttp.Port), request.EpochNumber, request.EpochPosition, Uuid.FromDto(request.EpochId).ToGuid(), request.LastCommitPosition, request.WriterCheckpoint, request.ChaserCheckpoint, request.NodePriority)); return(EmptyResult); }
public override async Task <Empty> PrepareOk(PrepareOkRequest request, ServerCallContext context) { var user = context.GetHttpContext().User; if (!await _authorizationProvider.CheckAccessAsync(user, PrepareOkOperation, context.CancellationToken).ConfigureAwait(false)) { throw AccessDenied(); } _bus.Publish(new ElectionMessage.PrepareOk( request.View, Uuid.FromDto(request.ServerId).ToGuid(), new DnsEndPoint(request.ServerHttp.Address, (int)request.ServerHttp.Port), request.EpochNumber, request.EpochPosition, Uuid.FromDto(request.EpochId).ToGuid(), request.LastCommitPosition, request.WriterCheckpoint, request.ChaserCheckpoint, request.NodePriority)); return(EmptyResult); }
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 <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; } } } }
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; if (!await _authorizationProvider.CheckAccessAsync(user, ProcessMessagesOperation.WithParameter(Plugins.Authorization.Operations.Subscriptions.Parameters.StreamId(options.StreamName)), context.CancellationToken).ConfigureAwait(false)) { throw 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, options.StreamName, options.GroupName, options.BufferSize, user, context.CancellationToken); var subscriptionId = await enumerator.Started.ConfigureAwait(false); var read = requestStream.ForEachAsync(HandleAckNack); 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); } await read.ConfigureAwait(false); 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 new InvalidOperationException() }, request.Nack.Ids.Select(id => Uuid.FromDto(id).ToGuid()).ToArray(), user), _ => throw new InvalidOperationException() });
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) });
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); } }
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; var connectionName = context.RequestHeaders.FirstOrDefault(x => x.Key == Constants.Headers.ConnectionName)?.Value ?? "<unknown>"; var correlationId = Guid.NewGuid(); var uuidOptionsCase = options.UuidOption.ContentCase; var source = new TaskCompletionSource <bool>(); string subscriptionId = default; await using var _ = context.CancellationToken.Register(source.SetCanceled).ConfigureAwait(false); #pragma warning disable 4014 requestStream.ForEachAsync(HandleAckNack); #pragma warning restore 4014 await using var enumerator = new PersistentStreamSubscriptionEnumerator(correlationId, connectionName, _queue, options.StreamName, options.GroupName, options.BufferSize, user, context.CancellationToken); subscriptionId = await enumerator.Started.ConfigureAwait(false); 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); } Task HandleAckNack(ReadReq request) { _queue.Publish(request.ContentCase switch { ReadReq.ContentOneofCase.Ack => (Message) 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 new InvalidOperationException() }, request.Nack.Ids.Select(id => Uuid.FromDto(id).ToGuid()).ToArray(), user), _ => throw new InvalidOperationException() });
public override async Task Read(IAsyncStreamReader <ReadReq> requestStream, IServerStreamWriter <ReadResp> responseStream, ServerCallContext context) { if (!await requestStream.MoveNext()) { return; } if (requestStream.Current.ContentCase != ReadReq.ContentOneofCase.Options) { throw new InvalidOperationException(); } var options = requestStream.Current.Options; var user = await GetUser(_authenticationProvider, context.RequestHeaders); var correlationId = Guid.NewGuid(); var source = new TaskCompletionSource <bool>(); string subscriptionId = default; context.CancellationToken.Register(source.SetCanceled); #pragma warning disable 4014 Task.Run(() => requestStream.ForEachAsync(HandleAckNack)); #pragma warning restore 4014 await using var enumerator = new PersistentStreamSubscriptionEnumerator(correlationId, _queue, options.StreamName, options.GroupName, options.BufferSize, user, context.CancellationToken); subscriptionId = await enumerator.Started; await responseStream.WriteAsync(new ReadResp { Empty = new ReadResp.Types.Empty() }); while (await enumerator.MoveNextAsync()) { await responseStream.WriteAsync(new ReadResp { Event = ConvertToReadEvent(enumerator.Current) }); } Task HandleAckNack(ReadReq request) { _queue.Publish(request.ContentCase switch { ReadReq.ContentOneofCase.Ack => (Message) 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 new InvalidOperationException() }, request.Nack.Ids.Select(id => Uuid.FromDto(id).ToGuid()).ToArray(), user), _ => throw new InvalidOperationException() });