private async Task ResubmitMessageAsync(Message message, Microsoft.Azure.ServiceBus.Core.MessageSender sender, int resubmitCount) { // https://markheath.net/post/defer-processing-azure-service-bus-message var clone = message.Clone(); clone.UserProperties["ResubmitCount"] = resubmitCount + 1; clone.ScheduledEnqueueTimeUtc = DateTime.UtcNow .AddMinutes(RetryTemporaryErrorsAfterMinutes) .AddSeconds(jitterer.Next(0, 120)); // plus some jitter up to 2 minutes await sender.SendAsync(clone); }
public async Task ForwardOrder( [ServiceBusTrigger( queueName: "%ServiceBusSettings:OrderProcessingQueueName%", // queueName can be stored in your app Configuration as it is here, or hard coded. Connection = "ServiceBusSettings:ConnectionString")] Message message, MessageReceiver messageReceiver, [ServiceBus( queueOrTopicName: "%ServiceBusSettings:OrderProcessingQueueName%", Connection = "ServiceBusSettings:ConnectionString" )] MessageSender messageSender, ILogger logger) => await _forwardJob.Run(logger, message, messageReceiver, messageSender);
public async Task Defer(TimeSpan tryAgainIn) { // Deferral of a message is implemented per the recommendation given here: // https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-deferral#message-deferral-apis // We defer the message in Azure Service Bus, effectively hiding it indefinitely. // We then send a new "control" message to the same queue that contains the sequence number of the deferred message. // The control message is scheduled for delivery at a certain time. // The message pump will handle the control message by getting the sequence number and using it to obtain the deferred message. var sender = new Microsoft.Azure.ServiceBus.Core.MessageSender(_messageReceiver.ServiceBusConnection, _messageReceiver.Path, _messageReceiver.RetryPolicy); var controlMessage = new Message(); controlMessage.UserProperties.Add(Constants.HeaderKeys.RPDeferredMessageSequenceNumber, _message.SystemProperties.SequenceNumber); controlMessage.UserProperties.Add(Constants.HeaderKeys.RPContextId, _settings.ContextId); //Copy the tenant Id if (_message.UserProperties.TryGetValue(Constants.HeaderKeys.RPTenantId, out var messageTenantId)) { controlMessage.UserProperties.Add(Constants.HeaderKeys.RPTenantId, messageTenantId); } controlMessage.ScheduledEnqueueTimeUtc = DateTime.UtcNow.Add(tryAgainIn); controlMessage.PartitionKey = _message.PartitionKey; // Ensure the control message goes to the same partition as the underlying message so transactions work // We want to use a transaction if we're not already in a transaction scope var useTransaction = !_isTransactional; using (var tx = useTransaction ? new TransactionScope(TransactionScopeAsyncFlowOption.Enabled) : null) { await sender.SendAsync(controlMessage).ConfigureAwait(false); if (_deferralControlMessage == null) { await _messageReceiver.DeferAsync(_message.SystemProperties.LockToken) .ConfigureAwait(false); } else { await _messageReceiver.AbandonAsync(_message.SystemProperties.LockToken) .ConfigureAwait(false); } // If we are deferring a message that was already deferred, we are sending a new control message, so complete // the current control message. if (_deferralControlMessage != null) { await _messageReceiver.CompleteAsync(_deferralControlMessage.SystemProperties.LockToken).ConfigureAwait(false); } tx?.Complete(); } }
static async Task Sender() { var queue = "myfirstqueue"; var sender = new Microsoft.Azure.ServiceBus.Core.MessageSender(NamespaceConnectionString, queue); var text = Console.ReadLine(); while (text != "q") { var message = new Microsoft.Azure.ServiceBus.Message(System.Text.Encoding.UTF8.GetBytes(text)); var messageWithOffset = new Microsoft.Azure.ServiceBus.Message(System.Text.Encoding.UTF8.GetBytes(text + " with offset")); await sender.ScheduleMessageAsync(messageWithOffset, DateTimeOffset.Now.AddSeconds(3)); await sender.SendAsync(message); text = Console.ReadLine(); } await sender.CloseAsync(); }
public async Task SendAsync(IList <Message> messageList) { int count = MessageSender.ValidateMessages(messageList); MessagingEventSource.Log.MessageSendStart(this.ClientId, count); try { await this.RetryPolicy.RunOperation( async() => { await this.OnSendAsync(messageList).ConfigureAwait(false); }, this.OperationTimeout) .ConfigureAwait(false); } catch (Exception exception) { MessagingEventSource.Log.MessageSendException(this.ClientId, exception); throw; } MessagingEventSource.Log.MessageSendStop(this.ClientId); }
public async Task Run(ILogger logger, Message message, MessageReceiver messageReceiver, Microsoft.Azure.ServiceBus.Core.MessageSender messageSender) { _logger = logger; var messageString = Encoding.UTF8.GetString(message.Body); var jsonMessage = JsonConvert.DeserializeObject <ExampleMessageType>(messageString); // Process our job and forward the order var result = await TryProcessJobAsync(jsonMessage); // Examine results. Log information and Retry job if necessary var lockToken = message.SystemProperties.LockToken; switch (result) { case ResultCode.Success: logger.LogInformation($"Completed the message {message.MessageId} due to successful handling."); break; case ResultCode.TemporaryFailure: int resubmitCount = message.UserProperties.ContainsKey("ResubmitCount") ? (int)message.UserProperties["ResubmitCount"] : 0; if (resubmitCount > 5) { await messageReceiver.DeadLetterAsync(lockToken, "Exceeded max retries", GetDeadLetterDescription()); logger.LogInformation("$Dead lettered the message due to exceeding max retries, this will need to be retried manually."); } else { await ResubmitMessageAsync(message, messageSender, resubmitCount); logger.LogInformation($"Resubmitted the message {message.MessageId} to be tried again in {RetryTemporaryErrorsAfterMinutes} minutes"); } break; case ResultCode.PermanentFailure: await messageReceiver.DeadLetterAsync(lockToken, "Permanent failure", GetDeadLetterDescription()); logger.LogInformation("$Dead lettered the message due to a permanent failure, this will need to be retried manually."); break; default: break; } LogProgress(); if (result != ResultCode.Success) { // throw an error so the function fails and we can see it as an error in azure functions monitor logs throw new Exception("There were one or more errors during job"); } }