fix(backend): force-exit failsafe so shutdown never hangs

shutdown() awaited httpServer.close(), which waits for ALL open
connections. A lingering WS/keep-alive socket left the process zombie
forever after an uncaughtException (e.g. pg 'Connection terminated
unexpectedly' to imrnes) — no exit, so systemd Restart=always could
never revive it; /api/guilds returned 502 until manual restart.

Add 10s force-exit timer in shutdown(); clear it on clean completion.
This commit is contained in:
asepharyana
2026-08-05 23:58:30 +07:00
parent 9a2fa999bf
commit f251e69f51
+11
View File
@@ -24,6 +24,15 @@ async function main() {
async function shutdown(signal: string) { async function shutdown(signal: string) {
logger.info({ signal }, "Shutting down gracefully"); logger.info({ signal }, "Shutting down gracefully");
// Failsafe: graceful shutdown must never hang the process forever.
// httpServer.close() waits for ALL open connections (including lingering
// WebSocket/keep-alive sockets), so on a stuck connection the process would
// otherwise sit zombie and systemd (Restart=always) can never revive it.
const forceExitTimer = setTimeout(() => {
logger.error({ signal }, "Graceful shutdown timed out; forcing exit");
process.exit(1);
}, 10_000);
try { try {
// 1. Stop accepting new HTTP connections // 1. Stop accepting new HTTP connections
if (httpServer) { if (httpServer) {
@@ -54,9 +63,11 @@ async function shutdown(signal: string) {
); );
logger.info("Graceful shutdown completed"); logger.info("Graceful shutdown completed");
clearTimeout(forceExitTimer);
process.exit(0); process.exit(0);
} catch (err) { } catch (err) {
logger.error({ err }, "Error during graceful shutdown"); logger.error({ err }, "Error during graceful shutdown");
clearTimeout(forceExitTimer);
process.exit(1); process.exit(1);
} }
} }