예제 #1
0
        /// <inheritdoc/>
        public bool TryGetConsentFor(SubscriptionId subscriptionId, out ConsentId consentId)
        {
            var result = _subscriptions.TryGetValue(subscriptionId, out var subscription);

            consentId = subscription?.ConsentId;
            return(result);
        }
예제 #2
0
        /// <inheritdoc />
        public bool TrySubscribe(
            ConsentId consentId,
            SubscriptionId subscriptionId,
            EventProcessor eventProcessor,
            EventsFromEventHorizonFetcher eventsFetcher,
            CancellationToken cancellationToken,
            out Subscription subscription)
        {
            subscription = default;
            if (_subscriptions.ContainsKey(subscriptionId))
            {
                _logger.Warning("Subscription: '{SubscriptionId}' already registered", subscriptionId);
                return(false);
            }

            subscription = new Subscription(
                consentId,
                subscriptionId,
                eventProcessor,
                eventsFetcher,
                _streamProcessorStates,
                () => Unregister(subscriptionId),
                _eventsFetcherPolicy,
                _loggerManager,
                cancellationToken);
            if (!_subscriptions.TryAdd(subscriptionId, subscription))
            {
                _logger.Warning("Subscription: '{SubscriptionId}' already registered", subscriptionId);
                subscription = default;
                return(false);
            }

            _logger.Trace("Subscription: '{SubscriptionId}' registered", subscriptionId);
            return(true);
        }
예제 #3
0
 /// <inheritdoc/>
 public async Task Write(CommittedEvent @event, ConsentId consentId, ScopeId scope, CancellationToken cancellationToken)
 {
     _logger.Trace(
         "Writing Event Horizon Event: {EventLogSequenceNumber} from Tenant: {Tenant} in Microservice {Microservice} to Scope: {Scope}",
         @event.EventLogSequenceNumber,
         @event.ExecutionContext.Tenant,
         @event.ExecutionContext.Microservice,
         scope);
     await _eventsToStreamsWriter.Write(
         await _streams.GetEventLog(scope, cancellationToken).ConfigureAwait(false),
         _eventFilter,
         streamPosition => _eventConverter.ToEventLogEvent(
             new CommittedExternalEvent(
                 streamPosition.Value,
                 @event.Occurred,
                 @event.EventSource,
                 @event.ExecutionContext,
                 @event.Type,
                 false,
                 @event.Content,
                 @event.EventLogSequenceNumber,
                 DateTimeOffset.UtcNow,
                 consentId)),
         cancellationToken).ConfigureAwait(false);
 }
예제 #4
0
 void StartProcessingEventHorizon(
     ConsentId consentId,
     SubscriptionId subscriptionId,
     MicroserviceAddress microserviceAddress,
     IReverseCallClient <EventHorizonConsumerToProducerMessage, EventHorizonProducerToConsumerMessage, ConsumerSubscriptionRequest, Contracts.SubscriptionResponse, ConsumerRequest, ConsumerResponse> reverseCallClient)
 {
     Task.Run(async() =>
     {
         try
         {
             await ReadEventsFromEventHorizon(consentId, subscriptionId, reverseCallClient).ConfigureAwait(false);
             throw new Todo();     // TODO: This is a hack to get the policy going. Remove this when we can have policies on return values
         }
         catch (Exception ex)
         {
             _logger.Debug(ex, "Reconnecting to event horizon with subscription {subscriptionId}", subscriptionId);
             await _policy.Execute(
                 async _ =>
             {
                 reverseCallClient    = CreateClient(microserviceAddress, _cancellationToken);
                 var receivedResponse = await Subscribe(reverseCallClient, subscriptionId, microserviceAddress, _cancellationToken).ConfigureAwait(false);
                 var response         = HandleSubscriptionResponse(receivedResponse, reverseCallClient.ConnectResponse, subscriptionId);
                 if (!response.Success)
                 {
                     throw new Todo();                            // TODO: This is a hack to get the policy going. Remove this when we can have policies on return values
                 }
                 await ReadEventsFromEventHorizon(response.ConsentId, subscriptionId, reverseCallClient).ConfigureAwait(false);
                 throw new Todo();         // TODO: This is a hack to get the policy going. Remove this when we can have policies on return values
             }, _cancellationToken).ConfigureAwait(false);
         }
     });
 }
예제 #5
0
    /// <inheritdoc/>
    public async Task <EventLogSequenceNumber> Write(CommittedEvent @event, ConsentId consentId, ScopeId scope, CancellationToken cancellationToken)
    {
        _logger.WritingEventHorizonEvent(
            @event.EventLogSequenceNumber,
            @event.ExecutionContext.Tenant,
            @event.ExecutionContext.Microservice,
            scope);
        var writtenStreamPosition = await _eventsToStreamsWriter.Write(
            await _streams.GetEventLog(scope, cancellationToken).ConfigureAwait(false),
            _eventFilter,
            streamPosition => _eventConverter.ToEventLogEvent(
                new CommittedExternalEvent(
                    streamPosition.Value,
                    @event.Occurred,
                    @event.EventSource,
                    @event.ExecutionContext,
                    @event.Type,
                    false,
                    @event.Content,
                    @event.EventLogSequenceNumber,
                    DateTimeOffset.UtcNow,
                    consentId)),
            cancellationToken).ConfigureAwait(false);

        _streamWatcher.NotifyForEvent(scope, StreamId.EventLog, writtenStreamPosition);
        return(writtenStreamPosition.Value);
    }
예제 #6
0
        async Task ReadEventsFromEventHorizon(
            ConsentId consentId,
            SubscriptionId subscriptionId,
            IReverseCallClient <EventHorizonConsumerToProducerMessage, EventHorizonProducerToConsumerMessage, ConsumerSubscriptionRequest, Contracts.SubscriptionResponse, ConsumerRequest, ConsumerResponse> reverseCallClient)
        {
            _logger.Information("Successfully connected event horizon with {subscriptionId}. Waiting for events to process", subscriptionId);
            var queue         = new AsyncProducerConsumerQueue <StreamEvent>();
            var eventsFetcher = new EventsFromEventHorizonFetcher(queue);

            using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken);
            var tasks = new List <Task>();

            try
            {
                _subscriptions.TrySubscribe(
                    consentId,
                    subscriptionId,
                    new EventProcessor(consentId, subscriptionId, _eventHorizonEventsWriter, _eventProcessorPolicy, _logger),
                    eventsFetcher,
                    linkedTokenSource.Token,
                    out var outputtedStreamProcessor);
                using var streamProcessor = outputtedStreamProcessor;
                await streamProcessor.Initialize().ConfigureAwait(false);

                tasks.Add(Task.Run(async() =>
                {
                    await reverseCallClient.Handle(
                        (request, cancellationToken) => HandleConsumerRequest(subscriptionId, queue, request, cancellationToken),
                        linkedTokenSource.Token).ConfigureAwait(false);
                    linkedTokenSource.Cancel();
                }));
                tasks.Add(streamProcessor.Start());
            }
            catch (Exception ex)
            {
                linkedTokenSource.Cancel();
                _logger.Warning(ex, "Error occurred while initializing Subscription: {subscriptionId}", subscriptionId);
                return;
            }

            var finishedTask = await Task.WhenAny(tasks).ConfigureAwait(false);

            if (!linkedTokenSource.IsCancellationRequested)
            {
                linkedTokenSource.Cancel();
            }
            if (TryGetException(tasks, out var exception))
            {
                linkedTokenSource.Cancel();
                _logger.Warning(exception, "Error occurred while processing Subscription: {subscriptionId}", subscriptionId);
            }

            await Task.WhenAll(tasks).ConfigureAwait(false);
        }
예제 #7
0
 /// <summary>
 /// Initializes a new instance of the <see cref="EventProcessor"/> class.
 /// </summary>
 /// <param name="consentId">THe <see cref="ConsentId" />.</param>
 /// <param name="subscription">The <see cref="Subscription" />.</param>
 /// <param name="externalEventsCommitter">The <see cref="ICommitExternalEvents"/>.</param>
 /// <param name="metrics">The system for collecting metrics.</param>
 /// <param name="logger">The <see cref="ILogger" />.</param>
 public EventProcessor(
     ConsentId consentId,
     SubscriptionId subscription,
     ICommitExternalEvents externalEventsCommitter,
     IMetricsCollector metrics,
     ILogger logger)
 {
     _consentId               = consentId;
     Scope                    = subscription.ScopeId;
     Identifier               = subscription.ProducerTenantId.Value;
     _subscriptionId          = subscription;
     _externalEventsCommitter = externalEventsCommitter;
     _metrics                 = metrics;
     _logger                  = logger;
 }
예제 #8
0
 /// <summary>
 /// Initializes a new instance of the <see cref="EventProcessor"/> class.
 /// </summary>
 /// <param name="consentId">THe <see cref="ConsentId" />.</param>
 /// <param name="subscription">The <see cref="Subscription" />.</param>
 /// <param name="receivedEventsWriter">The <see cref="IWriteEventHorizonEvents" />.</param>
 /// <param name="policy">The <see cref="IAsyncPolicyFor{T}" /> <see cref="EventProcessor" />.</param>
 /// <param name="logger">The <see cref="ILogger" />.</param>
 public EventProcessor(
     ConsentId consentId,
     SubscriptionId subscription,
     IWriteEventHorizonEvents receivedEventsWriter,
     IAsyncPolicyFor <EventProcessor> policy,
     ILogger logger)
 {
     _consentId            = consentId;
     Scope                 = subscription.ScopeId;
     Identifier            = subscription.ProducerTenantId.Value;
     _subscriptionId       = subscription;
     _receivedEventsWriter = receivedEventsWriter;
     _policy               = policy;
     _logger               = logger;
 }
예제 #9
0
 /// <summary>
 /// Initializes a new instance of the <see cref="CommittedExternalEvent"/> class.
 /// </summary>
 /// <param name="eventLogSequenceNumber">The <see cref="EventLogSequenceNumber" />.</param>
 /// <param name="occurred">The <see cref="DateTimeOffset" /> of when the Event was committed.</param>
 /// <param name="eventSource">The <see cref="EventSourceId" />.</param>
 /// <param name="executionContext">THe <see cref="ExecutionContext" />.</param>
 /// <param name="type">The <see cref="Artifact" />.</param>
 /// <param name="isPublic">Whether the event is public.</param>
 /// <param name="content">The Event content.</param>
 /// <param name="externalEventLogSequenceNumber">The external <see cref="EventLogSequenceNumber" />.</param>
 /// <param name="received">The <see cref="DateTimeOffset" /> that this external Event was received.</param>
 /// <param name="consent">The <see cref="Consent" />.</param>
 public CommittedExternalEvent(
     EventLogSequenceNumber eventLogSequenceNumber,
     DateTimeOffset occurred,
     EventSourceId eventSource,
     ExecutionContext executionContext,
     Artifact type,
     bool isPublic,
     string content,
     EventLogSequenceNumber externalEventLogSequenceNumber,
     DateTimeOffset received,
     ConsentId consent)
     : base(eventLogSequenceNumber, occurred, eventSource, executionContext, type, isPublic, content)
 {
     ExternalEventLogSequenceNumber = externalEventLogSequenceNumber;
     Received = received;
     Consent  = consent;
 }
예제 #10
0
 /// <inheritdoc/>
 public IStreamProcessor Create(
     ConsentId consent,
     SubscriptionId subscription,
     ExecutionContext executionContext,
     EventsFromEventHorizonFetcher eventsFromEventHorizonFetcher)
 => new StreamProcessor(
     subscription,
     executionContext,
     new EventProcessor(
         consent,
         subscription,
         _externalEventsCommitter,
         _metrics,
         _loggerFactory.CreateLogger <EventProcessor>()),
     eventsFromEventHorizonFetcher,
     _streamProcessorStates,
     _eventsFetcherPolicy,
     _metrics,
     _loggerFactory);
예제 #11
0
 bool TryGetConsent(ConsumerSubscriptionArguments arguments, out ConsentId consent, out SubscriptionResponse failureResponse)
 {
     consent = default;
     try
     {
         return(TryGetEventHorizonsConfiguration(arguments, out var eventHorizonsConfiguration, out failureResponse) &&
                TryGetConsentFromConfiguration(arguments, eventHorizonsConfiguration, out consent, out failureResponse));
     }
     catch (Exception ex)
     {
         Log.ErrorCreatingSubscriptionResponse(_logger, ex);
         failureResponse = new SubscriptionResponse
         {
             Failure = new ProtobufContracts.Failure
             {
                 Id     = FailureId.Other.ToProtobuf(),
                 Reason = "Error occurred while creating subscription response"
             }
         };
         return(false);
     }
 }
예제 #12
0
    /// <inheritdoc />
    public async Task Commit(CommittedEvents events, ConsentId consent, ScopeId scope)
    {
        foreach (var @event in events)
        {
            var sequenceNumber = await _policies.WriteEvent.ExecuteAsync(
                _ => _receivedEventsWriter.Write(@event, consent, scope, CancellationToken.None),
                CancellationToken.None).ConfigureAwait(false);

            await _eventStoreClient.CommitExternal(new CommitExternalEventsRequest
            {
                ScopeId = scope.ToProtobuf(),
                Event   = new CommittedEvent(
                    sequenceNumber,
                    @event.Occurred,
                    @event.EventSource,
                    @event.ExecutionContext,
                    @event.Type,
                    @event.Public,
                    @event.Content).ToProtobuf()
            }, CancellationToken.None).ConfigureAwait(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
        /// <summary>
        /// Initializes a new instance of the <see cref="Subscription"/> class.
        /// </summary>
        /// <param name="consentId">The <see cref="ConsentId" />.</param>
        /// <param name="subscriptionId">The <see cref="StreamProcessorId" />.</param>
        /// <param name="eventProcessor">The <see cref="IEventProcessor" />.</param>
        /// <param name="eventsFetcher">The <see cref="EventsFromEventHorizonFetcher" />.</param>
        /// <param name="streamProcessorStates">The <see cref="IResilientStreamProcessorStateRepository" />.</param>
        /// <param name="unregister">An <see cref="Action" /> that unregisters the <see cref="ScopedStreamProcessor" />.</param>
        /// <param name="eventsFetcherPolicy">The <see cref="IAsyncPolicyFor{T}" /> <see cref="ICanFetchEventsFromStream" />.</param>
        /// <param name="loggerManager">The <see cref="ILoggerManager" />.</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken" />.</param>
        public Subscription(
            ConsentId consentId,
            SubscriptionId subscriptionId,
            EventProcessor eventProcessor,
            EventsFromEventHorizonFetcher eventsFetcher,
            IResilientStreamProcessorStateRepository streamProcessorStates,
            Action unregister,
            IAsyncPolicyFor <ICanFetchEventsFromStream> eventsFetcherPolicy,
            ILoggerManager loggerManager,
            CancellationToken cancellationToken)
        {
            _identifier            = subscriptionId;
            _eventProcessor        = eventProcessor;
            _unregister            = unregister;
            _streamProcessorStates = streamProcessorStates;
            _eventsFetcher         = eventsFetcher;
            _eventsFetcherPolicy   = eventsFetcherPolicy;
            _loggerManager         = loggerManager;
            _logger                      = loggerManager.CreateLogger <StreamProcessor>();
            _cancellationToken           = cancellationToken;
            _unregisterTokenRegistration = _cancellationToken.Register(_unregister);

            ConsentId = consentId;
        }
예제 #15
0
    bool TryGetConsentFromConfiguration(
        ConsumerSubscriptionArguments arguments,
        EventHorizonsPerMicroserviceConfiguration eventHorizonsConfiguration,
        out ConsentId consentId,
        out SubscriptionResponse failureResponse)
    {
        consentId       = default;
        failureResponse = default;
        Log.CheckingConsents(
            _logger,
            arguments.Partition,
            arguments.PublicStream,
            arguments.ProducerTenant,
            arguments.ConsumerTenant,
            arguments.ConsumerMicroservice);

        if (!eventHorizonsConfiguration.TryGetValue(arguments.ConsumerMicroservice, out var eventHorizonConfiguration))
        {
            failureResponse = CreateNoConsentsConfiguredResponse(
                arguments,
                $"There are no consents configured for Consumer Microservice {arguments.ConsumerMicroservice}");
            return(false);
        }
        foreach (var consent in eventHorizonConfiguration.Consents)
        {
            if (consent.ConsumerTenant == arguments.ConsumerTenant.Value && consent.Stream == arguments.PublicStream.Value && consent.Partition == arguments.Partition.Value)
            {
                consentId = consent.Consent;
                return(true);
            }
        }
        failureResponse = CreateNoConsentsConfiguredResponse(
            arguments,
            $"There are no consents configured for Consumer Tenant {arguments.ConsumerTenant} from Public Stream {arguments.PublicStream} and Partition {arguments.Partition}");
        return(false);
    }
예제 #16
0
 internal static void SubscriptionIsReceivingAndWriting(this ILogger logger, SubscriptionId subscriptionId, ConsentId consentId)
 => _subscriptionIsReceivingAndWriting(logger, consentId, subscriptionId, null);
예제 #17
0
        bool TryGetConsentFor(Microservice consumerMicroservice, TenantId consumerTenant, TenantId producerTenant, StreamId publicStream, PartitionId partition, out ConsentId consentId)
        {
            consentId = null;
            _logger.Trace(
                "Checking consents configured for Partition: {Partition} in Public Stream {PublicStream} in Tenant {ProducerTenant} to Consumer Tenant {ConsumerTenant} in Microservice {ConsumerMicroservice}",
                partition,
                publicStream,
                producerTenant,
                consumerTenant,
                consumerMicroservice);

            var consentsForSubscription = _eventHorizonConsents
                                          .GetConsentConfigurationsFor(producerTenant)
                                          .Where(_ => _.Microservice == consumerMicroservice && _.Tenant == consumerTenant && _.Stream == publicStream && _.Partition == partition).ToArray();

            if (consentsForSubscription.Length == 0)
            {
                _logger.Debug(
                    "There are no consent configured for partition '{Partition}' in public stream '{PublicStream}' in tenant '{ProducerTenant}' to consumer tenant '{ConsumerTenant}' in microservice '{ConsumerMicroservice}'",
                    partition,
                    publicStream,
                    producerTenant,
                    consumerTenant,
                    consumerMicroservice);
                return(false);
            }

            if (consentsForSubscription.Length > 1)
            {
                _logger.Warning(
                    "There are multiple consents configured for Partition {Partition} in Public Stream {PublicStream} in Tenant {ProducerTenant} to Consumer Tenant {ConsumerTenant} in Microservice {ConsumerMicroservice}",
                    partition,
                    publicStream,
                    producerTenant,
                    consumerTenant,
                    consumerMicroservice);
            }

            consentId = consentsForSubscription.SingleOrDefault()?.Consent;
            return(consentId != null);
        }
예제 #18
0
 /// <summary>
 /// Initializes a new instance of the <see cref="SuccessfulSubscriptionResponse"/> class.
 /// </summary>
 /// <param name="consentId">The <see cref="ConsentId" />.</param>
 public SuccessfulSubscriptionResponse(ConsentId consentId)
     : base(consentId)
 {
 }
예제 #19
0
 /// <summary>
 /// Initializes a new instance of the <see cref="SubscriptionResponse"/> class.
 /// </summary>
 /// <param name="consentId">The <see cref="ConsentId" />.</param>
 SubscriptionResponse(ConsentId consentId)
 {
     Success   = true;
     ConsentId = consentId;
 }
예제 #20
0
 internal static void SubsciptionFailedWhileReceivingAndWriting(this ILogger logger, SubscriptionId subscriptionId, ConsentId consentId, Exception exception)
 => _subsciptionFailedWhileReceivingAndWriting(logger, consentId, subscriptionId, exception);