Deploys were producing about a dozen 502s each, concentrated in a two-second window, and only on requests that took longer than five seconds. The service had preStop hooks, a terminationGracePeriodSeconds , and a SIGTERM handler. All of it was there. The order was wrong.
What shutdown actually did
def shutdown ( signum , frame ): server . stop_accepting () time . sleep ( 5 ) sys . exit ( 0 )
Enter fullscreen mode
Exit fullscreen mode
That reads like "stop taking new work, let in-flight work finish, exit". The third line is the problem: sys.exit does not wait for anything. It raises SystemExit in the main thread, which unwinds the stack and ends the process with open transactions, open sockets, and any in-flight requests still being handled on worker threads.
Five seconds happened to be shorter than the slowest write path. So the requests that mattered β the ones holding an open transaction β were exactly the ones killed.
Graceful shutdown is four ordered steps
1.β¦