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