function createReadiness() {
let ready = true;
function markUnready() {
ready = false;
}
function isReady() {
return ready;
}
function registerRoutes(app) {
app.get('/livez', (req, res) => res.status(200).send('ok'));
app.get('/readyz', (req, res) => {
if (isReady()) return res.status(200).send('ready');
return res.status(503).send('draining');
});
}
return { markUnready, isReady, registerRoutes };
}
module.exports = { createReadiness };
const connections = new Set();
function trackConnections(server) {
server.on('connection', (socket) => {
socket.idle = true;
connections.add(socket);
socket.on('close', () => connections.delete(socket));
});
server.on('request', (req, res) => {
req.socket.idle = false;
res.on('finish', () => {
req.socket.idle = true;
});
});
}
function closeIdleConnections() {
for (const socket of connections) {
if (socket.idle) socket.destroy();
}
}
function createGracefulShutdown(server, { forceTimeout = 30000 } = {}) {
let shuttingDown = false;
trackConnections(server);
server.on('request', (req, res) => {
if (shuttingDown) res.setHeader('Connection', 'close');
});
return function shutdown() {
if (shuttingDown) return Promise.resolve();
shuttingDown = true;
console.log('draining in-flight requests...');
return new Promise((resolve) => {
const forced = setTimeout(() => {
console.error('drain timed out, forcing exit');
process.exit(1);
}, forceTimeout).unref();
server.close(() => {
clearTimeout(forced);
console.log('all connections drained');
resolve();
});
closeIdleConnections();
const sweep = setInterval(closeIdleConnections, 1000).unref();
server.on('close', () => clearInterval(sweep));
});
};
}
module.exports = { createGracefulShutdown };
const express = require('express');
const http = require('http');
const { createGracefulShutdown } = require('./graceful-shutdown');
const { createReadiness } = require('./readiness');
const app = express();
const readiness = createReadiness();
readiness.registerRoutes(app);
app.get('/slow', async (req, res) => {
await new Promise((r) => setTimeout(r, 5000));
res.json({ done: true });
});
const server = http.createServer(app);
const shutdown = createGracefulShutdown(server, { forceTimeout: 30000 });
const readinessDelay = 5000;
function handleSignal(signal) {
console.log(`received ${signal}`);
readiness.markUnready();
setTimeout(() => {
shutdown().then(() => process.exit(0));
}, readinessDelay);
}
process.once('SIGTERM', () => handleSignal('SIGTERM'));
process.once('SIGINT', () => handleSignal('SIGINT'));
server.listen(3000, () => {
console.log('listening on :3000');
});
When an orchestrator like Kubernetes rolls out a new version, it sends SIGTERM to the old process and expects it to exit cleanly. A naive server that calls process.exit() immediately drops connections mid-request, producing 5xx errors for users who happened to be in flight. The pattern shown here drains those requests instead: the server stops accepting new work, lets active requests finish, and only then closes.
In graceful-shutdown.js, createGracefulShutdown wraps a Node http.Server and installs signal handlers. The core is server.close(), which stops the listener from accepting new connections and invokes its callback once all existing connections are idle. The subtlety is HTTP keep-alive: idle-but-open sockets can hold close() open indefinitely, so the module tracks every socket in a Set and, once shutdown begins, sets Connection: close on new responses and destroys sockets that go idle. A forceTimeout guards against requests that never finish by calling process.exit(1) after a deadline.
The trackConnections helper listens on the connection event to add and remove sockets from the set, marking each socket idle between requests via the request/finish hooks. During draining, closeIdleConnections walks the set and destroys any socket not currently serving a request, which is what actually lets keep-alive clients disconnect promptly.
A critical detail lives in readiness.js: the health endpoint flips to failing before draining starts. This gives the load balancer time to stop routing new traffic, avoiding a race where the server rejects requests it was still advertised as able to handle. The SIGTERM handler waits a short readinessDelay after failing the probe before calling shutdown.
Finally, server.js shows the wiring: an Express app with a slow route, a livez/readyz split, and registration of the shutdown handler with sensible timeouts. Note that once is used for signal handlers so a second SIGTERM triggers an immediate exit rather than restarting the drain. The main trade-off is latency during deploys — the process lingers for up to forceTimeout — in exchange for zero dropped requests. This approach is worth reaching for in any long-lived HTTP service behind a load balancer.
Related snips
export interface RetryOptions {
retries: number;
baseMs: number;
maxMs: number;
signal?: AbortSignal;
onRetry?: (attempt: number, delay: number, err: unknown) => void;
Exponential backoff with jitter for retries
class Product(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
cost = models.DecimalField(max_digits=10, decimal_places=2)
margin = models.DecimalField(max_digits=5, decimal_places=2, blank=True)
Django model signals vs overriding save
package dbutil
import (
"context"
"github.com/jackc/pgconn"
Retry Postgres serialization failures with bounded attempts
import { randomBytes, createHash } from "crypto";
import jwt from "jsonwebtoken";
import { RefreshTokenStore } from "./store";
const ACCESS_SECRET = process.env.ACCESS_SECRET!;
const ACCESS_TTL = "15m";
JWT access + refresh token rotation (conceptual)
package deps
import (
"net"
"net/http"
"time"
HTTP client tuned for production: timeouts, transport, and connection reuse
package api
import (
"io"
"net/http"
"os"
Safe multipart uploads using temp files (bounded memory)
Share this code
Here's the card — post it anywhere.