7 queues, 7 DLQs: what failure handling actually looks like

The obvious design for a multi-stage LLM pipeline is one queue with a type field on the message. It is simpler, and it is wrong for the same reason a single thread pool is wrong.

We ran seven queues — six FIFO, one standard for the pure-compute stage — one per pipeline stage, each with its own dead-letter queue.

The stages have genuinely different shapes. Transcription is long and I/O-bound. Speaker identification is a cascade of cheap checks with one expensive vision call at the end. Video compilation is pure CPU and doesn't need ordering — the one standard queue in an otherwise FIFO fleet. Share a queue and you share a retry policy, a concurrency limit, and a scaling signal, and the slowest stage sets the pace for all of them.

Separate queues mean a backed-up stage backs up alone.

The dead-letter queues matter more than the queues. Bounded retries — three attempts, maxReceiveCount: 3 — then the message moves to its DLQ with fourteen-day retention, so a poison message stops costing money within seconds instead of retrying until someone notices the bill. Alarms fire on any DLQ depth of one, or a queue depth over a hundred.

What made seven queues survivable is that no worker implements any of this itself. One shared pool library under all nine handles the parts everyone gets wrong: a visibility heartbeat extending the lease seven minutes every three, so a long job isn't redelivered while it's still running; a circuit breaker at a 50% error rate in a 30-second window; scale-in protection enabled on job start and cleared in a finally, so the autoscaler can't kill a task mid-message; and a graceful SIGTERM drain that waits up to thirty seconds for in-flight work. Each worker entrypoint is about fifty lines of glue on top.

The cost is operational surface: seven sets of alarms and retry policies to misconfigure instead of one, plus a shared library whose bugs are everyone's bugs at once. Worth it — "what is broken" became answerable by looking at which DLQ has depth, rather than by grepping logs.


← All writing