internal async Task ProcessMessageAsync(IStorageQueueMessage message, TimeSpan visibilityTimeout, CancellationToken cancellationToken)
        {
            try
            {
                if (!await _queueProcessor.BeginProcessingMessageAsync(message.SdkObject, cancellationToken))
                {
                    return;
                }

                FunctionResult result = null;
                using (ITaskSeriesTimer timer = CreateUpdateMessageVisibilityTimer(_queue, message, visibilityTimeout, _backgroundExceptionDispatcher))
                {
                    timer.Start();

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

                    await timer.StopAsync(cancellationToken);
                }

                await _queueProcessor.CompleteProcessingMessageAsync(message.SdkObject, result, cancellationToken);
            }
            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.)
                _backgroundExceptionDispatcher.Throw(ExceptionDispatchInfo.Capture(exception));
            }
        }
Beispiel #2
0
        public async Task CompleteProcessingMessageAsync_Failure_AppliesVisibilityTimeout()
        {
            var queuesOptions = new QueuesOptions
            {
                // configure a non-zero visibility timeout
                VisibilityTimeout = TimeSpan.FromMinutes(5)
            };
            Mock <QueueClient> queueClientMock          = new Mock <QueueClient>();
            TimeSpan           updatedVisibilityTimeout = TimeSpan.Zero;

            queueClientMock.Setup(x => x.UpdateMessageAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <TimeSpan>(), It.IsAny <CancellationToken>()))
            .Callback((string messageId, string popReceipt, string messageText, TimeSpan visibilityTimeout, CancellationToken cancellationToken) =>
            {
                updatedVisibilityTimeout = visibilityTimeout;
            })
            .ReturnsAsync(Response.FromValue(QueuesModelFactory.UpdateReceipt("x", DateTimeOffset.UtcNow.AddMinutes(5)), null));

            QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(queueClientMock.Object, null, queuesOptions, _poisonQueue);
            QueueProcessor localProcessor        = new QueueProcessor(context);

            string messageContent = Guid.NewGuid().ToString();
            await _queue.SendMessageAsync(messageContent);

            var          functionResult = new FunctionResult(false);
            QueueMessage message        = (await _queue.ReceiveMessagesAsync(1)).Value.FirstOrDefault();
            await localProcessor.CompleteProcessingMessageAsync(message, functionResult, CancellationToken.None);

            Assert.AreEqual(queuesOptions.VisibilityTimeout, updatedVisibilityTimeout);
        }
Beispiel #3
0
        public async Task CompleteProcessingMessageAsync_MaxDequeueCountExceeded_MovesMessageToPoisonQueue()
        {
            ILoggerFactory loggerFactory = new LoggerFactory();
            var            provider      = new TestLoggerProvider();

            loggerFactory.AddProvider(provider);
            QueueProcessorOptions context        = new QueueProcessorOptions(_queue, loggerFactory, _queuesOptions, _poisonQueue);
            QueueProcessor        localProcessor = new QueueProcessor(context);

            bool poisonMessageHandlerCalled = false;

            localProcessor.MessageAddedToPoisonQueueAsync += (sender, e) =>
            {
                Assert.AreSame(sender, localProcessor);
                Assert.AreSame(_poisonQueue, e.PoisonQueue);
                Assert.NotNull(e.Message);
                poisonMessageHandlerCalled = true;
                return(Task.CompletedTask);
            };

            string       messageContent = Guid.NewGuid().ToString();
            QueueMessage message        = null;
            await _queue.SendMessageAsync(messageContent);

            FunctionResult result = new FunctionResult(false);

            for (int i = 0; i < context.Options.MaxDequeueCount; i++)
            {
                message = (await _queue.ReceiveMessagesAsync(1)).Value.FirstOrDefault();
                await localProcessor.CompleteProcessingMessageAsync(message, result, CancellationToken.None);
            }

            message = (await _queue.ReceiveMessagesAsync(1)).Value.FirstOrDefault();
            Assert.Null(message);

            QueueMessage poisonMessage = (await _poisonQueue.ReceiveMessagesAsync(1)).Value.FirstOrDefault();

            Assert.NotNull(poisonMessage);
            Assert.AreEqual(messageContent, poisonMessage.MessageText);
            Assert.True(poisonMessageHandlerCalled);

            var categories = provider.GetAllLogMessages().Select(p => p.Category);

            CollectionAssert.Contains(categories, "Microsoft.Azure.WebJobs.Host.Queues.QueueProcessor");
        }
Beispiel #4
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.
            }
        }
        public async Task CompleteProcessingMessageAsync_MaxDequeueCountExceeded_MovesMessageToPoisonQueue()
        {
            QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_queue, null, _queuesOptions, _poisonQueue);
            QueueProcessor localProcessor        = new QueueProcessor(context);

            bool poisonMessageHandlerCalled = false;

            localProcessor.MessageAddedToPoisonQueue += (sender, e) =>
            {
                Assert.Same(sender, localProcessor);
                Assert.Same(_poisonQueue, e.PoisonQueue);
                Assert.NotNull(e.Message);
                poisonMessageHandlerCalled = true;
            };

            string            messageContent = Guid.NewGuid().ToString();
            CloudQueueMessage message        = new CloudQueueMessage(messageContent);
            await _queue.AddMessageAsync(message, CancellationToken.None);

            FunctionResult result = new FunctionResult(false);

            for (int i = 0; i < context.MaxDequeueCount; i++)
            {
                message = await _queue.GetMessageAsync();

                await localProcessor.CompleteProcessingMessageAsync(message, result, CancellationToken.None);
            }

            message = await _queue.GetMessageAsync();

            Assert.Null(message);

            CloudQueueMessage poisonMessage = await _poisonQueue.GetMessageAsync();

            Assert.NotNull(poisonMessage);
            Assert.Equal(messageContent, poisonMessage.AsString);
            Assert.True(poisonMessageHandlerCalled);
        }
        public async Task CompleteProcessingMessageAsync_Failure_AppliesVisibilityTimeout()
        {
            var queuesOptions = new QueuesOptions
            {
                // configure a non-zero visibility timeout
                VisibilityTimeout = TimeSpan.FromMinutes(5)
            };
            QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_queue, null, queuesOptions, _poisonQueue);
            QueueProcessor localProcessor        = new QueueProcessor(context);

            string            messageContent = Guid.NewGuid().ToString();
            CloudQueueMessage message        = new CloudQueueMessage(messageContent);
            await _queue.AddMessageAsync(message, CancellationToken.None);

            var functionResult = new FunctionResult(false);

            message = await _queue.GetMessageAsync();

            await localProcessor.CompleteProcessingMessageAsync(message, functionResult, CancellationToken.None);

            var delta = message.NextVisibleTime - DateTime.UtcNow;

            Assert.True(delta.Value.TotalMinutes > 4);
        }
        public async Task CompleteProcessingMessageAsync_MaxDequeueCountExceeded_MovesMessageToPoisonQueue()
        {
            QueueProcessorOptions context        = new QueueProcessorOptions(_queue, null, _queuesOptions, _poisonQueue);
            QueueProcessor        localProcessor = new QueueProcessor(context);

            bool poisonMessageHandlerCalled = false;

            localProcessor.MessageAddedToPoisonQueue += (sender, e) =>
            {
                Assert.AreSame(sender, localProcessor);
                Assert.AreSame(_poisonQueue, e.PoisonQueue);
                Assert.NotNull(e.Message);
                poisonMessageHandlerCalled = true;
            };

            string       messageContent = Guid.NewGuid().ToString();
            QueueMessage message        = null;
            await _queue.SendMessageAsync(messageContent);

            FunctionResult result = new FunctionResult(false);

            for (int i = 0; i < context.Options.MaxDequeueCount; i++)
            {
                message = (await _queue.ReceiveMessagesAsync(1)).Value.FirstOrDefault();
                await localProcessor.CompleteProcessingMessageAsync(message, result, CancellationToken.None);
            }

            message = (await _queue.ReceiveMessagesAsync(1)).Value.FirstOrDefault();
            Assert.Null(message);

            QueueMessage poisonMessage = (await _poisonQueue.ReceiveMessagesAsync(1)).Value.FirstOrDefault();

            Assert.NotNull(poisonMessage);
            Assert.AreEqual(messageContent, poisonMessage.MessageText);
            Assert.True(poisonMessageHandlerCalled);
        }