In this lesson, you will learn how to verify that your Node.js application cleanly flushes pending transactions and terminates active connections when receiving a termination signal.
When a process receives a SIGTERM or SIGINT, simply killing the process can lead to data corruption or orphaned database transactions. A robust "graceful shutdown" ensures the server stops accepting new requests, waits for in-flight requests to finish, and closes open handles (like DB pools) before exiting.
The Verification Strategy
To test this, we cannot rely on standard unit tests; we need an integration approach that simulates the signal. The goal is to prove that a transaction initiated just before the shutdown signal is fully persisted rather than dropped.
it('should finish pending transactions before closing', async () => {
const server = startServer();
// 1. Initiate a slow request that involves a DB transaction
const pendingRequest = agent.post('/api/save-order').send({ id: 123 });
// 2. Trigger the shutdown signal
process.kill(process.pid, 'SIGTERM');
// 3. Await the pending request and check if DB state is final
await pendingRequest;
const order = await db.orders.findById(123);
expect(order.status).toBe('completed');
// 4. Verify server is no longer listening
await expect(server.listening).toBe(false);
});
Key Considerations
- The "Drain" Period: Your test must verify that the server does not immediately exit. If the test finishes before the transaction, you have a race condition. Use
awaiton the request lifecycle. - Connection Pools: If your database driver keeps a pool, your test must verify that
pool.end()was called. You can mock the database adapter to spy on itsend()orclose()method to confirm the graceful shutdown logic invoked the teardown. - Timeout Logic: Always ensure your test has a global timeout. If your shutdown logic hangs (e.g., waiting for a request that never ends), your test suite should fail rather than block indefinitely.
Testing this behavior ensures that your infrastructure doesn't treat an orchestrated deployment (like a Kubernetes rolling update) as a catastrophic crash.
Key Takeaway: Testing graceful shutdown requires verifying that the event loop remains active long enough to commit stateful operations before the process exits.