Skip to content

Dead-Letter Strategies

This guide explains how dead-letter handling works in the ServiceBus RabbitMQ implementation.

It is important to distinguish between:

  • the automatic retry topology managed by the library;
  • custom dead-letter queues implemented directly in RabbitMQ.

These are different mechanisms intended for different scenarios.


Automatic Retry Topology

The ServiceBus library provides a built-in retry mechanism through the RetryPolicy property of SubscriberBoundDescriptor.

var descriptor = new SubscriberBoundDescriptor
{
    RetryPolicy = new RetryPolicyDescriptor
    {
        Enabled = true,
        MaxRetry = 5,
        Delayed = TimeSpan.FromSeconds(30)
    }
};

When retries are enabled, the retry topology is generated automatically during subscriber initialization.

The topology is derived from:

  • SubscriberBoundDescriptor.Queue
  • SubscriberBoundDescriptor.RetryPolicy

No additional RabbitMQ objects need to be configured.


Automatically Created Topology

When the subscriber starts, the library automatically creates the required RabbitMQ resources.

The generated topology consists of:

  • a Dead-Letter Exchange (<queue>_dlx);
  • a Retry Exchange (<queue>_retry);
  • a Retry Queue (<queue>_retry).

The retry queue is configured internally with:

  • x-message-ttl
  • x-dead-letter-exchange
  • x-queue-type

The required bindings between these resources are also created automatically.

flowchart LR

    MainQueue["orders"]

    DLX["orders_dlx"]

    RetryQueue["orders_retry"]

    RetryExchange["orders_retry"]

    MainQueue -->|Rejected| DLX

    DLX --> RetryQueue

    RetryQueue -->|TTL expired| RetryExchange

    RetryExchange --> MainQueue

The retry topology is completely managed by the library.


Retry Configuration

Applications configure retries exclusively through RetryPolicyDescriptor.

Example:

RetryPolicy = new RetryPolicyDescriptor
{
    Enabled = true,

    MaxRetry = 5,

    Delayed = TimeSpan.FromSeconds(30),

    Durable = true,

    AutoDelete = false
}

The library uses this configuration to create the retry queue and exchanges.

Applications do not create or configure these RabbitMQ resources directly.


Manual Configuration

The retry topology is not intended to be configured manually.

The library owns the lifecycle of:

  • the retry queue;
  • the retry exchange;
  • the dead-letter exchange;
  • the required bindings.

Applications should only configure the RetryPolicy.

Changing or creating these resources manually may produce an inconsistent topology and is therefore not supported.


Custom Dead-Letter Queues

Some systems require permanent dead-letter queues for operational analysis or manual message recovery.

This scenario is different from the automatic retry mechanism provided by the library.

A custom Dead-Letter Queue (DLQ) is implemented directly in RabbitMQ using queue arguments such as:

  • x-dead-letter-exchange
  • x-dead-letter-routing-key

These queues are entirely managed by the application or the RabbitMQ administrator and are outside the scope of the library retry mechanism.


Retry vs Dead-Letter Queue

Automatic Retry Custom Dead-Letter Queue
Managed by the library Managed by the application
Configured through RetryPolicyDescriptor Configured directly in RabbitMQ
Retry queue and exchanges created automatically RabbitMQ resources created manually
Used for delayed retry processing Used for permanent message isolation
No manual RabbitMQ configuration required Full RabbitMQ configuration required

Best Practices

  • Configure retries only through SubscriberBoundDescriptor.RetryPolicy.
  • Do not manually create or modify the retry queue or retry exchanges.
  • Let the library manage the retry topology.
  • Use custom Dead-Letter Queues only when messages must be permanently isolated after processing failures.