Skip to content

Message Redelivery Configuration

Some errors take a while to resolve, say a remote service is down or a SQL server has crashed. In these situations, it’s best to dust off and nuke the site from orbit — at a much later time, obviously. Redelivery is a form of retry (some refer to it as second-level retry) where the message is removed from the queue and then redelivered to the queue at a future time.

To use delayed redelivery, ensure the transport is properly configured. When using UseDelayedRedelivery on RabbitMQ, the delayed-exchange plug-in is required. ActiveMQ (non-Artemis) requires the scheduler to be enabled via the XML configuration. For RabbitMQ environments where the plug-in isn’t available, see Queue-Based Delayed Redelivery below.

services.AddMassTransit(x =>
{
x.AddConsumer<SubmitOrderConsumer>();
x.AddConfigureEndpointsCallback((context,name,cfg) =>
{
cfg.UseDelayedRedelivery(r => r.Intervals(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(30)));
cfg.UseMessageRetry(r => r.Immediate(5));
});
x.UsingRabbitMq((context, cfg) =>
{
cfg.ConfigureEndpoints(context);
});
});

Now, if the initial 5 immediate retries fail (the database is really, really down), the message will retry an additional three times after 5, 15, and 30 minutes. This could mean a total of 15 retry attempts (on top of the initial 4 attempts prior to the retry/redelivery filters taking control).

Not every RabbitMQ deployment allows plugins: managed instances, locked-down brokers, corporate policies, you name it.

MassTransit has a new queue-based delayed redelivery feature that achieves the same result as the delayed-exchange plug-in but uses plain RabbitMQ features ( message TTL + dead-letter exchanges), no plugins needed.

services.AddMassTransit(x =>
{
x.AddConsumer<SubmitOrderConsumer>();
x.AddConfigureEndpointsCallback((context, name, cfg) =>
{
cfg.UseQueueBasedDelayedRedelivery(r => r.Intervals(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(30)));
cfg.UseMessageRetry(r => r.Immediate(5));
});
x.UsingRabbitMq((context, cfg) =>
{
cfg.ConfigureEndpoints(context);
});
});

This is a drop-in replacement for UseDelayedRedelivery. The retry policy API is identical, only the method name changes.

When the bus starts, MassTransit automatically creates the following infrastructure on the broker:

  • A mt-delay-return direct exchange that routes expired messages back to the original queue
  • Per-interval mt-delay-{ms} exchange + queue pairs, each configured with x-message-ttl set to the delay interval and x-dead-letter-exchange pointing to mt-delay-return

When a message is scheduled for redelivery, it is published to the exchange matching the desired delay. The message sits in the corresponding TTL queue until it expires, at which point RabbitMQ dead-letters it through mt-delay-return back to the original queue for another attempt.

When queue-based delayed redelivery is necessary, the routing key is used to deliver the message back to its original queue. If the message has an existing routing key, the original routing key is saved in an MT-Original-RoutingKey header before the message is moved to a delay queue. When the message returns to the original queue, the routing key is restored transparently. Consumers see the correct RabbitMQ-RoutingKey value as usual.

UseDelayedRedeliveryUseQueueBasedDelayedRedelivery
RabbitMQ plugins supportedYes (delayed-exchange)Optional
Native transport supportAnyRabbitMQ only