Back Pressure
A flow control mechanism where a slow consumer signals upstream producers to slow down. Prevents systems from being overwhelmed by data they can't process.
What is Back Pressure?
In short
Back pressure is a flow control mechanism where a slow consumer signals upstream producers to slow down or stop sending data until it can catch up. It stops a fast producer from overwhelming a slower component, which would otherwise cause unbounded memory growth, dropped messages, or a crash.
What back pressure actually is
Imagine a kitchen where the cook plates food faster than the waiters can carry it out. Plates pile up on the pass, then they fall on the floor. Back pressure is the cook pausing until a waiter is free. In software the cook is a producer, the waiter is a consumer, and the pile of plates is a buffer or queue sitting between them.
Any time one part of a system produces work faster than the next part can handle it, the difference has to go somewhere. With no back pressure that somewhere is memory: the queue grows until the process runs out of heap and the operating system kills it, or the system starts dropping data silently. Back pressure replaces that failure mode with a controlled one. The consumer tells the producer how much it can take, and the producer respects that limit.
The signal can flow in two directions of control. In a pull model the consumer asks for the next batch only when it is ready, so the producer is naturally rate limited. In a push model the producer sends freely, and back pressure is a separate signal the consumer pushes back upstream to say slow down or stop.
How it works under the hood
The most common mechanism is a bounded buffer. You set a maximum size on the queue between two stages. When the queue is full, the producer's attempt to add more either blocks until space frees up, or returns a failure the producer must handle. A blocking call is the simplest form of back pressure: the producer's own thread stops making progress until the consumer drains the queue, so the two speeds equalize automatically.
TCP gives you back pressure for free at the network layer. Each connection has a receive window. The receiver advertises how many bytes it is willing to accept right now, and the sender is not allowed to exceed that window. If the receiving application stops reading from the socket, the window shrinks to zero and the sender's writes block. This is why a slow client can throttle a server without any application code.
Reactive libraries make the signal explicit. In the Reactive Streams standard, used by Project Reactor, RxJava, and Akka Streams, the consumer calls request(n) to demand exactly n items. The publisher may never emit more than the outstanding demand. Higher level systems use credits or windows: Kafka consumers pull batches with max.poll.records, gRPC uses HTTP/2 flow control windows per stream, and many message brokers cap unacknowledged in flight messages with a prefetch limit.
When to use it and the trade-offs
Use back pressure anywhere a producer and consumer run at different and variable speeds: ingestion pipelines, log shippers, event streaming, API gateways under burst traffic, and any service that fans out work to a slower backend. The benefit is stability. The system degrades gracefully under overload instead of falling over, and you bound your memory use to a number you chose rather than to how much traffic arrives.
The cost is that slowness propagates backward. If your database is slow, back pressure makes the queue fill, which makes the API layer slow, which makes the load balancer queue, which makes the client wait. That is correct behavior, but it means a single slow component can stall the whole chain. You have to decide what happens at the edge, where there is no further upstream to push against.
That edge decision is the real design choice. The options are: block and make the client wait, shed load by rejecting requests with a 429 or 503, or drop lower priority data and keep the rest. Blocking preserves every request but can exhaust client connections. Load shedding protects the system but loses work. Many production systems combine a bounded buffer with a timeout, so they hold work briefly and shed it only when the backlog proves the slowdown is real.
A concrete example
Suppose you run a service that reads click events from Kafka, enriches each one with a database lookup, and writes the result to a data warehouse. Kafka can deliver hundreds of thousands of events per second. Your database lookup takes 5 milliseconds, so a single thread can do about 200 lookups per second. Without back pressure you would pull events as fast as Kafka serves them, hold them in memory waiting for the slow lookups, and the process would run out of memory within seconds.
With back pressure the consumer pulls in batches and only fetches the next batch after the current one is fully written downstream. The Kafka consumer's poll loop naturally paces itself to the speed of the database. The unprocessed events stay safely on the Kafka brokers, which are built to store them, instead of in your fragile application heap. Your memory stays flat and your throughput settles at the true bottleneck speed.
If the warehouse write also slows down, the back pressure chains all the way back: the writer stops accepting, the enricher stops requesting, the Kafka consumer stops polling, and consumer lag grows on the broker. That growing lag is now your visible signal that something downstream is slow, which is far better than a silent crash and lost events.
Where it is used in production
Apache Kafka
Consumers pull batches at their own pace and cap in flight records with max.poll.records, so a slow consumer just builds visible lag on the broker instead of overwhelming itself.
TCP
The receive window is back pressure at the transport layer: a receiver that stops reading shrinks its window to zero and forces the sender to stop transmitting.
Project Reactor and RxJava
Implement the Reactive Streams request(n) protocol where the subscriber demands an exact number of items and the publisher may never exceed that outstanding demand.
gRPC
Rides on HTTP/2 per-stream flow control windows, so a slow reader on one stream pauses the writer without affecting other streams on the same connection.
Frequently asked questions
- What is the difference between back pressure and rate limiting?
- Rate limiting caps the producer at a fixed rate you choose in advance, like 100 requests per second, regardless of what the consumer is doing. Back pressure is dynamic and consumer driven: the actual consumer reports how much it can take right now, and that capacity changes moment to moment. Rate limiting is a guess at a safe rate; back pressure is the consumer telling you the real one.
- What is the difference between back pressure and buffering?
- Buffering is a queue that absorbs short bursts so a brief speed mismatch does not cause loss. It works until the buffer fills. Back pressure is what you do when the buffer is full: signal the producer to stop. A buffer alone with no back pressure just delays the failure, because a sustained mismatch will eventually overflow it.
- Does back pressure cause cascading slowdowns?
- Yes, by design. Slowness propagates upstream because each stage waits for the next one. That is intentional and safe, but it means one slow component can stall the whole pipeline. You handle this at the entry point by adding timeouts and load shedding, so the system rejects or drops work rather than blocking forever when a backlog persists.
- How do push based systems implement back pressure?
- Since the producer pushes freely, the consumer sends an explicit signal back upstream. Reactive Streams uses request(n) so the consumer demands an exact count. Message brokers use a prefetch or credit limit that caps unacknowledged messages. HTTP/2 and gRPC use flow control windows. In all cases the producer is contractually forbidden from sending more than the consumer has granted.
- What happens if you ignore back pressure entirely?
- The queue between a fast producer and slow consumer grows without bound. The process eventually exhausts memory and is killed by the operating system, or it starts silently dropping data once internal limits are hit. Either way you lose work and reliability, which is exactly the failure mode back pressure is meant to prevent.
Learn Back Pressure hands-on
This page explains the idea. The full lesson lets you step through the ring as servers join and leave, read the implementation, and check yourself with a quiz. It is one of 760+ lessons in the System Design Masterclass, from your first API call to distributed consensus. Eleven Foundation lessons are free, no signup. Lifetime access is ₹499 in India or $7.99 worldwide, one payment, no subscription.
Related lessons
Lessons that touch on Back Pressure as part of a larger topic.
See also
Related glossary terms you might want to look up next.
Message Queue
A buffer that stores messages between producers and consumers. Messages are processed one by one, in order. Think of it as a to-do list for your services.
Throttling
Slowing down the rate of processing requests instead of rejecting them outright. The gentler cousin of rate limiting.
Rate Limiting
Controlling how many requests a client can make in a given time window. Protects your API from abuse and ensures fair usage.
Chaos Engineering
Deliberately injecting failures into a system to test its resilience. Netflix's Chaos Monkey randomly kills servers to ensure the system survives.
SLI
Service Level Indicator: a quantitative measure of service behavior, like the proportion of requests faster than 300ms. The raw metric that feeds SLOs.
SLO
Service Level Objective: a target value for an SLI, like '99.9% of requests under 300ms.' The internal engineering goal that drives reliability investment.