private static void MonitorProcessingTaskPrematureCompletion(Task processingTask, ISequence sequence) { if (sequence.ParentSequence != null) { return; } Task.Run( async() => { try { await processingTask.ConfigureAwait(false); // Abort only parent sequences and don't consider the enumeration as aborted if the // sequence is actually complete if (sequence.ParentSequence != null || sequence.IsComplete) { return; } // Call AbortAsync to abort the uncompleted sequence, to avoid unreleased locks. // The reason behind this call here may be counterintuitive but with // SequenceAbortReason.EnumerationAborted a commit is in fact performed. await sequence.AbortAsync(SequenceAbortReason.EnumerationAborted) .ConfigureAwait(false); } catch (Exception exception) { if (!sequence.IsPending || sequence.ParentSequence != null) { return; } await sequence.AbortAsync(SequenceAbortReason.Error, exception) .ConfigureAwait(false); } finally { if (sequence is ISequenceImplementation sequenceImplementation && sequenceImplementation.ShouldCreateNewActivity) { sequenceImplementation.Activity?.Stop(); } } }); }
private async Task <Task> PublishStreamProviderAsync(ISequence sequence, ConsumerPipelineContext context) { _logger.LogTraceWithMessageInfo( IntegrationEventIds.LowLevelTracing, $"Publishing {sequence.GetType().Name} '{sequence.SequenceId}'...", context); var publisher = context.ServiceProvider.GetRequiredService <IStreamPublisher>(); var processingTasks = await publisher.PublishAsync(sequence.StreamProvider).ConfigureAwait(false); if (processingTasks.Count == 0) { _logger.LogTraceWithMessageInfo( IntegrationEventIds.LowLevelTracing, $"No subscribers for {sequence.GetType().Name} '{sequence.SequenceId}'.", context); return(Task.CompletedTask); } return(Task.Run( async() => { try { if (processingTasks.Count == 1) { await processingTasks.First().ConfigureAwait(false); return; } using var cancellationTokenSource = new CancellationTokenSource(); var tasks = processingTasks.Select(task => task.CancelOnExceptionAsync(cancellationTokenSource)) .ToList(); await Task.WhenAny(tasks).ConfigureAwait(false); if (!sequence.IsComplete && tasks.All(task => !task.IsFaulted)) { // Call AbortAsync to abort the uncompleted sequence, to avoid unreleased locks. // The reason behind this call here may be counterintuitive but with // SequenceAbortReason.EnumerationAborted a commit is in fact performed. await sequence.AbortAsync(SequenceAbortReason.EnumerationAborted).ConfigureAwait(false); } await Task.WhenAll(processingTasks).ConfigureAwait(false); } catch (Exception exception) { await sequence.AbortAsync(SequenceAbortReason.Error, exception).ConfigureAwait(false); sequence.Dispose(); } finally { _logger.LogTraceWithMessageInfo( IntegrationEventIds.LowLevelTracing, $"{sequence.GetType().Name} '{sequence.SequenceId}' processing completed.", context); } })); }