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)); } }
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); }
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"); }
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); }