Backpressure — what is it and how does Reactor handle it? — Cracked Java
// Spring Framework & Spring Boot · Reactive — WebFlux, Reactor
SeniorTheoryBig Tech

Backpressure — what is it and how does Reactor handle it?

Backpressure is the mechanism by which a slow consumer tells a fast producer to slow down, so the producer can't overwhelm it. In Reactive Streams it's not a bolt-on — it's the core of the Subscription.request(n) contract: the subscriber signals how many elements it can handle, and a well-behaved publisher emits no more than the outstanding demand.

Why it matters

Without backpressure, a source emitting faster than you can process (a firehose database cursor, a busy message topic) forces you to either buffer unboundedly until you OOM, or drop data silently. Backpressure makes the flow-control explicit and bounded.

How Reactor wires it for you

Most operators handle demand automatically. When you write flux.map(...).subscribe(...), the final subscriber requests Long.MAX_VALUE (unbounded) by default, and operators propagate demand upstream. A truly reactive source (R2DBC, a reactive Kafka consumer) honours that demand and fetches lazily. The problem case is a source that can't be slowed — a UI event stream, a sensor — where production is inherently faster than consumption.

Strategies for unstoppable sources

For sources you can't pause, Reactor gives explicit overflow strategies:

Flux<Tick> ticks = ...;

ticks.onBackpressureBuffer(1000)   // queue up to N, then error/drop
     .onBackpressureDrop(dropped -> log.warn("dropped {}", dropped))
     .onBackpressureLatest();      // keep only the most recent, discard the rest
  • onBackpressureBuffer — buffer overflow elements (bounded or unbounded; unbounded risks OOM).
  • onBackpressureDrop — discard new elements the consumer isn't ready for.
  • onBackpressureLatest — drop everything but the newest value (great for "current price"-style streams).

The default with no strategy on an overproducing source is an OverflowException.

Controlling demand yourself

You can shape demand from the subscriber side:

flux.limitRate(100)      // request in batches of 100, not MAX_VALUE
    .subscribe(...);

limitRate(n) is the clean way to cap how much an unbounded subscriber pulls at once, which also bounds upstream prefetch.

Mark your status