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;