Concept · Async work & messaging

Backpressure

Let a slow consumer slow the producer down instead of drowning in a growing queue.

2 minflow controlbounded queue

Backpressure is any mechanism by which a component that can't keep up signals upstream to send less. Without it, the excess piles up somewhere — usually in an unbounded queue or buffer — until memory runs out or every item waits so long that it's useless.

Why unbounded queues fail

By Little's law, wait time = queue length / processing rate. If producers send 1,200 messages per second and consumers handle 1,000, the queue grows by 200 per second; after ten minutes, a new message waits two minutes. For a request–response system, that means every request times out — the server is busy, but doing work nobody will receive.

Forms of backpressure

  • Bounded queues that block or reject when full. In Go, a buffered channel blocks the sender when it's full: that is backpressure.
  • Explicit rejection: return 429 or 503 with Retry-After so callers slow down (load shedding).
  • Flow control in protocols: TCP's receive window, HTTP/2 stream windows and gRPC streaming all stop the sender when the receiver's buffer fills.
  • Pull-based consumption: consumers fetch when ready (Kafka consumers, reactive streams with demand signals), so a slow consumer simply falls behind rather than being overwhelmed.

In asynchronous pipelines

With a durable log, falling behind is safe for a while — the backlog is stored — but you must notice it:

  • Monitor consumer lag (messages or seconds behind the head) and alert when it grows.
  • Scale consumers with lag, up to the partition count.
  • Decide what happens if lag exceeds retention: messages are deleted unread.

The principle

Every queue should have a bound and a policy for what happens at the bound: block, reject, drop the oldest, or drop the newest. Choosing that policy deliberately is the difference between graceful overload and collapse.

Learn it in depth