How does libuv's thread pool work, and what uses it?
Quick Answer
libuv keeps a small pool of background threads (default 4, set via UV_THREADPOOL_SIZE) for operations that have no async OS primitive — file system I/O, DNS lookups (getaddrinfo), and some crypto/zlib functions. Network I/O does NOT use the pool; it uses the OS's async facilities directly.
Detailed Answer
Answer: Not every asynchronous operation in Node is handled the same way. libuv splits work into two categories.
1. OS-level async (no threads needed): Network sockets use the operating system's event notification (epoll on Linux, kqueue on macOS, IOCP on Windows). These scale to many thousands of connections without extra threads.
2. The thread pool (for work with no async OS API): Some operations can only be done synchronously at the OS level, so libuv runs them on a pool of background threads and calls back when done:
- File system operations (
fs.readFile, etc.) - DNS resolution via
dns.lookup/getaddrinfo - Some
cryptofunctions (pbkdf2,randomBytesasync forms) zlibcompression
Pool size:
- Default is 4 threads. Configurable via the
UV_THREADPOOL_SIZEenvironment variable (up to 1024), set before the process starts.
UV_THREADPOOL_SIZE=8 node app.js
Why it matters in interviews:
- If you fire many concurrent
fs/crypto/dns.lookupcalls, they contend for those 4 threads — extra ones queue. This can cause surprising latency under load. dns.lookupuses the pool, butdns.resolve(which queries DNS servers over the network) does not — a classic gotcha.- Raising
UV_THREADPOOL_SIZEcan help throughput for pool-bound workloads, but it doesn't help pure network I/O.