Skip to content

Configure Retry Policies

Retry policies define how a subscriber handles processing failures.

The retry mechanism is based on RabbitMQ DLX and TTL semantics.


Retry Condition

A message is retried only when the handler sets:

args.Acknowledged = false;
args.Requeue = true;

Example:

subscriber.Received.Add(
    async (sender, args) =>
    {
        try
        {
            var message = args.Message.Data;

            Console.WriteLine(
                $"processing order: {message.OrderId}");

            args.Acknowledged = true;
            args.Requeue = false;
        }
        catch
        {
            args.Acknowledged = false;
            args.Requeue = true;

            throw;
        }

        await Task.CompletedTask;
    });

Descriptor

var descriptor = new SubscriberBoundDescriptor
{
    PrefetchCount = 10,

    Queue = new QueueDescriptor
    {
        Name = "orders.created",
        Durable = true
    },

    Exchange = new ExchangeDescriptor
    {
        Name = "orders",
        Type = ExchangeTypes.Direct,
        Durable = true
    },

    Binder = new BinderDescriptor
    {
        RoutingKey = "orders.created"
    },

    RetryPolicy = new RetryPolicyDescriptor
    {
        Enabled = true,
        MaxRetry = 5,
        Delayed = TimeSpan.FromSeconds(10),
        Durable = true,
        AutoDelete = false
    }
};

Runtime Topology

When retry is enabled, the provider creates a DLX-based retry topology.

graph LR
    MainQueue["orders.created"]
    Subscriber["Subscriber"]
    DlxExchange["orders.created_dlx"]
    RetryQueue["orders.created_retry"]
    RetryExchange["orders.created_retry"]

    MainQueue --> Subscriber
    Subscriber -->|"Acknowledged = false<br/>Requeue = true"| DlxExchange
    DlxExchange --> RetryQueue
    RetryQueue -->|"x-message-ttl expires"| RetryExchange
    RetryExchange --> MainQueue

Retry Queue Arguments

The retry queue is declared with arguments equivalent to:

Arguments =
{
    ["x-message-ttl"] = Convert.ToInt64(
        retryPolicy.Delayed.TotalMilliseconds),

    ["x-dead-letter-exchange"] = retryExchange.FullName,

    ["x-queue-type"] = "classic"
};

If the main queue defines x-queue-type, the same queue type is propagated to the retry queue.


Max Retry Event

When retry attempts are exhausted, the subscriber exposes a max retry event.

subscriber.OnMaxRetryFailed.Add(
    async (sender, args) =>
    {
        Console.WriteLine(
            $"max retry reached: {args.Exception.Message}");

        await Task.CompletedTask;
    });

When To Use Retries

Retries are useful for transient failures, such as:

  • database timeouts;
  • network interruptions;
  • temporary downstream unavailability;
  • temporary lock conflicts.

When Not To Retry

Do not retry messages that cannot succeed after another attempt.

Examples:

  • invalid payload;
  • unsupported schema version;
  • business validation failure;
  • missing required data.

In these cases, set:

args.Acknowledged = false;
args.Requeue = false;