public async Task <ResolutionEligibility> IsStreamEventEligibleForResolution(StreamEvent streamEvent, long firstPositionInStream) { var streamId = streamEvent.IsLink ? streamEvent.Link.StreamId : streamEvent.StreamId; var position = streamEvent.IsLink ? streamEvent.Link.Position : streamEvent.Position; var streamState = await _streamStateRepo.LoadStreamStateAsync(streamId); long expectedPosition = firstPositionInStream; if (streamState != null) { if (streamState.HasError) { return(ResolutionEligibility.UnableStreamHasError); } if (position <= streamState.LastAttemptedPosition) { return(ResolutionEligibility.SkippedAlreadyProcessed); } expectedPosition = streamState.LastAttemptedPosition + 1; } // Sanity check to ensure events arrive sequentially for a given stream. if (position != expectedPosition) { throw new InvalidOperationException($"Expected sequential event position {expectedPosition} from stream {streamEvent.StreamId} but received {streamEvent.Position}. Unable to continue."); } return(ResolutionEligibility.Eligible); }
// Not thread safe. public async Task ManageAsync(CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { while (_handlingQueue.IsEventsAvailable) { _awaiter.ResetHandlerCompletionSignal(); // Clean up finished tasks. _handlerTasks.PurgeFinishedTasks(); // Get the next event from the queue that isn't in a parallel group already running. var item = _handlingQueue.TryDequeue(_handlerTasks.Keys); if (item != null) { // When a stream has an error there may already be subscriber events in the // handling queue that have made it past the first check for errored stream state // prior to deserialization. If that's the case we just ignore the event. var state = await _streamStateRepo.LoadStreamStateAsync(item.SubscriberEvent.StreamId); if (state == null || !state.HasError) { await Task.WhenAny(new[] { _awaiter.AwaitThrottleAsync(), cancellationToken.WaitHandle.AsTask() }); if (!cancellationToken.IsCancellationRequested) { _handlerTasks.Add(item.ParallelKey, _handlerRunner.TryRunHandlerAsync(item.SubscriberEvent, cancellationToken)); } } } else { // Events are available but none in a parallel group that is not already executing. // Wait for a new event to arrive or for an event handler to complete. await Task.WhenAny(new Task[] { _awaiter.AwaitHandlerCompletionSignalAsync(), _handlingQueue.AwaitEnqueueSignalAsync(), cancellationToken.WaitHandle.AsTask() }); } if (cancellationToken.IsCancellationRequested) { break; } } if (cancellationToken.IsCancellationRequested) { break; } await Task.WhenAny(new Task[] { _handlingQueue.AwaitEnqueueSignalAsync(), cancellationToken.WaitHandle.AsTask() }); } } catch (Exception ex) { _logger.LogError(ex, "Exception while managing handler execution."); throw; } }