Skip to content
Latticework
Esc
Try
No. 031Distributed Systems & Data

Backpressure

When a consumer cannot keep up, it must be able to slow the producer — or the system falls over.

When the consumer cannot keep up, the bounded queue fills and the producer is told to slow down instead of buffering without limit.

To an executive

When demand outstrips capacity, a system without limits fails suddenly and loses work. Slowing demand gracefully protects customers from outages and data loss.

To an engineer

Avoid unbounded buffers. Let overloaded consumers signal producers to slow down, using bounded queues, rate limits or load shedding, so overload shows up as slower responses, not a crash.

The idea

In plumbing, pressure that builds up has to go somewhere. In software, the usual answer is an unbounded buffer, which quietly turns overload into memory exhaustion and enormous latency, and then into a crash that loses everything in the buffer.

Backpressure is the alternative: when a component is struggling to keep up, it signals upstream so producers slow down. The Reactive Manifesto describes it as the system as a whole responding sensibly to an overloaded component, rather than that component failing or dropping messages unpredictably. The signal may reach users as slower responses or a "try again later", but the system stays inside its stable operating range.

Why it works

Bounded queues make overload visible. An unbounded queue hides trouble until it is too late. A full bounded queue is a clear, early signal.

Rejecting early is cheaper than failing late. Turning work away at the door costs little. Accepting work you cannot finish wastes resources and often loses the work anyway.

It keeps latency honest. By Little's Law, a growing queue at constant throughput means growing wait times. Capping the queue caps the wait.

Where it applies

  • Message queues and stream processing, where producers can outpace consumers for long periods.
  • APIs and gateways, through rate limits, admission control and early 429 or 503 responses.
  • Internal service calls, where bounded thread and connection pools stop one slow dependency from exhausting a caller.
  • Batch and bulk imports, where a large upload should not starve interactive traffic.

How to apply

  1. Bound every queue and pool. Choose a limit based on how much wait time is acceptable, not how much memory is available.
  2. Decide what happens when full. Block the producer, reject the request, or drop the lowest-priority work, and make the choice explicit.
  3. Propagate the signal. Make sure upstream components see the pushback and slow down, rather than retrying harder.
  4. Shed load by priority. Protect the most important work, such as checkout over recommendations.

Example scenario

A notification service accepted every event into an unbounded in-memory queue. During a large marketing campaign, the producers sent far faster than the email provider would accept. The queue grew for hours until the process ran out of memory and crashed, losing every buffered notification.

The team replaced it with a bounded, durable queue, rate-limited sending to the provider's limit, and made producers back off when the queue was full. Low-priority campaign emails were delayed during the next peak, but transactional emails such as password resets went out on time and nothing was lost.

Questions to ask

  • What happens when the consumer is slower than the producer for an hour?
  • Is every queue and buffer in this path bounded, and what happens when it is full?
  • Which work do we shed first under overload, and who decides?

When not to use it

  • Short, predictable bursts that a modest, bounded buffer can absorb without slowing anyone down.
  • Producers that cannot slow down, such as sensors or market feeds; there you need sampling, dropping or durable storage instead.

How it misleads you

Pushing backpressure all the way to end users when buffering or asynchronous processing would have served them better. A user uploading a file does not need to wait while every downstream system catches up. Decide where in the chain the pressure should stop, and absorb it there with a durable queue.

Further reading

Last reviewed · Suggest an improvement