/// <inheritdoc cref="IConsumerBehavior.HandleAsync" /> public virtual async Task HandleAsync(ConsumerPipelineContext context, ConsumerBehaviorHandler next) { Check.NotNull(context, nameof(context)); Check.NotNull(next, nameof(next)); var sequenceReader = await _sequenceReaders .FirstOrDefaultAsync(reader => reader.CanHandleAsync(context)) .ConfigureAwait(false); if (sequenceReader == null) { await next(context).ConfigureAwait(false); return; } // Store the original envelope in case it is replaced in the GetSequence method (see ChunkSequenceReader) var originalEnvelope = context.Envelope; // Store the previous sequence since it must be added to the new one (e.g. ChunkSequence into BatchSequence) var previousSequence = context.Sequence; ISequence?sequence; // Loop to handle edge cases where the sequence gets completed between the calls to // GetSequenceAsync and AddAsync while (true) { sequence = await GetSequenceAsync(context, next, sequenceReader).ConfigureAwait(false); if (sequence == null) { return; } // Loop again if the retrieved sequence has completed already in the meanwhile // ...unless it was a new sequence, in which case it can only mean that an error // occurred in the subscriber before consuming the actual first message and it doesn't // make sense to recreate and publish once again the sequence. if (!sequence.IsPending || sequence.IsCompleting) { if (sequence.IsNew) { break; } continue; } await sequence.AddAsync(originalEnvelope, previousSequence).ConfigureAwait(false); _logger.LogMessageAddedToSequence(context.Envelope, sequence); AddSequenceTagToActivity(sequence); break; } if (sequence.IsComplete) { await AwaitOtherBehaviorIfNeededAsync(sequence).ConfigureAwait(false); // Mark the envelope as the end of the sequence only if the sequence wasn't swapped (e.g. chunk -> batch) if (sequence.Context.Sequence == null || sequence == sequence.Context.Sequence || sequence.Context.Sequence.IsCompleting || sequence.Context.Sequence.IsComplete) { context.SetIsSequenceEnd(); } _logger.LogSequenceCompleted(sequence); } }
/// <inheritdoc cref="IConsumerBehavior.HandleAsync" /> public virtual async Task HandleAsync(ConsumerPipelineContext context, ConsumerBehaviorHandler next) { Check.NotNull(context, nameof(context)); Check.NotNull(next, nameof(next)); var sequenceReader = await _sequenceReaders .FirstOrDefaultAsync(reader => reader.CanHandleAsync(context)) .ConfigureAwait(false); if (sequenceReader == null) { await next(context).ConfigureAwait(false); return; } // Store the original envelope in case it is replaced in the GetSequence method (see ChunkSequenceReader) var originalEnvelope = context.Envelope; // Store the previous sequence since it must be added to the new one (e.g. ChunkSequence into BatchSequence) var previousSequence = context.Sequence; ISequence?sequence; // Loop to handle edge cases where the sequence gets completed between the calls to // GetSequenceAsync and AddAsync while (true) { sequence = await GetSequenceAsync(context, next, sequenceReader).ConfigureAwait(false); if (sequence == null) { return; } if (!sequence.IsPending || sequence.IsCompleting) { continue; } await sequence.AddAsync(originalEnvelope, previousSequence).ConfigureAwait(false); AddSequenceTagToActivity(sequence); break; } _logger.LogMessageAddedToSequence(context.Envelope, sequence); if (sequence.IsComplete) { await AwaitOtherBehaviorIfNeededAsync(sequence).ConfigureAwait(false); // Mark the envelope as the end of the sequence only if the sequence wasn't swapped (e.g. chunk -> batch) if (sequence.Context.Sequence == null || sequence == sequence.Context.Sequence || sequence.Context.Sequence.IsCompleting || sequence.Context.Sequence.IsComplete) { context.SetIsSequenceEnd(); } _logger.LogSequenceCompleted(sequence); } }
/// <inheritdoc cref="IConsumerBehavior.HandleAsync" /> public virtual async Task HandleAsync(ConsumerPipelineContext context, ConsumerBehaviorHandler next) { Check.NotNull(context, nameof(context)); Check.NotNull(next, nameof(next)); var sequenceReader = await _sequenceReaders.FirstOrDefaultAsync(reader => reader.CanHandleAsync(context)).ConfigureAwait(false); if (sequenceReader == null) { await next(context).ConfigureAwait(false); return; } // Store the original envelope in case it is replaced in the GetSequence method (see ChunkSequenceReader) var originalEnvelope = context.Envelope; // Store the previous sequence since it must be added to the new one (e.g. ChunkSequence into BatchSequence) var previousSequence = context.Sequence; var sequence = await sequenceReader.GetSequenceAsync(context).ConfigureAwait(false); if (sequence == null) { _logger.LogWarningWithMessageInfo( IntegrationEventIds.IncompleteSequenceDiscarded, "The incomplete sequence is ignored (probably missing the first message).", context); return; } context.SetSequence(sequence, sequence.IsNew); if (sequence.IsNew) { await PublishSequenceAsync(context, next).ConfigureAwait(false); if (context.ProcessingTask != null) { MonitorProcessingTaskPrematureCompletion(context.ProcessingTask, sequence); } _logger.LogDebugWithMessageInfo( IntegrationEventIds.SequenceStarted, $"Started new {sequence.GetType().Name}.", context); } await sequence.AddAsync(originalEnvelope, previousSequence).ConfigureAwait(false); _logger.LogDebugWithMessageInfo( IntegrationEventIds.MessageAddedToSequence, $"Message added to {sequence.GetType().Name}.", context); if (sequence.IsComplete) { await AwaitOtherBehaviorIfNeededAsync(sequence).ConfigureAwait(false); // Mark the envelope as the end of the sequence only if the sequence wasn't swapped (e.g. chunk -> batch) if (sequence.Context.Sequence == null || sequence == sequence.Context.Sequence || sequence.Context.Sequence.IsCompleting || sequence.Context.Sequence.IsComplete) { context.SetIsSequenceEnd(); } _logger.LogDebugWithMessageInfo( IntegrationEventIds.SequenceCompleted, $"{sequence.GetType().Name} '{sequence.SequenceId}' completed.", context); } }