Reactive demand is a subscriber’s request for a bounded number of values from a publisher, while cancellation ends that subscription’s interest in further signals.
Spring reactive foundations: request values and cancel a subscription
The downloadable Spring source kit pins Java 21 and Spring Boot 4.0.8 with its managed dependencies. Run mvn test to check the named fixture.
Begin with zero demand
The Reactor fixture supplies five integer receipt markers. StepVerifier subscribes with an initial request of zero, asks for two values, expects one and two, then cancels. A separate assertion checks the cancellation hook. Another test requests all five and observes completion.
This is a Reactor Core sequence, not a WebFlux HTTP endpoint. Keeping that distinction explicit avoids claiming a transport, request-body buffer limit or network backpressure policy from a small in-memory publisher test. The sequence runs without a scheduler switch or blocking database call.
Demand is not a universal resource limit
An operator can prefetch or buffer upstream work, so a downstream request of two does not imply every upstream resource holds exactly two items. Cancellation also cannot undo a payment or database commit that already happened. Design cleanup and side-effect boundaries independently.
A blocking repository call inside a reactive callback still blocks its executing thread. Replacing an MVC return type with a publisher does not convert that repository into nonblocking I/O. Worker capacity and Cancellation rules remain relevant before moving this model into an HTTP service.
Checked source
package in.aitrove.contracts;
import reactor.core.publisher.Flux;
import java.util.concurrent.atomic.AtomicBoolean;
public class ReactiveReceiptDemand {
public static Flux<Integer> records(AtomicBoolean cancelled) {
return Flux.range(1,5).doOnCancel(() -> cancelled.set(true));
}
}Test the boundary
FrameworkBoundaryTest.demandGatesValuesAndCancelStopsSubscription and completeDemandReceivesAllValues checks this contract in the source kit. Excerpts belong to the named classes; use the downloadable files for imports, configuration and assertions.
Costs and boundaries
Flux.range emits a fixed bounded sequence here; the test requests only two or five values. Buffering, scheduler queues, upstream I/O and concurrent subscriptions are outside this fixture. No throughput or end-to-end memory guarantee follows from passing it.
Common Mistakes
- Do not treat demand as a rollback mechanism.
- Do not hide blocking I/O behind a publisher return type.
- Test cancellation cleanup separately from normal completion.
Read next
Spring task executors: capacity, rejection and lost context, Web APIs, Java cancellation: timed waits and cooperative interruption, Java blocking queues: bounded capacity and backpressure.
Extend the tested workflow
Continue with Java Flow: request items and observe publisher completion, Spring WebFlux SSE: emit bounded receipt events and test HTTP encoding, Spring WebFlux cancellation: observe downstream cleanup without undoing work.
