示例#1
0
    async Task <Try <IEnumerable <Task> > > TryStartFilter <TClientMessage, TConnectRequest, TResponse, TFilterDefinition>(
        IReverseCallDispatcher <TClientMessage, FilterRuntimeToClientMessage, TConnectRequest, FilterRegistrationResponse, FilterEventRequest, TResponse> dispatcher,
        StreamProcessor streamProcessor,
        ScopeId scopeId,
        TFilterDefinition filterDefinition,
        Func <TenantId, IFilterProcessor <TFilterDefinition> > getFilterProcessor,
        CancellationToken cancellationToken)
        where TClientMessage : IMessage, new()
        where TConnectRequest : class
        where TResponse : class
        where TFilterDefinition : IFilterDefinition
    {
        _logger.StartingFilter(filterDefinition.TargetStream);
        try
        {
            var runningDispatcher = dispatcher.Accept(new FilterRegistrationResponse(), cancellationToken);
            await streamProcessor.Initialize().ConfigureAwait(false);
            await ValidateFilter(
                scopeId,
                filterDefinition,
                getFilterProcessor,
                cancellationToken).ConfigureAwait(false);

            return(new[] { streamProcessor.Start(), runningDispatcher });
        }
        catch (Exception ex)
        {
            if (!cancellationToken.IsCancellationRequested)
            {
                _logger.ErrorWhileStartingFilter(ex, filterDefinition.TargetStream, scopeId);
            }

            return(ex);
        }
    }
示例#2
0
    async Task <bool> RejectIfInvalidDefinition(
        EmbeddingDefinition definition,
        IReverseCallDispatcher <EmbeddingClientToRuntimeMessage, EmbeddingRuntimeToClientMessage, EmbeddingRegistrationRequest, EmbeddingRegistrationResponse, EmbeddingRequest, EmbeddingResponse> dispatcher,
        CancellationToken cancellationToken)
    {
        _logger.ComparingEmbeddingDefinition(definition);
        var tenantsAndComparisonResult = await _embeddingDefinitionComparer.DiffersFromPersisted(
            new EmbeddingDefinition(
                definition.Embedding,
                definition.Events,
                definition.InititalState),
            cancellationToken).ConfigureAwait(false);

        if (tenantsAndComparisonResult.Values.All(_ => _.Succeeded))
        {
            return(false);
        }

        var unsuccessfulComparisons = tenantsAndComparisonResult
                                      .Where(_ => !_.Value.Succeeded)
                                      .Select(_ => (_.Key, _.Value));

        _logger.InvalidEmbeddingDefinition(definition, unsuccessfulComparisons);

        await dispatcher.Reject(CreateInvalidValidationResponse(
                                    unsuccessfulComparisons,
                                    definition.Embedding),
                                cancellationToken).ConfigureAwait(false);


        return(true);
    }
示例#3
0
        async Task WriteEventsToEventHorizon(
            IReverseCallDispatcher <EventHorizonConsumerToProducerMessage, EventHorizonProducerToConsumerMessage, ConsumerSubscriptionRequest, Contracts.SubscriptionResponse, ConsumerRequest, ConsumerResponse> dispatcher,
            TenantId producerTenant,
            StreamId publicStream,
            PartitionId partition,
            StreamPosition streamPosition,
            CancellationToken cancellationToken)
        {
            try
            {
                _executionContextManager.CurrentFor(
                    _thisMicroservice,
                    producerTenant,
                    _executionContextManager.Current.CorrelationId);
                var publicEvents = await _getEventFetchers().GetPartitionedFetcherFor(
                    ScopeId.Default,
                    new StreamDefinition(new PublicFilterDefinition(StreamId.EventLog, publicStream)),
                    cancellationToken).ConfigureAwait(false);

                while (!cancellationToken.IsCancellationRequested && !_disposed)
                {
                    try
                    {
                        var tryGetStreamEvent = await publicEvents.FetchInPartition(partition, streamPosition, cancellationToken).ConfigureAwait(false);

                        if (!tryGetStreamEvent.Success)
                        {
                            await Task.Delay(250).ConfigureAwait(false);

                            continue;
                        }

                        var streamEvent = tryGetStreamEvent.Result;
                        var response    = await dispatcher.Call(
                            new ConsumerRequest { Event = streamEvent.ToEventHorizonEvent() },
                            cancellationToken).ConfigureAwait(false);

                        if (response.Failure != null)
                        {
                            _logger.Warning(
                                "An error occurred while handling request. FailureId: {FailureId} Reason: {Reason}",
                                response.Failure.Id,
                                response.Failure.Reason);
                            return;
                        }

                        streamPosition = streamEvent.Position + 1;
                    }
                    catch (EventStoreUnavailable)
                    {
                        await Task.Delay(1000).ConfigureAwait(false);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.Warning(ex, "An error ocurred while writing events to event horizon");
            }
        }
示例#4
0
 /// <summary>
 /// Initializes a new instance of the <see cref="PublicFilterProcessor"/> class.
 /// </summary>
 /// <param name="definition">The <see cref="PublicFilterDefinition"/>.</param>
 /// <param name="dispatcher"><see cref="IReverseCallDispatcher{TClientMessage, TServerMessage, TConnectArguments, TConnectResponse, TRequest, TResponse}"/>.</param>
 /// <param name="eventsToPublicStreamsWriter">The <see cref="IWriteEventsToStreams">writer</see> for writing events.</param>
 /// <param name="logger"><see cref="ILogger"/> for logging.</param>
 public PublicFilterProcessor(
     PublicFilterDefinition definition,
     IReverseCallDispatcher <PublicFilterClientToRuntimeMessage, FilterRuntimeToClientMessage, PublicFilterRegistrationRequest, FilterRegistrationResponse, FilterEventRequest, PartitionedFilterResponse> dispatcher,
     IWriteEventsToPublicStreams eventsToPublicStreamsWriter,
     ILogger logger)
     : base(ScopeId.Default, definition, eventsToPublicStreamsWriter, logger)
 {
     _dispatcher = dispatcher;
 }
示例#5
0
 Task WriteFailedRegistrationResponse <TClientMessage, TConnectRequest, TResponse>(
     IReverseCallDispatcher <TClientMessage, FilterRuntimeToClientMessage, TConnectRequest, FilterRegistrationResponse, FilterEventRequest, TResponse> dispatcher,
     Failure failure,
     CancellationToken cancellationToken)
     where TClientMessage : IMessage, new()
     where TConnectRequest : class
     where TResponse : class => dispatcher.Reject(new FilterRegistrationResponse {
     Failure = failure
 }, cancellationToken);
示例#6
0
 /// <summary>
 /// Initializes a new instance of the <see cref="Projection"/> class.
 /// </summary>
 /// <param name="dispatcher">The <see cref="IReverseCallDispatcher{TClientMessage, TServerMessage, TConnectArguments, TConnectResponse, TRequest, TResponse}"/> dispatcher to use to send requests to the head.</param>
 /// <param name="projectionDefinition">The <see cref="ProjectionDefinition" />.</param>
 /// <param name="alias">The alias of the Projection (if provided) from the Client.</param>
 /// <param name="hasAlias">A value indicating whether an alias was provided by the Client.</param>
 public Projection(
     IReverseCallDispatcher <ProjectionClientToRuntimeMessage, ProjectionRuntimeToClientMessage, ProjectionRegistrationRequest, ProjectionRegistrationResponse, ProjectionRequest, ProjectionResponse> dispatcher,
     ProjectionDefinition projectionDefinition,
     ProjectionAlias alias,
     bool hasAlias)
 {
     Definition  = projectionDefinition;
     _dispatcher = dispatcher;
     Alias       = alias;
     HasAlias    = hasAlias;
 }
示例#7
0
 /// <summary>
 /// Initializes a new instance of the <see cref="FilterProcessor"/> class.
 /// </summary>
 /// <param name="scope">The <see cref="ScopeId" />.</param>
 /// <param name="definition">The <see cref="FilterDefinition"/>.</param>
 /// <param name="dispatcher">The <see cref="IReverseCallDispatcher{TClientMessage, TServerMessage, TConnectArguments, TConnectResponse, TRequest, TResponse}" />.</param>
 /// <param name="eventsToStreamsWriter">The <see cref="IWriteEventsToStreams">writer</see> for writing events.</param>
 /// <param name="logger"><see cref="ILogger"/> for logging.</param>
 public FilterProcessor(
     ScopeId scope,
     FilterDefinition definition,
     IReverseCallDispatcher <FilterClientToRuntimeMessage, FilterRuntimeToClientMessage, FilterRegistrationRequest, FilterRegistrationResponse, FilterEventRequest, FilterResponse> dispatcher,
     IWriteEventsToStreams eventsToStreamsWriter,
     ILogger <FilterProcessor> logger)
     : base(scope, definition, eventsToStreamsWriter, logger)
 {
     _dispatcher = dispatcher;
     _logger     = logger;
 }
示例#8
0
 /// <summary>
 /// Initializes a new instance of the <see cref="EventProcessor"/> class.
 /// </summary>
 /// <param name="scope">The <see cref="ScopeId" />.</param>
 /// <param name="id">The <see cref="EventProcessorId" />.</param>
 /// <param name="dispatcher"><see cref="IReverseCallDispatcher{TClientMessage, TServerMessage, TConnectArguments, TConnectResponse, TRequest, TResponse}"/> dispatcher.</param>
 /// <param name="logger">The <see cref="ILogger" />.</param>
 public EventProcessor(
     ScopeId scope,
     EventProcessorId id,
     IReverseCallDispatcher <EventHandlerClientToRuntimeMessage, EventHandlerRuntimeToClientMessage, EventHandlerRegistrationRequest, EventHandlerRegistrationResponse, HandleEventRequest, EventHandlerResponse> dispatcher,
     ILogger logger)
 {
     Scope             = scope;
     Identifier        = id;
     _dispatcher       = dispatcher;
     _logger           = logger;
     _logMessagePrefix = $"Event Processor '{Identifier}'";
 }
示例#9
0
 Task <Try <ExecutionContext> > CreateExecutionContextOrReject <TClientMessage, TConnectRequest, TResponse>(
     IReverseCallDispatcher <TClientMessage, FilterRuntimeToClientMessage, TConnectRequest, FilterRegistrationResponse, FilterEventRequest, TResponse> dispatcher,
     ExecutionContext requestExecutionContext,
     CancellationToken cancellationToken)
     where TClientMessage : IMessage, new()
     where TConnectRequest : class
     where TResponse : class
 => _executionContextCreator
 .TryCreateUsing(requestExecutionContext)
 .Catch(async exception =>
 {
     _logger.ExecutionContextIsNotValid(exception);
     var failure = new Failure(FiltersFailures.CannotRegisterFilterOnNonWriteableStream, $"Execution context is invalid: {exception.Message}");
     await WriteFailedRegistrationResponse(dispatcher, failure, cancellationToken).ConfigureAwait(false);
 });
示例#10
0
        async Task <bool> RejectIfInvalidFilterId <TClientMessage, TConnectRequest, TResponse>(
            IReverseCallDispatcher <TClientMessage, FilterRuntimeToClientMessage, TConnectRequest, FilterRegistrationResponse, FilterEventRequest, TResponse> dispatcher,
            StreamId filterId,
            CancellationToken cancellationToken)
            where TClientMessage : IMessage, new()
            where TConnectRequest : class
            where TResponse : class
        {
            if (filterId.IsNonWriteable)
            {
                _logger.Warning("Filter: '{Filter}' is an invalid Stream Id", filterId);
                var failure = new Failure(FiltersFailures.CannotRegisterFilterOnNonWriteableStream, $"Filter Id: '{filterId}' is an invalid Stream Id");
                await WriteFailedRegistrationResponse(dispatcher, failure, cancellationToken).ConfigureAwait(false);

                return(true);
            }

            return(false);
        }
示例#11
0
        async Task <Try <IEnumerable <Task> > > TryStartEventHandler <TClientMessage, TConnectRequest, TResponse, TFilterDefinition>(
            IReverseCallDispatcher <TClientMessage, EventHandlerRuntimeToClientMessage, TConnectRequest, EventHandlerRegistrationResponse, HandleEventRequest, TResponse> dispatcher,
            StreamProcessor filterStreamProcessor,
            StreamProcessor eventProcessorStreamProcessor,
            ScopeId scopeId,
            TFilterDefinition filterDefinition,
            Func <IFilterProcessor <TFilterDefinition> > getFilterProcessor,
            CancellationToken cancellationToken)
            where TClientMessage : IMessage, new()
            where TConnectRequest : class
            where TResponse : class
            where TFilterDefinition : IFilterDefinition
        {
            _logger.Debug("Starting Event Handler '{EventHandlerId}'", filterDefinition.TargetStream);
            try
            {
                var runningDispatcher = dispatcher.Accept(new EventHandlerRegistrationResponse(), cancellationToken);
                await filterStreamProcessor.Initialize().ConfigureAwait(false);

                await eventProcessorStreamProcessor.Initialize().ConfigureAwait(false);
                await ValidateFilter(
                    scopeId,
                    filterDefinition,
                    getFilterProcessor,
                    cancellationToken).ConfigureAwait(false);

                return(new[] { filterStreamProcessor.Start(), eventProcessorStreamProcessor.Start(), runningDispatcher });
            }
            catch (Exception ex)
            {
                if (!cancellationToken.IsCancellationRequested)
                {
                    _logger.Warning(
                        ex,
                        "Error occurred while trying to start Event Handler '{EventHandlerId}'",
                        filterDefinition.TargetStream);
                }

                return(ex);
            }
        }
示例#12
0
        async Task <bool> RejectIfNotReceivedArguments <TClientMessage, TConnectRequest, TResponse>(
            IReverseCallDispatcher <TClientMessage, FilterRuntimeToClientMessage, TConnectRequest, FilterRegistrationResponse, FilterEventRequest, TResponse> dispatcher,
            CancellationToken cancellationToken)
            where TClientMessage : IMessage, new()
            where TConnectRequest : class
            where TResponse : class
        {
            _logger.Trace("Waiting for connection arguments...");
            if (!await dispatcher.ReceiveArguments(cancellationToken).ConfigureAwait(false))
            {
                const string message = "Connection arguments were not received";
                _logger.Warning(message);
                var failure = new Failure(FiltersFailures.NoFilterRegistrationReceived, message);
                await WriteFailedRegistrationResponse(dispatcher, failure, cancellationToken).ConfigureAwait(false);

                return(true);
            }

            _logger.Trace("Received connection arguments");
            return(false);
        }
示例#13
0
 /// <summary>
 /// Initializes a new instance of the <see cref="EventHorizon"/> class.
 /// </summary>
 /// <param name="id">The <see cref="EventHorizonId"/>.</param>
 /// <param name="consent">The <see cref="ConsentId"/>.</param>
 /// <param name="currentPosition">The initial current <see cref="StreamPosition"/> of the event horizon.</param>
 /// <param name="dispatcher">The reverse call dispatcher.</param>
 /// <param name="executionContexts">The <see cref="ICreateExecutionContexts"/>.</param>
 /// <param name="getEventFetchers">The <see cref="Func{TResult}"/> callback for getting <see cref="IEventFetchers"/> in a tenant container.</param>
 /// <param name="getStreamWaiter">The <see cref="Func{TResult}"/> callback for getting <see cref="IStreamEventWatcher"/> in a tenant container</param>
 /// <param name="metrics">The <see cref="IMetricsCollector"/>.</param>
 /// <param name="logger">The <see cref="ILogger"/>.</param>
 /// <param name="cancellationToken">The cancellation token.</param>
 public EventHorizon(
     EventHorizonId id,
     ConsentId consent,
     StreamPosition currentPosition,
     IReverseCallDispatcher <EventHorizonConsumerToProducerMessage, EventHorizonProducerToConsumerMessage, ConsumerSubscriptionRequest, SubscriptionResponse, ConsumerRequest, ConsumerResponse> dispatcher,
     ICreateExecutionContexts executionContexts,
     Func <TenantId, IEventFetchers> getEventFetchers,
     Func <TenantId, IStreamEventWatcher> getStreamWaiter,
     IMetricsCollector metrics,
     ILogger logger,
     CancellationToken cancellationToken)
 {
     Consent            = consent;
     _dispatcher        = dispatcher;
     _executionContexts = executionContexts;
     _getEventFetchers  = getEventFetchers;
     _getStreamWaiter   = getStreamWaiter;
     _metrics           = metrics;
     _logger            = logger;
     Id = id;
     CurrentPosition          = currentPosition;
     _cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
 }
示例#14
0
 Task WriteFailedRegistrationResponse(
     IReverseCallDispatcher <EventHandlerClientToRuntimeMessage, EventHandlerRuntimeToClientMessage, EventHandlerRegistrationRequest, EventHandlerRegistrationResponse, HandleEventRequest, EventHandlerResponse> dispatcher,
     Failure failure,
     CancellationToken cancellationToken) => dispatcher.Reject(new EventHandlerRegistrationResponse {
     Failure = failure
 }, cancellationToken);
示例#15
0
        async Task RegisterFilter <TFilterDefinition, TClientMessage, TConnectRequest, TResponse>(
            IReverseCallDispatcher <TClientMessage, FilterRuntimeToClientMessage, TConnectRequest, FilterRegistrationResponse, FilterEventRequest, TResponse> dispatcher,
            ScopeId scopeId,
            TFilterDefinition filterDefinition,
            Func <IFilterProcessor <TFilterDefinition> > getFilterProcessor,
            CancellationToken externalCancellationToken)
            where TFilterDefinition : IFilterDefinition
            where TClientMessage : IMessage, new()
            where TConnectRequest : class
            where TResponse : class
        {
            _logger.Trace("Received Source Stream '{SourceStream}'", filterDefinition.SourceStream);
            _logger.Trace("Received Filter '{Filter}'", filterDefinition.TargetStream);
            _logger.Trace("Received Scope '{Scope}'", scopeId);
            using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(externalCancellationToken);
            var cancellationToken = linkedTokenSource.Token;

            _logger.Debug("Connecting Filter '{Filter}'", filterDefinition.TargetStream);

            var tryRegisterFilter = TryRegisterStreamProcessor(scopeId, filterDefinition, getFilterProcessor, cancellationToken);

            if (!tryRegisterFilter.Success)
            {
                linkedTokenSource.Cancel();
                if (tryRegisterFilter.HasException)
                {
                    var exception = tryRegisterFilter.Exception;
                    _logger.Warning(exception, "An error occurred while registering Filter '{Filter}'", filterDefinition.TargetStream);
                    ExceptionDispatchInfo.Capture(exception).Throw();
                }
                else
                {
                    _logger.Warning("Failed to register Filter '{Filter}'. Filter already registered", filterDefinition.TargetStream);
                    var failure = new Failure(
                        FiltersFailures.FailedToRegisterFilter,
                        $"Failed to register Filter '{filterDefinition.TargetStream}'. Filter already registered.");
                    await WriteFailedRegistrationResponse(dispatcher, failure, externalCancellationToken).ConfigureAwait(false);

                    return;
                }
            }

            using var filterStreamProcessor = tryRegisterFilter.Result;
            var tryStartFilter = await TryStartFilter(
                dispatcher,
                filterStreamProcessor,
                scopeId,
                filterDefinition,
                getFilterProcessor,
                cancellationToken).ConfigureAwait(false);

            if (!tryStartFilter.Success)
            {
                linkedTokenSource.Cancel();
                if (tryStartFilter.HasException)
                {
                    var exception = tryStartFilter.Exception;
                    _logger.Warning(exception, "An error occurred while starting Filter '{Filter}' in Scope '{Scope}'", filterDefinition.TargetStream, scopeId);
                    ExceptionDispatchInfo.Capture(exception).Throw();
                }
                else
                {
                    _logger.Warning("Could not start Filter '{Filter}' in Scope '{Scope}'", filterDefinition.TargetStream, scopeId);
                    return;
                }
            }

            var tasks = tryStartFilter.Result;

            try
            {
                await Task.WhenAny(tasks).ConfigureAwait(false);

                if (TryGetException(tasks, out var ex))
                {
                    _logger.Warning(ex, "An error occurred while running Filter '{Filter}' in Scope '{Scope}'", filterDefinition.TargetStream, scopeId);
                    await Task.WhenAll(tasks).ConfigureAwait(false);

                    ExceptionDispatchInfo.Capture(ex).Throw();
                }
            }
            finally
            {
                linkedTokenSource.Cancel();
                await Task.WhenAll(tasks).ConfigureAwait(false);

                _logger.Debug("Filter '{Filter}' in Scope '{Scope}' stopped", filterDefinition.TargetStream, scopeId);
            }
        }
示例#16
0
    async Task RegisterFilter <TFilterDefinition, TClientMessage, TConnectRequest, TResponse>(
        IReverseCallDispatcher <TClientMessage, FilterRuntimeToClientMessage, TConnectRequest, FilterRegistrationResponse, FilterEventRequest, TResponse> dispatcher,
        ScopeId scopeId,
        TFilterDefinition filterDefinition,
        Func <TenantId, IFilterProcessor <TFilterDefinition> > getFilterProcessor,
        ExecutionContext executionContext,
        CancellationToken externalCancellationToken)
        where TFilterDefinition : IFilterDefinition
        where TClientMessage : IMessage, new()
        where TConnectRequest : class
        where TResponse : class
    {
        _logger.ReceivedFilter(filterDefinition.SourceStream, filterDefinition.TargetStream, scopeId);
        using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(externalCancellationToken);
        var cancellationToken = linkedTokenSource.Token;

        _logger.ConnectingFilter(filterDefinition.TargetStream);

        var tryRegisterFilter = TryRegisterStreamProcessor(scopeId, filterDefinition, getFilterProcessor, executionContext, cancellationToken);

        if (!tryRegisterFilter.Success)
        {
            linkedTokenSource.Cancel();

            if (tryRegisterFilter.Exception is StreamProcessorAlreadyRegistered)
            {
                _logger.FilterAlreadyRegistered(filterDefinition.TargetStream);
                var failure = new Failure(
                    FiltersFailures.FailedToRegisterFilter,
                    $"Failed to register Filter '{filterDefinition.TargetStream}'. Filter already registered.");
                await WriteFailedRegistrationResponse(dispatcher, failure, externalCancellationToken).ConfigureAwait(false);

                return;
            }
            else
            {
                var exception = tryRegisterFilter.Exception;
                _logger.ErrorWhileRegisteringFilter(exception, filterDefinition.TargetStream);
                ExceptionDispatchInfo.Capture(exception).Throw();
            }
        }

        using var filterStreamProcessor = tryRegisterFilter.Result;
        var tryStartFilter = await TryStartFilter(
            dispatcher,
            filterStreamProcessor,
            scopeId,
            filterDefinition,
            getFilterProcessor,
            cancellationToken).ConfigureAwait(false);

        if (!tryStartFilter.Success)
        {
            linkedTokenSource.Cancel();
            var exception = tryStartFilter.Exception;
            _logger.ErrorWhileStartingFilter(exception, filterDefinition.TargetStream, scopeId);
            ExceptionDispatchInfo.Capture(exception).Throw();
        }

        var tasks = new TaskGroup(tryStartFilter.Result);

        tasks.OnFirstTaskFailure  += (_, ex) => _logger.ErrorWhileRunningFilter(ex, filterDefinition.TargetStream, scopeId);
        tasks.OnAllTasksCompleted += () => _logger.FilterStopped(filterDefinition.TargetStream, scopeId);

        await tasks.WaitForAllCancellingOnFirst(linkedTokenSource).ConfigureAwait(false);
    }