public async Task RegisteringAndProcessing() { var x = await Task.WhenAll(_projectionsToRun.Select(projection => _projections.Register( projection, Runtime.CreateExecutionContextFor("a2775100-bad1-4260-a97f-13ef9caf9720"), _cancellationTokenSource.Token))).ConfigureAwait(false); var dispatcherTask = _dispatcher.Object.Accept(new ProjectionRegistrationResponse(), CancellationToken.None); var tasks = x.Select(_ => _.Result.Start()).Append(dispatcherTask); var taskGroup = new TaskGroup(tasks); await taskGroup.WaitForAllCancellingOnFirst(_cancellationTokenSource); foreach (var projectionProcessor in x) { projectionProcessor.Result?.Dispose(); } }
/// <inheritdoc/> public override async Task Connect( IAsyncStreamReader <ProjectionClientToRuntimeMessage> runtimeStream, IServerStreamWriter <ProjectionRuntimeToClientMessage> clientStream, ServerCallContext context) { // TODO: It seems like things are not properly unregistered on exceptions? // TODO: I tested this out and while making the DI container work, it kept failing and telling me that the projection was already registered on the second attempt. Log.ConnectingProjections(_logger); using var cts = CancellationTokenSource.CreateLinkedTokenSource(_hostApplicationLifetime.ApplicationStopping, context.CancellationToken); var tryConnect = await _reverseCallServices.Connect(runtimeStream, clientStream, context, _protocol, cts.Token).ConfigureAwait(false); if (!tryConnect.Success) { return; } using var dispatcher = tryConnect.Result.dispatcher; var arguments = tryConnect.Result.arguments; var executionContext = arguments.ExecutionContext; var definition = arguments.ProjectionDefinition; var tryCreateExecutionContext = _executionContextCreator.TryCreateUsing(arguments.ExecutionContext); if (!tryCreateExecutionContext.Success) { await dispatcher.Reject( _protocol.CreateFailedConnectResponse($"Failed to register Projection because the execution context is invalid: ${tryCreateExecutionContext.Exception.Message}"), cts.Token).ConfigureAwait(false); return; } Log.ReceivedProjectionArguments(_logger, definition.Scope, definition.Projection); var projection = new Projection(dispatcher, definition, arguments.Alias, arguments.HasAlias); var registration = await _projections.Register(projection, executionContext, cts.Token); if (!registration.Success) { Log.ErrorWhileRegisteringProjection(_logger, definition.Scope, definition.Projection, registration.Exception); await dispatcher.Reject(new ProjectionRegistrationResponse { Failure = new Failure( ProjectionFailures.FailedToRegisterProjection, $"Failed to register Projection {projection.Definition.Projection}. {registration.Exception.Message}") }, cts.Token).ConfigureAwait(false); return; } using var processor = registration.Result; var reverseCall = dispatcher.Accept(new ProjectionRegistrationResponse(), cts.Token); var processing = processor.Start(); var tasks = new TaskGroup(reverseCall, processing); tasks.OnFirstTaskFailure += (_, ex) => Log.ErrorWhileRunningProjection(_logger, projection.Definition.Scope, projection.Definition.Projection, ex); tasks.OnAllTasksCompleted += () => Log.ProjectionDisconnected(_logger, projection.Definition.Scope, projection.Definition.Projection); await tasks.WaitForAllCancellingOnFirst(cts).ConfigureAwait(false); }