Example #1
0
        /// <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);
            }
        }
Example #2
0
        /// <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);
            }
        }
Example #3
0
        /// <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);
            }
        }