flatMap Concurrency and Prefetch — Tuning Reactor Throughput
Fast recall
Quick revision
Core answer
concurrency argument caps how many inner publishers run at once — directly controlling parallel I/O. The prefetch argument sets how many items each operator requests from upstream in a batch (default 256). For I/O-bound pipelines (HTTP, R2DBC), tune concurrency to match the downstream pool size; for CPU-bound work, use parallel() instead. Lower prefetch reduces memory at the cost of more upstream request() calls. The wrong defaults cause two opposite failures: too low concurrency under-uses capacity, too high concurrency exhausts connection pools or upstream rate limits.Whether you can reason about Reactor's internal demand mechanics and tune them against real-world resource limits (DB pools, HTTP semaphores, rate limits)
Treating `flatMap` as if it had unbounded concurrency in old Reactor versions, or leaving the default 256 against a 10-connection pool
Discuss `flatMapSequential` vs `concatMap` ordering, when to switch to `parallel().runOn(scheduler)`, and how prefetch tuning affects memory under bursty loads
Interview-ready guide
Interview answer
flatMap is Reactor's main concurrency knob, and its defaults — concurrency 256, prefetch 256 — are dangerous for I/O against small pools.
The two arguments:
concurrencycaps in-flight inner publishers; when full, source items wait, naturally generating backpressureprefetchis the upstream batch size forrequest()calls; default 256 is fine for most workloads
Choosing concurrency:
- HTTP fan-out → match WebClient pool size
- DB fan-out → match R2DBC pool size
- Rate-limited API →
rate_limit × p99_latency(Little's Law) - In-memory transforms → leave default
flatMap vs flatMapSequential vs concatMap:
flatMapfor max throughput, no orderingflatMapSequentialfor ordered emissions with concurrent execution and a buffering memory costconcatMapfor strict serial when ordering is sacred
flatMap vs parallel:
flatMap(fn, N)for I/O-bound — calls that mostly waitparallel(N).runOn(Schedulers.parallel())for CPU-bound transforms- For blocking calls, wrap with
Mono.fromCallable(...).subscribeOn(boundedElastic())inside aflatMap
Production diagnostic: if p99 latency grows while throughput stays flat, downstream is saturated — reduce concurrency. If throughput plateaus far below downstream capacity, increase it. The 80/20 of tuning is just matching flatMap concurrency to the downstream pool.
flatMap Concurrency and Prefetch — Tuning Reactor Throughput
Direct Answer
flatMap(fn, concurrency, prefetch) is Reactor's main throughput knob. The concurrency argument caps how many inner publishers run at once — directly controlling parallel I/O. The prefetch argument sets how many items each operator requests from upstream in a batch (default 256). For I/O-bound pipelines (HTTP, R2DBC), tune concurrency to match the downstream pool size; for CPU-bound work, use parallel() instead. Lower prefetch reduces memory at the cost of more upstream request() calls. The wrong defaults cause two opposite failures: too low concurrency under-uses capacity, too high concurrency exhausts connection pools or upstream rate limits.
Common Mistakes & Pitfalls
Treating flatMap as if it had unbounded concurrency in old Reactor versions, or leaving the default 256 against a 10-connection pool
Check your understanding
Answer these without looking back at the article.
- When should you use concatMap instead of flatMap, and what does it cost?
- How does Little's Law help you size flatMap concurrency for a rate-limited API?
- What's the difference between flatMap(fn, N) and parallel(N).runOn(parallel())?
- How do you diagnose a Reactor pipeline that is starving a downstream connection pool?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.