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); }
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); }
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); }
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); }
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++; } } }