Example #1
0
    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();
        }
    }
Example #2
0
    /// <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);
    }