The whole server freezes during one heavy request
“One customer uploads a big export or hits a report endpoint, and every other request, including health checks, stalls for seconds. CPU sits at 100% on one core while the database is idle.”
THE PRINCIPLE
Work per callback must be small and bounded by input size you control. CPU-heavy or unbounded work belongs in a worker thread, another service, or a streaming design.
FIRST MOVES
- Export event loop delay (perf_hooks.monitorEventLoopDelay, p99 and max) and event loop utilization; a spike that matches the freeze confirms it.
- Capture a CPU profile under the slow request (node --cpu-prof, or DevTools via --inspect) and read the widest synchronous frame.
- Check the usual suspects: JSON.parse/stringify of large bodies, *Sync fs/zlib/crypto/child_process calls, large sorts or regexes on user input (ReDoS).
- Cap request body sizes at the parser and stream large imports and exports instead of materializing them.
- Move CPU-bound work to worker_threads (a fixed pool, not one per request) or partition it with setImmediate between chunks.
TOOLS: THEN → NOW
Sync APIs (readFileSync, pbkdf2Sync, zlib *Sync) in request pathsAsync variants, and a worker_threads pool for CPU-bound JavaScript
Inferring blocking from CPU graphsmonitorEventLoopDelay and eventLoopUtilization metrics plus a CPU profile
Parsing whole payloads in memoryBody size limits and streaming parsers for large inputs
PATTERN SNAPSHOT
import { monitorEventLoopDelay } from "node:perf_hooks";
const delay = monitorEventLoopDelay({ resolution: 20 });
delay.enable();
setInterval(() => {
// Histogram values are nanoseconds.
metrics.gauge("nodejs_eventloop_delay_p99_ms", delay.percentile(99) / 1e6);
metrics.gauge("nodejs_eventloop_delay_max_ms", delay.max / 1e6);
delay.reset();
}, 10_000).unref();CLOSE THE AI. EXPLAIN THIS.
Why does one slow JSON.parse delay unrelated requests in Node but not in a thread-per-request server, and what metric proves it?HOW IT WORKS UNDERNEATH
RELATED SYMPTOMS
SOURCES
Guide reviewed