A scheduled worker must stop taking new records and account for in-flight claims when the application terminates.
Spring worker shutdown: release admission before a lease expires
Separate new work from owned work
The local poller uses a bounded batch and a lease token. On termination, cancel the next poll first. A record already claimed still needs one of two outcomes: finish delivery and acknowledge with its current token, or leave the claim to expire so another worker can retry. A stale worker must never acknowledge after another owner has renewed or replaced the lease.
Token-fenced acknowledgement and lease renewal are checked against H2. Those tests use direct calls and a logical clock. They do not demonstrate how the deployed scheduler receives a stop signal or how a broker publication is confirmed.
Do not promise exactly once
If the consumer commits and the worker dies before acknowledgement, the event returns after lease expiry. The lost-ack fixture checks this local replay; the consumer's event-ID reservation must make the second delivery safe. A shutdown hook cannot eliminate this interval.
Choose an in-flight deadline shorter than the lease or renew while work continues. Record backlog age, claims still active at stop and the number of expired leases after restart. The current source kit cancels a test scheduler cleanly, but no scheduler runs inside ReceiptApplication.
Working sketch
stop accepting new polls
finish or abandon each claimed event before the stop deadline
acknowledge only with the current lease tokenVerification boundary
ScheduledOutboxPollerTest, ConcurrentOutboxLeaseTest and OutboxRelayFlowTest check local states; no deployed stop signal is tested.
Costs and limits
The source kit uses H2 and a fake transport. Broker confirmation, process death and restart recovery are still untested.
Common Mistakes
- Do not start a new batch after the stop decision.
- Do not acknowledge with an expired lease token.
- Do not claim graceful shutdown removes duplicate-delivery risk.
Read next
Spring TaskScheduler shutdown: stop new polls and account for in-flight work, Outbox acknowledgements: require the current lease token, Spring outbox lease renewal: extend only the current owner, Spring scheduled relay: recover when the consumer commits before acknowledgement.
