RabbitMQ Message TTL & Expiration Strategy Prompt
Decide where and how to apply message TTL, queue TTL, and max-length so stale messages expire safely instead of piling up or silently disappearing.
- Target user
- Backend and platform engineers designing message-expiration policies
- Difficulty
- Intermediate
- Tools
- Claude, ChatGPT, Cursor
The prompt
You are a senior platform engineer who has set message- and queue-level TTL policies that keep queues bounded without quietly losing important messages. Help me design mine. I will provide: - The message types and which ones go stale (and after how long) vs which must never expire [DESCRIBE] - Current queue config: `rabbitmqadmin list queues name arguments messages` and any policies [PASTE OUTPUT] - What should happen to expired/overflow messages: drop, or dead-letter for inspection [DESCRIBE] Your job: 1. **Choose the TTL level** — explain per-message TTL (`expiration` property) vs per-queue message-TTL (`x-message-ttl`) vs queue-TTL (`x-expires`, which deletes an unused queue). Pick the right level for each flow and note that the lower of message and queue TTL wins. 2. **Mind the expiry-from-head behavior** — per-queue TTL only expires messages from the head of the queue, so a message can sit expired-but-not-removed behind a long backlog; explain when this matters and how max-length helps. 3. **Bound length too** — combine TTL with `x-max-length` / `x-max-length-bytes` and an `x-overflow` policy (`drop-head`, `reject-publish`, or `reject-publish-dlx`) so the queue stays bounded under burst. 4. **Route expired messages deliberately** — wire a DLX so expired and overflow messages are dead-lettered for inspection rather than silently dropped, unless dropping is genuinely intended. 5. **Watch interactions** — TTL plus DLX is the backbone of delayed-retry topologies; flag any place a TTL change would alter retry timing or drop messages a downstream still needs. Output as: (a) a per-flow TTL/max-length/overflow matrix, (b) the exact x-arguments or policy definition, (c) where messages go on expiry, (d) the metrics to confirm queues stay bounded. Validate on a staging broker and confirm expired messages land where you intend before applying to prod. Confirm the DLX exists before changing TTL; without it, expired and overflow messages are discarded permanently, which is real message loss.
Why this prompt works
TTL in RabbitMQ has more sharp edges than its name suggests, and the prompt makes the operator confront each one before touching a live queue. It separates the three distinct mechanisms people conflate: per-message expiration, per-queue x-message-ttl, and queue x-expires (which deletes the whole unused queue, not messages). Knowing that the lower of message and queue TTL wins, and that queue-TTL deletes the queue, prevents the surprise of a queue vanishing or a message expiring sooner than intended.
The most under-appreciated behavior it captures is head-of-queue expiry: per-queue TTL only removes messages from the head, so an expired message can sit behind a backlog and stay visible far longer than its TTL. Teams that assume TTL means prompt deletion get burned by this. Pairing TTL with x-max-length and an explicit x-overflow policy is what actually keeps a queue bounded under burst, and the prompt insists on choosing that overflow behavior deliberately.
The guardrails close the message-loss gap. Adding or lowering a TTL on a live queue can immediately expire a wave of messages, and if no DLX is wired they’re gone for good. By forcing confirmation that the dead-letter target exists and that the change is rehearsed on staging, the prompt keeps an expiration policy from becoming silent data loss — while still supporting the legitimate TTL-plus-DLX pattern that powers delayed retries.