Beispiel #1
0
        private async Task <List <ExtractedEvent> > FetchEventTimeline(PollerContext ctx, List <PollerState> pollerStates)
        {
            var tasks = new List <Task <List <ExtractedEvent> > >(Sources.Length);

            foreach (var source in Sources)
            {
                var minSequenceId = GetMinSequenceIdFor(ctx.ContractsRegistry, pollerStates, source);
                ctx.Logger.Trace("Min sequence id for {0} is {1}", _pollerContractName, minSequenceId.ToString());

                var concreteSubscriptionIdentifier = _contractSubscriptions.Count > 0 && _contractSubscriptions.ContainsKey(source)
                    ? _contractSubscriptions[source]
                    : (int?)null;

                tasks.Add(source.FetchAsync(
                              ctx.ContractsRegistry,
                              ctx.UpConverterFactory,
                              minSequenceId,
                              concreteSubscriptionIdentifier));
            }

            var events = await Task.WhenAll(tasks.ToArray());

            var merged = Merge(events);

            ctx.Logger.Trace("{0} fetched {1} events from {2} stream sources.", _pollerContractName, merged.Count.ToString(), Sources.Length.ToString());
            return(merged);
        }
Beispiel #2
0
        public Runner(IContractsRegistry contractsRegistry, ILogger logger, IPollerStateRepository stateRepository, SubscriptionPoller poller, IUpConverterFactory upConverterFactory)
        {
            if (poller == null)
            {
                throw new ArgumentNullException(nameof(poller));
            }
            Poller = poller;

            _pollerContext = new PollerContext(contractsRegistry, logger, stateRepository, upConverterFactory);
            _startedAt     = new InterlockedDateTime(DateTime.MaxValue);
            _timeoutCalc   = new PollerTimeoutCalculator(Poller.GetFetchTimeout());
            _runnerTimer   = CreateTimer(_timeoutCalc);
        }
Beispiel #3
0
        private async Task <bool> TryDispatch(PollerContext ctx, HandlerRegistrar.HandlerTypeInfo handlerInfo, EventEnvelope envelope, PollerState state)
        {
            var eventType      = envelope.Event.GetType();
            var shouldDispatch = handlerInfo.ContainsEventType(eventType);

            if (shouldDispatch)
            {
                PreHandleEvent(envelope, handlerInfo.HandlerType);
            }

            using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, _transactionOptions, TransactionScopeAsyncFlowOption.Enabled))
            {
                if (shouldDispatch)
                {
                    var handlerInstance = CreateHandlerInstance(handlerInfo.HandlerType);
                    if (handlerInstance == null)
                    {
                        throw new NullReferenceException($"Handler instance {handlerInfo.HandlerType.FullName} is null.");
                    }
                    ctx.Logger.Trace("Dispatching event {0} to {1}...", eventType.FullName, handlerInfo.HandlerType.FullName);
                    if (handlerInfo.IsAsync)
                    {
                        await((dynamic)handlerInstance).Handle((dynamic)envelope.Event, envelope);
                    }
                    else
                    {
                        ((dynamic)handlerInstance).Handle((dynamic)envelope.Event, envelope);
                    }
                }

                state.EventSequenceId = envelope.SequenceId;

                if (shouldDispatch)
                {
                    await ctx.StateRepository.InsertOrUpdateAsync(state);

                    state.Clear();
                    ctx.Logger.Trace("Dispatched event {0} by {1} saved with sequence {2}.", eventType.FullName, handlerInfo.HandlerType.FullName, state.EventSequenceId);
                }
                else
                {
                    ctx.Logger.Trace("Dispatched event {0} by {1} marked as dirty with sequence {2}.", eventType.FullName, handlerInfo.HandlerType.FullName, state.EventSequenceId);
                }
                scope.Complete();
            }
            if (shouldDispatch)
            {
                PostHandleEvent(envelope, handlerInfo.HandlerType);
            }
            return(shouldDispatch);
        }
Beispiel #4
0
        private async Task SaveDirtyStates(PollerContext ctx, List <PollerState> pollerStates)
        {
            var dirtyStates = pollerStates.Where(x => x.IsDirty).ToArray();

            if (dirtyStates.Length == 0)
            {
                return;
            }

            ctx.Logger.Trace("Saving dirty states {0} for {1}...", dirtyStates.Length, _pollerContractName);

            using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, _transactionOptions, TransactionScopeAsyncFlowOption.Enabled))
            {
                foreach (var state in dirtyStates)
                {
                    await ctx.StateRepository.InsertOrUpdateAsync(state);
                }
                scope.Complete();
            }
            ctx.Logger.Trace("Dirty states for {0} saved.", _pollerContractName);
        }
Beispiel #5
0
        internal async Task <bool> Execute(PollerContext ctx, CancellationToken cancellationToken = default(CancellationToken))
        {
            var anyDispatched          = false;
            var executionRetryAttempts = 0;

            while (true)
            {
                try
                {
                    var pollerStates = new List <PollerState>(await ctx.StateRepository.LoadAsync(_pollerContractName, cancellationToken));
                    var timeline     = await FetchEventTimeline(ctx, pollerStates);

                    PreExecuting(timeline.Count);

                    var typesListOfDispatchedHandlers = new List <Type>(_handlerRegistrar.RegisteredHandlerInfos.Length);
                    foreach (var item in timeline)
                    {
                        var sourceContractName = ctx.ContractsRegistry.GetContractName(item.SourceType);
                        foreach (var handlerInfo in _handlerRegistrar.RegisteredHandlerInfos) // all handlers can/should run in parallel
                        {
                            var state = FindOrCreateState(ctx.ContractsRegistry, pollerStates, sourceContractName, handlerInfo.HandlerType);
                            if (item.Envelope.SequenceId <= state.EventSequenceId)
                            {
                                continue;
                            }

                            var  handlingRetryAttempts = 0;
                            bool dispatched;
                            while (true)
                            {
                                try
                                {
                                    dispatched = await TryDispatch(ctx, handlerInfo, item.Envelope, state);

                                    break;
                                }
                                catch (Exception ex)
                                {
                                    PostHandleEventError(item.Envelope, handlerInfo.HandlerType, ex, handlingRetryAttempts);
                                    if (handlingRetryAttempts >= RetriesPolicy.HandlerAttemptsThreshold)
                                    {
                                        throw;
                                    }
                                    handlingRetryAttempts++;
                                }
                            }

                            if (dispatched && !typesListOfDispatchedHandlers.Contains(handlerInfo.HandlerType))
                            {
                                typesListOfDispatchedHandlers.Add(handlerInfo.HandlerType);
                            }

                            anyDispatched |= dispatched;
                        }
                    }

                    await SaveDirtyStates(ctx, pollerStates);

                    PostExecuting(timeline.Count, typesListOfDispatchedHandlers.ToArray());

                    return(pollerStates.Max(x => x.EventSequenceId) != pollerStates.Min(x => x.EventSequenceId) || anyDispatched);
                }
                catch (Exception e)
                {
                    if (executionRetryAttempts >= RetriesPolicy.FetchAttemptsThreshold)
                    {
                        throw new FetchAttemptsThresholdException(GetType().Name, executionRetryAttempts, e);
                    }
                    executionRetryAttempts++;
                }
            }
        }