Interview Prep Zoneby CuriouserLabs

Question 1 of 5 · staff level

What happens when your Spring Boot pod gets SIGTERM? Design a graceful shutdown that loses zero requests.

🎤 Say this first

The sequence must be: stop accepting new work → finish in-flight work → release resources — in that order. Enable server.shutdown=graceful (+ spring.lifecycle.timeout-per-shutdown-phase); Boot then flips readiness to refusing, the web server stops taking connections but drains active requests, SmartLifecycle phases stop message listeners before pools close, and @PreDestroy runs last. Kubernetes nuance: readiness-flip propagation to endpoints/LB isn't instant — add a preStop sleep so traffic stops arriving before the server stops accepting, and keep terminationGracePeriodSeconds > your drain timeout.

The full picture

The full recipe, app + platform
# application.yml
server.shutdown: graceful
spring.lifecycle.timeout-per-shutdown-phase: 25s

# k8s deployment
spec:
  terminationGracePeriodSeconds: 40        # > preStop + drain
  containers:
    - lifecycle:
        preStop:
          exec: { command: ["sh", "-c", "sleep 8"] }  # let LB/endpoints catch up
# SIGTERM timeline:
# t0 preStop sleep (still serving, endpoints removing pod)
# t8 SIGTERM -> readiness DOWN, server stops accepting, drains in-flight
# Kafka/scheduler stop (SmartLifecycle), pools close, @PreDestroy
# t<40 clean exit — else SIGKILL (the thing all of this prevents)
  • Order dependencies via SmartLifecycle phases: consumers/listeners stop in high phases first; DataSource is a low-phase close — reversing it means in-flight work dies on 'pool is closed'.
  • In-flight beyond HTTP: @Scheduled jobs mid-run, @Async queues, Kafka batches — each needs a stop-and-drain story; executor beans get setWaitForTasksToCompleteOnShutdown(true) + a timeout.
  • The SIGKILL boundary: after grace expires nothing runs — never park durability (flush that 'must' happen) in shutdown hooks; crash-safety comes from transactions/idempotency, shutdown is just politeness at scale.
  • Test it: a load test that rolls pods mid-traffic and asserts zero 5xx is the only proof; teams discover their shutdown story during their first busy-hour deploy otherwise.

🔄 Likely follow-up questions

  • Why is the preStop sleep needed if readiness already flipped?
  • How do you drain long-lived connections (SSE, websockets)?
  • What's different for a Kafka consumer group during shutdown (rebalance storms)?