Backpressure, queue lag and dead-letter queues
Mathematical queue drain times, rate-limiting upstream producers, and isolating poison pills.
What you will learn
- Predict whether a queue grows or drains from the arrival rate and consumer capacity.
- Work out how long a backlog takes to drain after a spike.
- Isolate poison pills with backoff, a retry limit and a dead-letter queue.
Queue lag dynamics and backlog accumulation
Let be the inbound message arrival rate (messages/sec), be the active consumer worker count, and be the average processing capacity per worker (messages/sec).
The net queue accumulation rate is defined as:
- Equilibrium (): Consumers keep pace with ingestion; queue depth remains near zero.
- Deficit (): Queue lag accumulates at a rate of messages/second.
- Drain Time (): If an initial backlog accumulates during a traffic spike, the time required to restore the queue to zero once arrival drops to is:
This only works when ; otherwise the backlog never drains and keeps growing.
Back pressure: An ever-growing queue can outgrow memory, causing cache misses, disk reads and even slower processing. Limiting the queue size keeps throughput and response times good for the jobs already in it. Once the queue is full, new requests are refused (for example with HTTP 503), and clients retry later, ideally with exponential backoff.
Poison pills and Dead-Letter Queue (DLQ) routing
A poison pill is a corrupted, unparseable, or schema-invalid message that causes worker code to throw an unhandled exception every time it is read:
How much damage it does depends on the broker. In an ordered log such as a Kafka partition, a consumer that keeps retrying the bad message never moves past it, so everything behind it waits (head-of-line blocking). In a queue such as SQS, a message that is not deleted is received again later (SQS moves standard-queue messages received three or more times to the back of the queue), so other messages keep flowing, but the bad one keeps coming back and wasting work.
The DLQ pattern:
- Capped exponential backoff with jitter: Retry transient failures with waits that grow exponentially up to a cap, and add randomness so many clients do not retry in lockstep. Marc Brooker's "full jitter" picks the wait at random between zero and the capped exponential value:where is the base delay (e.g. ), the cap (e.g. ), and the attempt number.
- Max retry threshold: After a set number of failed attempts, move the message to a dead-letter queue (DLQ) with its error details. In SQS this is the redrive policy's
maxReceiveCount: set it high enough to allow genuine retries. (A DLQ breaks strict ordering, so avoid one where the exact order of messages matters.) - Unblocked pipeline: Healthy messages keep flowing. Engineers inspect the DLQ, fix the bug, and move the messages back for reprocessing (SQS calls this redrive).
Backpressure, drain time and a dead-letter queue
- Burst Traffic Producers: 1 × Clients & Users
- Rate-Limiting API Gateway: 2 × Application Load Balancer
- Main Ingestion Queue: 3 × Message Queue / Kafka
- Autoscaling Worker Fleet: 6 × App Compute Server
- Dead-Letter Queue (DLQ): 2 × Distributed NoSQL Store
- Durable Media Sink: 2 × Object Storage (S3)
- Burst Traffic Producers sends requests to Rate-Limiting API Gateway
- Rate-Limiting API Gateway sends requests to Main Ingestion Queue
- Main Ingestion Queue sends requests to Autoscaling Worker Fleet
- Autoscaling Worker Fleet sends requests to Durable Media Sink
- Autoscaling Worker Fleet sends requests to Dead-Letter Queue (DLQ)
Key takeaways
- A backlog grows at when arrivals beat capacity, and drains in only if capacity exceeds the later arrival rate.
- Back pressure caps the queue and refuses new work when it is full, so the jobs already queued stay fast.
- A message that always fails blocks an ordered partition or loops forever; after a set number of attempts, move it to a dead-letter queue.
- Retry transient failures with capped exponential backoff and full jitter, so clients do not retry in lockstep.
Sources
- System Design Primer: Back pressure
- Marc Brooker, "Exponential Backoff And Jitter", AWS Architecture Blog (2015)
- Amazon Builders' Library: Timeouts, retries, and backoff with jitter
- Amazon SQS Developer Guide: Using dead-letter queues
Adapted from The System Design Primer by Donne Martin, licensed under CC BY 4.0. Changes were made.