diagram.mmd — flowchart
Message Queue Retry flowchart diagram

Message queue retry is a reliability pattern in which a failed message is re-enqueued for reprocessing after a delay, with the attempt count tracked to eventually route persistently failing messages to a dead letter queue.

Transient failures are a fact of life in distributed systems: a downstream API returns a 503, a database connection times out, or a downstream service restarts during a deployment. Without a retry strategy, a single transient error causes permanent message loss. Queue-level retry addresses this without requiring the producer to resend.

The pattern works by attaching metadata to each message: a delivery count (or attempt count) and optionally a next-visible-at timestamp. When a consumer fails to process a message (either by throwing an exception or by not acking within the visibility timeout), the queue makes the message visible again after a backoff delay. Exponential backoff — doubling the wait time on each retry — is the standard approach: 1s, 2s, 4s, 8s. This prevents a storm of retries from overwhelming an already-struggling downstream service.

Each retry increments the attempt counter. When the counter exceeds the configured maximum (typically 3–5), the message is not re-queued into the main queue but instead forwarded to a Dead Letter Queue for inspection, alerting, or manual replay. This prevents a poison message from blocking the queue indefinitely.

Retry logic pairs directly with Idempotent Consumer design: because a message may be delivered and processed multiple times, consumers must produce the same outcome regardless of how many times they receive the same message. This is the foundation of safe at-least-once delivery semantics, explored further in Exactly Once Delivery.

Free online editor
Edit this diagram in Graphlet
Fork, modify, and export to SVG or PNG. No sign-up required.
Open in Graphlet →

Frequently asked questions

Message queue retry is a reliability pattern where a message that fails processing is automatically re-enqueued after a delay, with the delivery count tracked across attempts. If the message continues to fail after reaching the configured maximum retries, it is routed to a dead letter queue for inspection rather than blocking the primary queue.
Each message carries a delivery count and optionally a next-visible-at timestamp. When processing fails — either by exception or by missing the visibility timeout — the queue increments the count and makes the message visible again after a backoff delay. Exponential backoff (1s, 2s, 4s, 8s) is standard, preventing retry storms from overwhelming a struggling downstream service. Once the count exceeds the maximum, the message is dead-lettered.
Use retry for any message queue that processes operations against external dependencies — APIs, databases, downstream services — that may experience transient failures. Without retry, a single 503 from a downstream service causes permanent message loss. Retry is the baseline for reliable asynchronous processing in production.
The most common mistake is retrying without exponential backoff, causing a thundering-herd effect where all retries fire simultaneously and overwhelm a recovering service. Another pitfall is setting the maximum retry count too high for poison messages — a message that always fails will cycle through many retries before reaching the DLQ, tying up consumer threads. A third mistake is not implementing idempotent consumers: retried messages must be safe to process more than once.
mermaid
flowchart TD MQ[Message Queue] -->|deliver message\nattempt 1| C[Consumer] C -->|processing fails\ntransient error| F{Attempt\ncount} F -->|attempt <= max\n3 retries| D[Delay Queue\nexponential backoff] D -->|wait 1s / 2s / 4s| MQ F -->|attempt > max| DLQ[Dead Letter Queue] C -->|processing succeeds| ACK[Acknowledge\nmessage deleted] DLQ --> Alert[Alert On-Call] DLQ --> Inspect[Manual Inspection] DLQ -->|replay after fix| MQ style DLQ fill:#f55,color:#fff style ACK fill:#5a5,color:#fff
Copied to clipboard