Skip to main content
KBY Technologies Logo
cd ../lexicon
sys/docs/lexicon/head-of-line-blocking-hol-blocking.md
Lexicon Entry

Head-of-Line Blocking (HOL Blocking)

A stalled unit of work at the front of a strictly ordered queue or channel blocks all independent units queued behind it, even though they are individually ready to proceed.

Practical example

A single-partition Kafka consumer processes messages sequentially; one message triggers a slow downstream call, and every subsequent message in that partition queues behind it despite being unrelated and independently processable — resolved by partitioning on an independent key rather than adding more consumer threads.

HOL blocking occurs at any layer that imposes strict FIFO ordering on a shared channel while multiplexing logically independent streams over it. The canonical case is TCP: when multiple application-level streams (e.g. multiple HTTP/2 requests) are multiplexed over a single TCP connection, a single lost segment forces the kernel’s receive buffer to withhold all subsequent segments, including those belonging to unrelated streams that arrived intact, until retransmission and in-order delivery completes. This is why HTTP/2, despite offering stream multiplexing at the application layer, remains vulnerable to HOL blocking at the transport layer — a key motivation for QUIC/HTTP3, which multiplexes independent streams with independent loss recovery, so one stream’s retransmit does not stall the others.

The pattern recurs far above the transport layer. Connection pooling against a backend with synchronous, ordered response semantics (e.g. Redis pipelining, or older MySQL protocol implementations) exhibits the same failure mode: a slow query at the head of the pipeline blocks all queued responses behind it, even on an otherwise idle connection. Similarly, single-threaded event loop workers (or any actor with an unbounded mailbox) suffer HOL blocking when one message triggers a long-running or blocking syscall — every subsequent message, regardless of priority or independence, waits.

  • Load balancer / connection reuse: keep-alive connections to an upstream that itself serializes handling internally reintroduce HOL blocking even if the client-side multiplexing layer is HOL-free.
  • Message queues: a single-partition Kafka consumer processing messages sequentially will stall all downstream consumption behind one poison-pill or slow-to-process message; this is why partitioning by independent keys is the primary mitigation, not consumer parallelism alone.
  • gRPC streaming: server-streaming RPCs multiplexed over HTTP/2 inherit the transport-level vulnerability described above under packet loss, particularly visible on lossy or high-RTT links.

Mitigations generally fall into three categories: eliminate shared ordering constraints (per-stream loss recovery as in QUIC, per-partition parallelism in queues), bound the blast radius (per-connection timeouts, circuit breakers, dedicated connection pools per logical tenant/priority class), or make ordering explicit and cheap to skip (out-of-order acknowledgment protocols, request pipelining with response correlation IDs rather than strict FIFO). Diagnostically, HOL blocking is notoriously difficult to spot from aggregate throughput metrics alone — it presents as elevated p99/p999 latency with normal p50 and normal error rates, and is often misattributed to GC pauses or backend slowness before the shared-queue serialization is identified as the root cause.