Chaturmind
LearnDSASystem DesignInterview PrepDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth
  • Java Interview Prep

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java Interview Prep: 8+ Years (Senior & Lead)

Expert Core Java

  • Tricky Java Output, Operators & OOP Edge Cases — Interview Questions
  • Tricky Exceptions, Memory & Keyword Questions — Interview Questions
  • Classic Java Language Questions, Senior-Grade Answers — Interview Questions
  • Classic Collections, Threads & JDK APIs, Senior-Grade Answers — Interview Questions
  • Reflection, Dynamic Proxies, final & Modern OOP Design — Interview Questions

JVM Internals & Performance

  • Class Loading, Bytecode & Object Layout — Interview Questions
  • JIT Compilation & Runtime Optimisations — Interview Questions
  • Garbage Collectors Deep Dive — Interview Questions
  • JVM Tuning, GC Logs & Memory Footprint — Interview Questions
  • Memory Leaks, OutOfMemoryErrors & Profiling Tools — Interview Questions
  • Modules, Agents & Advanced JVM APIs — Interview Questions

Collections & Concurrency at Scale

  • Collections Internals & Complexity — Interview Questions
  • Iterators, Comparators & Ordering Contracts — Interview Questions
  • Concurrent Collections, Queues & Lock-Free Structures — Interview Questions
  • Threads, Executors & ForkJoin Internals — Interview Questions
  • Locks, Atomics, CAS & Synchronizers — Interview Questions
  • Java Memory Model, volatile, Fences & ThreadLocal — Interview Questions
  • Deadlock, Livelock, Starvation & Concurrent Design — Interview Questions
  • CompletableFuture, Parallel Streams & Non-Blocking I/O — Interview Questions

Modern Java (8 to 21+)

  • Lambdas & Functional Interfaces Internals — Interview Questions
  • Streams & Collectors Deep Dive — Interview Questions
  • Optional & Interface Default/Static Methods — Interview Questions
  • Java 9–25 Features & Virtual Threads — Interview Questions

Design Patterns, SOLID & Clean Code

  • Design Pattern Trade-offs & Combinations — Interview Questions
  • SOLID, Clean Code & Anti-Patterns — Interview Questions

Spring & Spring Boot Internals

  • IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions
  • Spring AOP, Proxies & @Async Internals — Interview Questions
  • Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions
  • Spring MVC & REST Internals, Exception Frameworks — Interview Questions
  • Spring Security Advanced Internals — Interview Questions
  • Spring WebFlux, Reactor & R2DBC — Interview Questions
  • Spring Cloud, Observability & Distributed Tracing — Interview Questions
  • Spring Boot 3, Native Images & Production Scenarios — Interview Questions

JPA, Hibernate & Databases at Scale

  • Spring Data JPA — Queries, Projections, Custom Repositories & Locking — Interview Questions
  • JPA Entity Mapping, Associations & Cascades — Interview Questions
  • JPQL vs Native Queries in Depth — Interview Questions
  • Hibernate Caching — First-Level, Second-Level & Query Cache — Interview Questions
  • Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions
  • JPA Transactions, Propagation, Isolation & Dirty Checking — Interview Questions
  • SQL vs NoSQL, Indexing & Query Tuning — Interview Questions
  • Database Scaling, Replication, Pooling & Consistency Models — Interview Questions
  • Redis, Search, Time-Series, CDC & Transactional Data Modelling — Interview Questions

Testing Strategy & API Design

  • Spring Boot Test Slices, Context & Test Strategy — Interview Questions
  • Testing Web, Persistence, Security, Async & Messaging in Spring Boot — Interview Questions
  • JUnit 5 & Mockito, Advanced — Interview Questions
Chaturmind
← Java Interview Prep: 8+ Years (Senior & Lead)

Expert Core Java

  • Tricky Java Output, Operators & OOP Edge Cases — Interview Questions
  • Tricky Exceptions, Memory & Keyword Questions — Interview Questions
  • Classic Java Language Questions, Senior-Grade Answers — Interview Questions
  • Classic Collections, Threads & JDK APIs, Senior-Grade Answers — Interview Questions
  • Reflection, Dynamic Proxies, final & Modern OOP Design — Interview Questions

JVM Internals & Performance

  • Class Loading, Bytecode & Object Layout — Interview Questions
  • JIT Compilation & Runtime Optimisations — Interview Questions
  • Garbage Collectors Deep Dive — Interview Questions
  • JVM Tuning, GC Logs & Memory Footprint — Interview Questions
  • Memory Leaks, OutOfMemoryErrors & Profiling Tools — Interview Questions
  • Modules, Agents & Advanced JVM APIs — Interview Questions

Collections & Concurrency at Scale

  • Collections Internals & Complexity — Interview Questions
  • Iterators, Comparators & Ordering Contracts — Interview Questions
  • Concurrent Collections, Queues & Lock-Free Structures — Interview Questions
  • Threads, Executors & ForkJoin Internals — Interview Questions
  • Locks, Atomics, CAS & Synchronizers — Interview Questions
  • Java Memory Model, volatile, Fences & ThreadLocal — Interview Questions
  • Deadlock, Livelock, Starvation & Concurrent Design — Interview Questions
  • CompletableFuture, Parallel Streams & Non-Blocking I/O — Interview Questions

Modern Java (8 to 21+)

  • Lambdas & Functional Interfaces Internals — Interview Questions
  • Streams & Collectors Deep Dive — Interview Questions
  • Optional & Interface Default/Static Methods — Interview Questions
  • Java 9–25 Features & Virtual Threads — Interview Questions

Design Patterns, SOLID & Clean Code

  • Design Pattern Trade-offs & Combinations — Interview Questions
  • SOLID, Clean Code & Anti-Patterns — Interview Questions

Spring & Spring Boot Internals

  • IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions
  • Spring AOP, Proxies & @Async Internals — Interview Questions
  • Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions
  • Spring MVC & REST Internals, Exception Frameworks — Interview Questions
  • Spring Security Advanced Internals — Interview Questions
  • Spring WebFlux, Reactor & R2DBC — Interview Questions
  • Spring Cloud, Observability & Distributed Tracing — Interview Questions
  • Spring Boot 3, Native Images & Production Scenarios — Interview Questions

JPA, Hibernate & Databases at Scale

  • Spring Data JPA — Queries, Projections, Custom Repositories & Locking — Interview Questions
  • JPA Entity Mapping, Associations & Cascades — Interview Questions
  • JPQL vs Native Queries in Depth — Interview Questions
  • Hibernate Caching — First-Level, Second-Level & Query Cache — Interview Questions
  • Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions
  • JPA Transactions, Propagation, Isolation & Dirty Checking — Interview Questions
  • SQL vs NoSQL, Indexing & Query Tuning — Interview Questions
  • Database Scaling, Replication, Pooling & Consistency Models — Interview Questions
  • Redis, Search, Time-Series, CDC & Transactional Data Modelling — Interview Questions

Testing Strategy & API Design

  • Spring Boot Test Slices, Context & Test Strategy — Interview Questions
  • Testing Web, Persistence, Security, Async & Messaging in Spring Boot — Interview Questions
  • JUnit 5 & Mockito, Advanced — Interview Questions
HomeLearnJava Interview PrepJava Interview Prep: 8+ Years (Senior & Lead)Spring & Spring Boot Internals
✓ FreeAdvanced· 11 min read

Spring WebFlux, Reactor & R2DBC — Interview Questions

Mono vs Flux with real use cases, back-pressure in WebFlux, why Netty (and not a blocking servlet model), error handling with @ControllerAdvice, onErrorResume and onErrorMap, RouterFunction vs annotated controllers, reactive database access challenges and best practices, R2DBC vs JDBC, reactive transactions, WebTestClient, blocking code inside WebFlux, flatMap vs concatMap vs switchMap, Reactor Context vs ThreadLocal, and WebClient vs RestTemplate/RestClient performance.

Published September 25, 2026


How to use this lesson

Reactive interviews test mental models:

  • nothing happens until subscribe;
  • everything must be non-blocking;
  • operators decide concurrency and ordering.

Pair every answer with the pragmatic view: for most CRUD services on Java 21+, MVC with virtual threads is simpler. WebFlux shines for streaming and high fan-out.

Q1. What's the difference between Mono and Flux? Give real use cases for both.

Short answer: Both are Reactor Publishers (Reactive Streams):

  • Mono<T>: 0 or 1 element, then completion or an error. It's the async equivalent of Optional/CompletableFuture. Uses: fetch a user by ID, save an entity, call one downstream API, Mono<Void> for fire-and-complete operations.
  • Flux<T>: 0..N elements (possibly infinite), then completion or an error. Uses: query results streamed row by row, server-sent events (live prices, notifications), Kafka message streams, WebSocket messages, paginated API aggregation, and file-line processing.
@GetMapping("/orders/{id}")
Mono<OrderDto> one(@PathVariable UUID id) { return orders.findById(id).map(mapper::toDto)
        .switchIfEmpty(Mono.error(new OrderNotFound(id))); }

@GetMapping(value = "/orders/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
Flux<OrderEvent> stream() { return events.asFlux(); }        // pushed to the client as they occur

Q2. How is back-pressure handled in WebFlux (and reactive streams)?

Short answer:

  • Inside the application: Reactive Streams request(n) demand propagates upstream through the Reactor operators. Operators have bounded prefetch queues (limitRate, prefetch parameters), so a slow consumer slows the producers. For sources that can't slow down, you choose an overflow strategy: onBackpressureBuffer(max), Drop, Latest, or sampling.
  • At the network edge: WebFlux maps demand onto TCP flow control. The server only reads or writes as fast as the socket accepts, so a slow HTTP client naturally throttles a streaming Flux.
  • The caveat: HTTP request-response APIs, JSON array responses, and many databases or protocols can't carry demand signals across the network. There, back-pressure is only partial. RSocket and reactive database drivers (R2DBC, Mongo) propagate demand end to end.

Q3. What role does Netty play in WebFlux, and why not Tomcat?

Short answer: WebFlux needs a non-blocking server. Reactor Netty (the default) is built on Netty's event loops: a few threads (about the number of cores) handle all the connections, driven by epoll or kqueue readiness events. That fits the reactive model perfectly, and supports HTTP/2, WebSockets, SSE and back-pressure natively.

Tomcat, Jetty or Undertow can run WebFlux through the Servlet 3.1+ non-blocking I/O API. It works, but you lose some efficiency and features compared with native Netty. Their traditional thread-per-request blocking model is what Spring MVC uses.

Key points to cover:

  • With Netty, blocking an event-loop thread stalls many connections. That's why blocking calls are forbidden in WebFlux handlers.

Q4. How do you handle exceptions in WebFlux: @ControllerAdvice, or onErrorResume()?

Short answer: Both, at different levels:

  • Global HTTP mapping: @RestControllerAdvice with @ExceptionHandler works for annotated controllers in WebFlux too, returning ProblemDetail/ResponseEntity/Mono. For functional routes, use a WebExceptionHandler or ErrorWebExceptionHandler (Boot provides a default one).
  • Inside the pipeline (recovery and translation):
    • onErrorResume(fn): recover by switching to a fallback publisher (cached data, a default, or another source);
    • onErrorReturn(value): a static fallback;
    • onErrorMap(fn): translate the exception (for example WebClientResponseException.NotFound to a domain ProductNotFound), which is still an error;
    • doOnError for logging, retryWhen(Retry.backoff(...)) for transient failures, and timeout(...).
Mono<Price> price(String sku) {
    return pricingClient.get(sku)
        .timeout(Duration.ofMillis(500))
        .retryWhen(Retry.backoff(2, Duration.ofMillis(100)).filter(ex -> ex instanceof WebClientRequestException))
        .onErrorMap(WebClientResponseException.NotFound.class, ex -> new PriceUnavailable(sku))
        .onErrorResume(TimeoutException.class, ex -> cache.lastKnown(sku));      // degraded, but served
}

Common trap: onErrorResume without a type predicate swallows every error, including bugs.

Q5. What is RouterFunction, and when would you use it instead of annotations?

Short answer: Functional endpoints (WebFlux, and also Spring MVC since 5.2) define routes as code: RouterFunctions.route().GET("/orders/{id}", handler::get).POST(...).filter(...).build(), with handler functions ServerRequest → Mono<ServerResponse>. Use them when:

  • you want explicit, composable routing (nesting, programmatic conditions, per-route filters);
  • the routes are dynamic or generated;
  • you prefer a lightweight, annotation-free style (and faster startup, which suits native images);
  • you're building small, functional services.

Annotated controllers are more familiar and declarative, and integrate easily with validation and OpenAPI tooling. Both can coexist.

Q6. What are the challenges of database access in reactive programming, and what are the best practices?

Short answer:

  • The challenges:
    • JDBC is blocking. Calling JPA or JDBC inside a reactive chain blocks the event loops, so throughput collapses.
    • You need reactive drivers (R2DBC for SQL, reactive MongoDB, Cassandra or Redis), which have fewer features (no JPA: no lazy loading, no dirty checking, no second-level cache) and less mature tooling.
    • Transactions need reactive transaction managers, and the context is carried in the Reactor Context, not in ThreadLocals.
    • Debugging and stack traces are harder.
    • Connection pools (r2dbc-pool) still bound the concurrency.
  • Best practices:
    • Use Spring Data R2DBC or reactive repositories end to end.
    • If you must call blocking code, isolate it with Mono.fromCallable(...).subscribeOn(Schedulers.boundedElastic()), with limits. Treat that as a smell.
    • Stream large results (Flux), rather than collecting lists.
    • Keep transactions short.
    • Use BlockHound in tests to detect blocking calls.
    • Choose MVC with virtual threads when JPA's features matter more than reactive streaming.

Q7. How does R2DBC differ from JDBC? How do you implement it?

Short answer:

  • JDBC: blocking calls, thread-per-connection usage, a mature ecosystem (JPA/Hibernate, many drivers).
  • R2DBC: Reactive Relational Database Connectivity. Non-blocking drivers returning Publishers (Postgres, MySQL, MariaDB, SQL Server, Oracle, H2), with back-pressure support, driven by the database protocol. It's not an ORM: Spring Data R2DBC offers simple entity mapping and repositories, but no lazy loading, relationships management or dirty checking.

The implementation:

  1. Add spring-boot-starter-data-r2dbc plus a driver.
  2. Set spring.r2dbc.url=r2dbc:postgresql://….
  3. Use ReactiveCrudRepository, R2dbcEntityTemplate, or DatabaseClient for SQL.
  4. Use Flyway or Liquibase through JDBC for migrations (they need a JDBC URL too).
interface OrderRepository extends ReactiveCrudRepository<OrderRow, UUID> {
    Flux<OrderRow> findByCustomerIdOrderByCreatedAtDesc(UUID customerId);
}
Flux<OrderSummary> summaries = client.sql("SELECT id, total FROM orders WHERE status = :s")
        .bind("s", "PAID").map((row, meta) -> new OrderSummary(row.get("id", UUID.class), row.get("total", BigDecimal.class)))
        .all();

Q8. What is a reactive transaction? How is reactive transaction management different?

Short answer: In reactive code, a transaction can't be bound to a thread (ThreadLocal), because a pipeline hops between threads. Spring uses a ReactiveTransactionManager (R2dbcTransactionManager, ReactiveMongoTransactionManager), and stores the transaction state in the Reactor Context, tied to the subscription. Two ways to use it:

  • @Transactional on methods returning Mono/Flux: the transaction begins on subscription, and commits on completion, or rolls back on error or cancellation;
  • TransactionalOperator: operator.transactional(publisher), or execute(...), programmatically.

The caveats:

  • the transaction only spans what's inside the returned publisher, so work launched separately (subscribe() inside) escapes it;
  • cancellation rolls back;
  • don't mix blocking JPA transactions with reactive ones.

Q9. How do you test reactive endpoints with WebTestClient?

Short answer: WebTestClient is a non-blocking test client with a fluent assertion API:

  • @WebFluxTest(OrderController.class), plus a mocked service (@MockitoBean), for a slice test;
  • or @SpringBootTest(webEnvironment = RANDOM_PORT) for a full server test;
  • or WebTestClient.bindToRouterFunction(...)/bindToController(...).

Test the pipelines themselves with StepVerifier (Reactor Test): expectNext, expectError, thenCancel, and virtual time for delays. (WebTestClient can also test Spring MVC applications.)

@WebFluxTest(OrderController.class)
class OrderControllerTest {
    @Autowired WebTestClient client;
    @MockitoBean OrderService service;

    @Test void returnsOrder() {
        given(service.get(ID)).willReturn(Mono.just(new OrderDto(ID, "PAID")));
        client.get().uri("/orders/{id}", ID).exchange()
              .expectStatus().isOk()
              .expectBody().jsonPath("$.status").isEqualTo("PAID");
    }
}

Q10. Can you use traditional blocking code in WebFlux? What are the trade-offs?

Short answer: Technically yes, but carefully. Blocking on a Netty event-loop thread (JDBC, RestTemplate, Thread.sleep, .block()) stalls every connection served by that loop, which can collapse throughput, or deadlock. If unavoidable:

  • offload it: Mono.fromCallable(blockingCall).subscribeOn(Schedulers.boundedElastic()). That's a bounded thread pool (10 × cores by default) designed for blocking work;
  • limit its concurrency, and accept that those calls use threads like MVC does. You lose the reactive benefit for that path;
  • never call .block() inside reactive handlers (Reactor throws IllegalStateException on non-blocking threads);
  • detect it with BlockHound in tests.

If most dependencies are blocking, Spring MVC with virtual threads is the better architecture.

Q11. What's the difference between flatMap, concatMap and switchMap?

Short answer: All three map each element to an inner publisher, and flatten the results. They differ in concurrency and ordering:

  • flatMap: subscribes to the inner publishers eagerly and concurrently (bounded by the concurrency parameter, default 256), and interleaves their results. Order isn't preserved. Use it for maximum throughput: parallel API calls per element.
  • concatMap: processes the inner publishers one at a time, in order. It waits for each to complete before starting the next. The order is preserved, and it's slower. Use it for sequential side effects, or ordering-sensitive processing.
  • flatMapSequential: concurrent subscription, but the results are re-ordered to match the source.
  • switchMap: when a new element arrives, it cancels the previous inner publisher, and switches to the new one. Only the latest matters. Use it for search-as-you-type, "latest value" streams, and refresh triggers.
Flux<Price> prices = skus.flatMap(sku -> pricing.price(sku), 16);   // 16 concurrent calls, unordered
Flux<Result> ordered = commands.concatMap(this::applyInOrder);       // strict order
Flux<Suggestions> live = queryChanges.switchMap(q -> search.suggest(q));   // cancel stale searches

Q12. What is Reactor's Context, and what problems does it solve with ThreadLocal?

Short answer: In reactive pipelines, work hops threads (event loops, schedulers). So ThreadLocal-based context (MDC trace IDs, security context, transactions, tenant IDs) gets lost, or leaks between requests that share threads. Reactor Context is an immutable key-value map attached to the subscription:

  • it's written with contextWrite(ctx -> ctx.put("tenant", t)), downstream, and is visible upstream;
  • it's read with Mono.deferContextual(ctx -> ...).

Bridging to ThreadLocal-based libraries (logging MDC, Micrometer tracing): use context propagation (Hooks.enableAutomaticContextPropagation(), together with Micrometer's context-propagation library). It restores ThreadLocals around operators automatically. Spring Security's WebFlux support and reactive transactions both use the Context.

Q13. What's the performance difference between WebClient and RestTemplate (and RestClient)?

Short answer:

  • RestTemplate: synchronous, blocking, one thread per in-flight call. It's in maintenance mode.
  • RestClient (Spring 6.1): the modern synchronous fluent API, on the same blocking model. It's the recommended choice for MVC, especially with virtual threads, where blocking is cheap.
  • WebClient: non-blocking, reactive, and Netty-based. It handles many concurrent calls with few threads, supports streaming and back-pressure, and composes easily for fan-out (Mono.zip, flatMap). It's the choice in WebFlux, or for massive concurrent fan-out on platform threads.

Performance:

  • per-call latency is similar (the network dominates);
  • the difference is resource efficiency under high concurrency: WebClient needs far fewer threads;
  • with virtual threads, RestClient approaches WebClient's scalability with simpler code;
  • pool and timeout configuration matters more than the choice of client.

Common trap: using WebClient with .block() in an MVC application gains nothing, and adds complexity.

Follow-up questions this topic invites — and their answers

Q: What does "nothing happens until you subscribe" mean in practice? A: Building a Mono or Flux chain only assembles it. The HTTP call or database query runs when something subscribes (WebFlux does this for handler return values). Forgetting to return or subscribe to a publisher means the work silently never happens.

Q: What are publishOn and subscribeOn? A: subscribeOn chooses the scheduler for the subscription (it affects the source, wherever it's placed). publishOn switches the thread for downstream operators, from that point on. Use boundedElastic for blocking work, and parallel for CPU work.

Q: Hot vs cold publishers? A: Cold publishers (most Flux/Mono, like HTTP or database calls) start their work for each subscriber. Hot publishers (Sinks, share(), and live event streams) emit regardless of subscribers, and late subscribers miss the earlier elements.

Q: When is WebFlux clearly the right choice? A: Streaming (SSE, WebSockets), very high concurrency with slow or long-lived connections, API gateways (Spring Cloud Gateway), and end-to-end reactive stacks (reactive database plus reactive messaging).

Previous

Spring Security Advanced Internals — Interview Questions

Next

Spring Cloud, Observability & Distributed Tracing — Interview Questions

AI Tutor

Lesson: Spring WebFlux, Reactor & R2DBC — Interview Questions

Quick actions

AI responses can be inaccurate. Verify critical information.