Concurrency
How many requests run in parallel, and how to raise it.
Requests are asynchronous: start as many as you like with Promise.all. How many actually run at the same time is decided by Node's libuv threadpool.
// Run with: UV_THREADPOOL_SIZE=64 node concurrency.mjs
await initTLS({ concurrency: 64 }); // at most 64 requests in flight
const session = new Session({ timeout: 15_000 });
const urls = Array.from(
{ length: 20 },
(_, i) => `https://httpbin.org/anything/${i}`,
);
// Start them all at once; up to UV_THREADPOOL_SIZE run in parallel,
// the rest wait in a queue.
const responses = await Promise.all(urls.map((url) => session.get(url)));
console.log(responses.map((r) => r.status)); // [ 200, 200, … ]How it works
Each request runs inside the native library on a libuv threadpool thread and holds that thread for its whole round-trip, from connecting to the last byte of the body. So:
- Parallelism =
UV_THREADPOOL_SIZE. Node's default is 4. When it's unset, this package sets it to CPU cores × 2 at import time. libuv caps it at 1024. - Requests beyond that wait in a queue and start as threads free up. Nothing is dropped.
- Native calls share the threadpool with Node's own
fs, DNS,zlibandcryptowork. Under sustained load with a small pool, those wait behind in-flight requests, so size the pool for both.
Raising the limit
Set UV_THREADPOOL_SIZE before Node starts:
UV_THREADPOOL_SIZE=64 node app.jsThe import-time default only works if nothing used the threadpool before this package was imported (an fs.promises call, a DNS lookup, …). It also doesn't apply when your entry file is an ES module on Node 20, Node 22 before 22.18 or Node 24 before 24.3; a warning says so. Set it in the real environment for a guaranteed effect. Set UV_THREADPOOL_SIZE=4 to keep Node's default.
To remove the limit altogether, use the node-tls-client binary: its requests don't hold a thread, so every request you start runs at once.
initTLS({ concurrency })
concurrency caps how many requests are admitted to the native layer at once. It defaults to the threadpool size.
- Values above
UV_THREADPOOL_SIZEonly queue and add no parallelism; a warning says so. - Each in-flight request reserves about 256 KiB of memory, so very high values cost RAM.
- The maximum is 4096.
- It's read once: calling
initTLS()again with a different value has no effect untildestroyTLS().
Lower it to limit memory or the load you put on a site; raise UV_THREADPOOL_SIZE to go faster.
Slow servers
Because a slow response keeps its thread busy until it finishes, slow endpoints use up the pool quickly. If throughput drops when a site gets slow, raise UV_THREADPOOL_SIZE and set a session timeout so stuck requests give their thread back.
Shutdown
destroyTLS() stops admitting requests, waits for the in-flight ones (each bounded by its own timeout), then frees native state.