Skip to main content

Head-of-Line Blocking

Head-of-line blocking lets one slow item at the front of an ordered queue stall all the ready items behind it, so tail latency tracks the worst case. Use multiplexing, per-key or priority queues, timeouts, and isolation so independent work proceeds in parallel.

Head-of-line (HOL) blocking happens when work is processed in strict order from a single queue, so the item at the front blocks every item behind it. If that front item is slow, stuck, or large, all the ready-to-go items waiting behind it are delayed for no reason of their own. The term is well known from networking (HTTP/1.1 pipelining, TCP under loss) but the pattern appears anywhere a single ordered channel carries mixed work.

Why It Happens

A single FIFO queue is the simplest fair-looking design: process in arrival order. It is correct and easy to reason about. The trouble starts when the queue carries a mix of fast and slow work, or when one consumer pulls from one ordered stream. A batch of small requests stuck behind one giant request, a connection pool serializing on one slow query, or a partitioned log where one slow consumer holds a partition — all are HOL blocking. It is fine when items are uniform and fast, and only bites with variance.

Why It Hurts

Latency for fast items becomes hostage to the slowest item ahead of them, so p99 latency tracks the worst case rather than the typical case. Throughput stalls while resources sit idle behind the blockage — the system is not busy, it is waiting. A single pathological or malicious slow request can degrade everyone sharing the queue, an availability and fairness problem. It also defeats parallelism: capacity exists but is gated behind one slot.

Warning Signs

  • A single FIFO queue or channel carrying both quick and slow work.
  • One slow request visibly delaying many unrelated ones.
  • p99 latency dominated by rare slow items rather than the median.
  • Idle workers or connections while a queue backs up.

Better Alternatives

  • Multiplexing / independent streams: HTTP/2 and HTTP/3 (QUIC) let many requests share a connection without one blocking another.
  • Multiple queues or worker lanes: separate fast and slow work, or shard by key so one key's backlog does not block others.
  • Priority queues: let short or urgent work jump ahead of long-running items.
  • Timeouts and isolation (bulkheads): cap how long any one item can hold the line; isolate slow tenants.
  • Out-of-order processing where ordering is not actually required.

How to Refactor Out of It

Decide whether strict ordering is genuinely required; often it is not, and processing can proceed out of order or in parallel. If ordering matters only per key, shard the queue by key so independent keys flow concurrently while each key stays ordered. Separate slow work into its own lane so it cannot block fast work, and add timeouts so a stuck item is evicted rather than holding the line forever. At the transport layer, adopt HTTP/2 or HTTP/3 multiplexing. Verify that p99 latency decouples from the slowest item after the change.