public async Task <TaskSeriesCommandResult> ExecuteAsync(CancellationToken cancellationToken)
        {
            TimeSpan delay;

            try
            {
                UpdateReceipt updateReceipt = await _queue.UpdateMessageAsync(_message.MessageId, _message.PopReceipt, visibilityTimeout : _visibilityTimeout, cancellationToken : cancellationToken).ConfigureAwait(false);

                _message = _message.Update(updateReceipt);
                _onUpdateReceipt?.Invoke(updateReceipt);
                // The next execution should occur after a normal delay.
                delay = _speedupStrategy.GetNextDelay(executionSucceeded: true);
            }
            catch (RequestFailedException exception)
            {
                // For consistency, the exceptions handled here should match PollQueueCommand.DeleteMessageAsync.
                if (exception.IsServerSideError())
                {
                    // The next execution should occur more quickly (try to update the visibility before it expires).
                    delay = _speedupStrategy.GetNextDelay(executionSucceeded: false);
                }
                if (exception.IsBadRequestPopReceiptMismatch())
                {
                    // There's no point to executing again. Once the pop receipt doesn't match, we've permanently lost
                    // ownership.
                    delay = Timeout.InfiniteTimeSpan;
                }
                else if (exception.IsNotFoundMessageOrQueueNotFound() ||
                         exception.IsConflictQueueBeingDeletedOrDisabled())
                {
                    // There's no point to executing again. Once the message or queue is deleted, we've permanently lost
                    // ownership.
                    // For queue disabled, in theory it's possible the queue could be re-enabled, but the scenarios here
                    // are currently unclear.
                    delay = Timeout.InfiniteTimeSpan;
                }
                else
                {
                    throw;
                }
            }

            return(new TaskSeriesCommandResult(wait: Task.Delay(delay, cancellationToken)));
        }
Ejemplo n.º 2
0
        internal async Task ProcessMessageAsync(QueueMessage message, TimeSpan visibilityTimeout, CancellationToken cancellationToken)
        {
            try
            {
                if (!await _queueProcessor.BeginProcessingMessageAsync(message, cancellationToken).ConfigureAwait(false))
                {
                    return;
                }

                FunctionResult         result          = null;
                Action <UpdateReceipt> onUpdateReceipt = updateReceipt => { message = message.Update(updateReceipt); };
                using (ITaskSeriesTimer timer = CreateUpdateMessageVisibilityTimer(_queue, message, visibilityTimeout, _exceptionHandler, onUpdateReceipt))
                {
                    timer.Start();

                    result = await _triggerExecutor.ExecuteAsync(message, cancellationToken).ConfigureAwait(false);

                    await timer.StopAsync(cancellationToken).ConfigureAwait(false);
                }

                // Use a different cancellation token for shutdown to allow graceful shutdown.
                // Specifically, don't cancel the completion or update of the message itself during graceful shutdown.
                // Only cancel completion or update of the message if a non-graceful shutdown is requested via _shutdownCancellationTokenSource.
                await _queueProcessor.CompleteProcessingMessageAsync(message, result, _shutdownCancellationTokenSource.Token).ConfigureAwait(false);
            }
            catch (TaskCanceledException)
            {
                // Don't fail the top-level task when an inner task cancels.
            }
            catch (OperationCanceledException)
            {
                // Don't fail the top-level task when an inner task cancels.
            }
            catch (Exception exception)
            {
                // Immediately report any unhandled exception from this background task.
                // (Don't capture the exception as a fault of this Task; that would delay any exception reporting until
                // Stop is called, which might never happen.)
#pragma warning disable AZC0103 // Do not wait synchronously in asynchronous scope.
                _exceptionHandler.OnUnhandledExceptionAsync(ExceptionDispatchInfo.Capture(exception)).GetAwaiter().GetResult();
#pragma warning restore AZC0103 // Do not wait synchronously in asynchronous scope.
            }
        }