Lesson 12 / 25
Dead-Letter Exchanges, TTL and Retry Strategies
Route failed or expired messages to dead-letter exchanges and build delayed retries.
Where failed messages go
A queue can be configured with a dead-letter exchange (DLX). A message is dead-lettered, republished to the DLX with an optional new routing key, when it is rejected or nacked with requeue=false, when its TTL expires, when messages are dropped from the head of a queue that exceeds its length limit (the default drop-head overflow behaviour), or, for quorum queues, when it exceeds the delivery limit (a redelivery count, default 20 since RabbitMQ 4.0). Bind a dead-letter queue to the DLX to inspect and replay failures. Message TTL (set per queue with the x-message-ttl argument or a policy) expires messages that wait too long. A common delayed retry pattern combines them: on a transient failure the consumer publishes the message to a retry queue that has a TTL (say 30 seconds) and no consumers, then acks the original; the retry queue dead-letters expired messages back to the work queue (for example through the default exchange with the work queue's name as the dead-letter routing key). Count attempts in a header (your own, or the x-death header RabbitMQ adds on each dead-lettering) and after N attempts publish to a parking-lot queue instead. Keep a quorum delivery limit as a separate guard against messages that crash consumers before they can ack. The community delayed message exchange plugin is an alternative but is not recommended for very large volumes of delayed messages.
Work queue, retry queue and dead-letter queue via policies
Policies apply DLX and TTL settings without changing application code.
# retry queue: no consumers, 30 s TTL, then back to billing.orders only
# (via the default exchange, so other subscribers of 'orders' never see retries)
rabbitmqctl set_policy -p shop retry-ttl '^billing\.orders\.retry$' \
'{"message-ttl":30000, "dead-letter-exchange":"", "dead-letter-routing-key":"billing.orders"}' --apply-to queues
# work queue guard: messages that crash consumers repeatedly go to the parking lot
rabbitmqctl set_policy -p shop work-guard '^billing\.orders$' \
'{"delivery-limit":5, "dead-letter-exchange":"", "dead-letter-routing-key":"billing.orders.parked"}' --apply-to queues
# consumer side (Python): retry or park, then ack the original
# attempts = int((props.headers or {}).get('x-retries', 0))
# target = 'billing.orders.retry' if attempts < 3 else 'billing.orders.parked'
# ch.basic_publish('', target, body, pika.BasicProperties(
# message_id=props.message_id, delivery_mode=2, headers={'x-retries': attempts + 1}))
# ch.basic_ack(method.delivery_tag)Always have a parking lot
Retrying forever hides bugs and wastes capacity. After a few attempts, move the message to a parking-lot (dead-letter) queue, alert on its depth and build a simple way to replay messages after a fix.
Quick check: Which event does NOT cause a message to be dead-lettered?
- The consumer acknowledges it successfully
- The consumer nacks it with requeue=false
- Its message TTL expires
- A quorum queue delivery limit is exceeded
Answer
The consumer acknowledges it successfully — Successful acknowledgement removes the message; the others trigger dead-lettering when a DLX is configured.