12

I am continuously receiving messages in peek mode and abandoning them if processing fails (Not the delivery). However, the message immediately becomes available again and is received for processing again. It fails quickly again and after max deliveries it is dead-lettered.

Is there a way to configure the topic/subscription to wait before releasing the message after it is abandoned? Preferably in exponential manner.

Of course I am open for suggestions via the code as well.

dvitonis
  • 263
  • 3
  • 9

5 Answers5

10

There is not a way to set an exponential back-off in the Service Bus configuration. I've experienced the same issue, and have done the following:

  1. Dequeue the message, and marked the message as received.
  2. Perform processing within a try/catch block. If there is an exception, enqueue a new message with a scheduled delivery at a certain point in the future.

We've wrapped our Service Bus Message Queue payload in a class that specifies the number of delivery attempts. We multiply the number of delivery attempts times a constant, then add that number to the current dateTime for scheduled delivery in the future. After the number of delivery attempts that we want to try are exceeded, we explicitly dead letter the message.

Edit 7-17-2020 Consider using Azure Durable Task Framework which has a customizable retry policy built right in.

Rob Reagan
  • 7,313
  • 3
  • 20
  • 49
  • If the app crashes between step 1 and 2 you will lose the message - consider using the peek lock feature – alastairtree Nov 19 '19 at 14:10
  • If by "crash" you mean an exception, this this will work just fine. If by "crash" you mean that the datacenter disappears in a mushroom cloud, then the message will indeed be lost. But using peeklock will invalidate this entire algorithm. – Rob Reagan Nov 19 '19 at 14:38
  • 3
    Any failure that stops execution of your code at that unfortunate moment could lead to data loss as the message is already completed. In some transactional systems this may be unacceptable, in others rare data loss is less of an issue. If data loss is a concern do not mark the first message as complete until either the first message is processed successfully or the replacement message has been enqueued successfully. Then the worst case scenario is that you crash after enqueueing the replacement and before completing on the first - and on restart you will get both messages but zero data loss. – alastairtree Nov 19 '19 at 16:07
  • To be completely safe from message loss, use PeekLock and complete the message along with scheduling the new one in a transaction. More info here https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-transactions – Adam Smejkal Dec 07 '21 at 16:10
0

Another option is using MassTransit which has support for Azure Service Bus.

Take a look at its extensive retry configuration.

Note that MassTransit is effectively doing the retries in memory after the message has been received so you'll need to adjust your topic subscription's MaxDeliveryCount and MaxAutoRenewDuration settings appropriately.

Your configuration might look something like:

var busControl = Bus.Factory.CreateUsingAzureServiceBus(cfg =>
{
    var host = cfg.Host(serviceBusConnectionString, hst => { });

    cfg.UseMessageRetry(retryConfigurator =>
        RetryConfigurationExtensions.Exponential(retryConfigurator, ...);

    cfg.SubscriptionEndpoint(
        "subscriptionName",
        "topicPath",
        e =>
        {
            e.Consumer<SomeConsumer>();
            // Let MassTransit do the retries
            e.MaxDeliveryCount = 1;
            e.MaxAutoRenewDuration = TimeSpan.FromMinutes(10);
        });
});
Simon Ness
  • 2,272
  • 22
  • 28
0

You can use Scheduled Messages for this.

Essentially, when you need to retry a message you just schedule it in the future and Service Bus will add it to the queue again once enough time has passed:

        ServiceBusSender queue = ...
        int secs = // calculate the delay

        var msg = new ServiceBusMessage("My scheduled message body")
        {
            ApplicationProperties =
            {
                ["RetryCount"] = retryCount,
            },
        };

        logger.LogInformation("Scheduling for " + secs + " secs");
        await queue.ScheduleMessageAsync(msg, DateTimeOffset.Now.AddSeconds(secs));

You must add information about retry count in a header or the body. Otherwise you won't know how many times you have tried it and cannot really calculate the future date.

Martin Wickman
  • 19,662
  • 12
  • 82
  • 106
0

The message should remain unavailable/hidden for the duration of the message lock. If you abandon the message explicitly (using the AbandonAsync method), then the lock is cancelled, making the message available for consumption; if you instead finish your process without explicitly abandoning the message, Azure will wait until the lock duration is finished to make the message available again, this could give you a bit more of delay. You can tune this to implement your retry operation; you can also implement more complete approaches (like exponential backoff) as explained here

Gedeon
  • 742
  • 9
  • 13
-5

I am looking into this topic as well and I came across the class RetryExponential class from Microsoft.

RetryExponential Class

Namespace: Microsoft.ServiceBus
Assembly: Microsoft.ServiceBus.dll

Represents an implementation of a retry policy. For each time the messaging operation must be retried, the delay between retries grows in a staggered, exponential manner.

public sealed class RetryExponential : Microsoft.ServiceBus.RetryPolicy

Community
  • 1
  • 1
Bas Kooistra
  • 129
  • 1
  • 2
  • 6
  • Bas, it would be better if you explained why/how this class solves the problem. I pulled a summary from MS site so your answer is not a "link-only" one (those tend to be deleted as low-quality) – brasofilo May 29 '19 at 16:44
  • 3
    The RetryExponential class will not help with this situation. This class is used to control retries of requests made via the Service Bus APIs to Service Bus itself, not retries of the messages on the queues themselves. i.e. RetryExponential can be used to automatically retry the ReceiveMessage call, the SendMessage call, etc. It will have no effect on when a failed message retried, or when it is sent to the dead letter queue. – Boschy Feb 21 '20 at 00:58