In this lesson, you will learn how to gracefully orchestrate the shutdown of database pools and network sockets to prevent resource leaks when your Node.js process terminates.
When a Node.js application receives a SIGTERM or SIGINT signal, it doesn't immediately stop. Instead, it enters a shutdown phase where it is your responsibility to close active connections. If you kill the process abruptly, database pools may leave "zombie" connections open, and network sockets might hang, causing potential data corruption or port binding conflicts upon immediate restart.
The Orchestration Pattern
Resource cleanup must be sequential and asynchronous. You cannot simply exit; you must wait for the database drivers to drain their pools and for pending HTTP requests to complete. Orchestration means creating a clean "shutdown sequence" that ensures your most critical resources close last, while high-level entry points (like your HTTP server) close first to stop accepting new traffic.
Here is how you structure that orchestration:
async function gracefulShutdown(signal) {
console.log(`Received ${signal}. Starting cleanup...`);
// 1. Stop accepting new requests
server.close(async () => {
console.log('HTTP server closed.');
// 2. Close database pools
try {
await dbPool.end();
console.log('Database pool closed.');
process.exit(0);
} catch (err) {
console.error('Error during pool closure:', err);
process.exit(1);
}
});
}
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
By wrapping your cleanup logic in an async function and hooking into system signals, you guarantee that your application doesn't vanish while a database write is in progress.
Note that server.close() stops the server from listening for new connections, but allows existing sockets to finish their tasks. Once the server callback fires, you move to the database layer. This ensures that you aren't trying to query a database that you have already disconnected, maintaining a clean state during the transition.
Key Takeaway: Always orchestrate your shutdown by disabling new entry points first, then sequentially draining your persistent connections before finally exiting the process.