Cancellation ends a subscriber’s interest in future signals and can trigger cleanup in the publisher chain.
Spring WebFlux cancellation: observe downstream cleanup without undoing work
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.
Observe a separate termination path
The HTTP fixture requests an event stream of five values, receives one value and cancels. ReactiveReceiptController records its doOnCancel callback in an AtomicBoolean, and the test checks that callback ran. Normal completion is covered by a different test because completion and cancellation are different signals.
The callback records a fact; it does not close a database transaction or erase an event already sent. An import service must define who owns its upstream resources and which cleanup method belongs to that resource. A cancellation hook is not a generic compensation mechanism.
Test the actual deployment boundary later
WebTestClient uses a mock controller binding here. Cancelling its response-body subscriber verifies propagation within that chain, not the timing of TCP disconnect detection through a proxy. Real server and network behavior needs separate tests, including clients that stop reading without disconnecting.
Operators may buffer or prefetch. Work already accepted upstream can remain in flight when a downstream subscriber cancels, and a committed side effect remains committed. Keep cancellation-safe operations separate from irreversible writes. Java task cancellation has comparable ownership questions but a different API.
Checked source
var controller = new ReactiveReceiptController();
var response = WebTestClient.bindToController(controller).build()
.get().uri("/contract/events?count=5").exchange()
.expectStatus().isOk()
.returnResult(new ParameterizedTypeReference<ServerSentEvent<String>>() {});
StepVerifier.create(response.getResponseBody()).expectNextCount(1)
.thenCancel().verify(Duration.ofSeconds(3));
assertTrue(controller.cancelled.get());Test the boundary
ReactiveHttpContractTest.downstreamCancellationReachesTheControllerSequence 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
The checked chain has at most five scheduled values and no external resource ownership. Retained operator state and the delay scheduler are specific to this fixture. Network detection delay, pooled connections, cancellation races during writes and deployment load are not measured.
Common Mistakes
- Separate cancellation cleanup from successful completion.
- Do not interpret cancellation as rollback of completed side effects.
- Test the real server and proxy path before promising disconnect timing.
Read next
Spring WebFlux SSE: emit bounded receipt events and test HTTP encoding, Spring reactive foundations: request values and cancel a subscription, Spring transaction events: run a listener after commit without claiming durability, Java cancellation: timed waits and cooperative interruption.
