Interview frequency: …
Short answer
It happens when expensive crypto operations or concurrent file reads occupy libuv's four default threads, delaying DNS requests and other I/O tasks.
Explanation
The crypto module (such as bcrypt or pbkdf2), filesystem operations (fs), and dns.lookup share the same small threadpool. Four simultaneous logins may occupy every thread and prevent the server from resolving any DNS address until the hashing finishes. Raising the size to 16 or 64 threads can resolve this bottleneck.
Code example
import crypto from "node:crypto";
import { promisify } from "node:util";
const pbkdf2 = promisify(crypto.pbkdf2); // runs in libuv's thread pool (4 threads by default)
console.time("8 hashes");
await Promise.all(Array.from({ length: 8 }, () => pbkdf2("password", "salt", 200_000, 64, "sha512")));
console.timeEnd("8 hashes");
// Run again with a bigger pool — must be set BEFORE the pool is first used:
// UV_THREADPOOL_SIZE=8 node app.js
// fs, crypto, zlib and dns.lookup share this pool, so heavy hashing can delay unrelated file/DNS work.Common mistakes
- Expecting a larger threadpool to solve starvation of pure CPU tasks; the threadpool is for libuv tasks, not ordinary JavaScript code.