Scheduler shutdown stops future polling callbacks; it does not automatically reverse an external operation already in flight.
Spring TaskScheduler shutdown: stop new polls and account for in-flight work
The checked lifecycle
The local test initializes one ThreadPoolTaskScheduler, schedules a fixed-delay poller, waits for one delivered event, cancels the returned ScheduledFuture and calls shutdown in a finally block. The cancelled future reports cancelled. This is enough to keep the test from leaving a polling thread behind.
The test does not start a graceful deployment drain, wait for a slow transport, renew an expiring lease, or restart after a kill. A callback could be inside a remote publish when the process receives a stop signal. The lease must expire or renew; the consumer marker must handle a retry.
Design the stop budget
For a service, stop accepting new claims, allow current callbacks a bounded completion window, and leave unacknowledged rows recoverable. A forced termination may happen at any point. Do not mark work delivered merely because a shutdown hook ran. Configure scheduler lifecycle with the application context and measure drain duration under realistic transport timeouts.
A callback that catches every exception without recording state can hide lost work. Keep the row transition, error category and event ID available for operators. Backlog age will show stalled recovery after a restart.
Checked source
var future = scheduler.scheduleWithFixedDelay(poller, Duration.ofMillis(20));
try { assertTrue(delivered.await(3, TimeUnit.SECONDS)); }
finally { future.cancel(false); scheduler.shutdown(); }Verification boundary
ScheduledOutboxPollerTest.scheduledWorkerConsumesCommittedRowAndStops. The excerpt is shortened or a labelled design sketch; the source kit contains the complete checked fixture.
Costs and limits
The test verifies cancellation and shutdown after one successful callback. It does not assert application-context lifecycle, in-flight timeout handling, process termination or deployment drain.
Common Mistakes
- Do not assume cancel(false) interrupts a running callback.
- Do not delete a leased row during shutdown.
- Do not call one clean test teardown a restart-recovery test.
Read next
Spring TaskScheduler: poll committed outbox rows in the background, Spring outbox claims: lease expiry and token-fenced acknowledgement, Spring scheduled relay: recover when the consumer commits before acknowledgement, Spring relay integration tests: prove the boundaries H2 cannot.
Continue with lease race checks
Continue with Spring outbox lease renewal: extend only the current owner.
Release boundary continuation
Continue with Spring worker shutdown: release admission before a lease expires.
