Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Spring WebFlux cancellation: observe downstream cleanup without undoing work

Last updated: 29 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

Cancellation ends a subscriber’s interest in future signals and can trigger cleanup in the publisher chain.

Download Spring source kit

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

Java
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.

spring
spring-boot
webflux-cancellation
Storage details