A production incident: a Spring Boot service's shutdown hook hangs during a deploy, blocked forever on a socket read to a downstream service that never responds. `TimeoutStopSec=15` is configured. Walk through exactly what happens, mechanically, from `systemctl stop` to the process actually being gone — and explain why the JVM's shutdown-hook mechanism doesn't run your cleanup code directly inside the OS signal handler in the first place.
systemctl stop sends SIGTERM to the process (or, more precisely, to the cgroup systemd tracks for the whole service). The JVM's own native signal handling catches that and, rather than running your cleanup code right there inside the handler, starts your registered shutdown-hook threads through the normal JVM thread machinery — which matters because a signal handler is only allowed to safely call a small, restricted set of functions (async-signal-safe ones), and arbitrary Java cleanup code (closing a database connection, writing a log line, reading from a socket) is absolutely not guaranteed to be safe to run in that context. Once your shutdown hook is running as an ordinary Java thread, it's ordinary code — which means if it blocks forever on a socket read, nothing about being "in a shutdown hook" makes the OS forcibly interrupt it. It just hangs, for as long as it's allowed to. TimeoutStopSec=15 is the actual backstop: systemd waits 15 seconds after sending SIGTERM, and if the service (as systemd understands it, via the whole cgroup it's tracking) hasn't fully stopped by then, systemd escalates to SIGKILL for everything still alive in that cgroup — which cannot be caught, blocked, or ignored, and ends the process immediately, mid-hang, with the shutdown hook never completing whatever it was doing.