Worker Pool
A fixed set of node:worker_threads workers running one script, with a queue in front. CPU-bound work handed to a worker stops blocking the event loop, so a request that hashes, parses or compresses something large does not stall every other request on the process.
The queue is bounded and the tasks are timed. An unbounded queue turns a slow worker into memory growth that ends as an out-of-memory kill, and a task with no timeout turns one wedged worker into a permanently smaller pool. Past the depth, submitting is refused rather than accepted and forgotten; past the timeout, the task is failed and its worker replaced.
The script is a filesystem path and must be absolute. A relative path resolves against whatever the process's working directory happens to be, and a data: or eval: URL would make the pool an arbitrary-code sink; both are refused.
b.workerPool.create(scriptPath, opts?) #
{
size: number, // workers; default the CPU count, in [1, 256]
maxQueueDepth: number, // queued tasks allowed; default 1024, cap 1048576
taskTimeoutMs: number, // per-task ceiling; default 300000, cap 3600000
onExit: function, // (code, worker) when a worker exits
}
Start a pool over scriptPath and answer the handle to work it. The handle carries run(payload), which resolves with what the worker posts back; drain(), which waits for the queued and running tasks to finish; terminate(), which stops the workers; and stats().
size defaults to the CPU count or 2, whichever is larger, and a value passed in must be a whole number in [1, 256] or it throws workerpool/bad-size. maxQueueDepth defaults to 1024 and caps at 1048576 (workerpool/bad-max-queue-depth), and run rejects rather than queueing past it. taskTimeoutMs defaults to five minutes and caps at one hour (workerpool/bad-task-timeout); a task that passes it is failed and its worker is replaced, so one wedged task does not shrink the pool for good. onExit is called when a worker dies, with the pool replacing it either way, and must be a function when supplied (workerpool/bad-on-exit).
A runtime that cannot load node:worker_threads throws workerpool/no-worker-threads.
scriptPath must be absolute and must be a file. A relative path would resolve against the process's working directory, and a data: or eval: URL would make the pool run whatever a caller supplied; both throw workerpool/bad-script-path.
The pool spawns its workers while create runs, so a worker the runtime refuses to start throws workerpool/spawn-failed from this call rather than from the first task.
var b = require("@blamejs/core");
var path = require("node:path");
var pool = b.workerPool.create(path.join(__dirname, "hash-worker.js"), {
size: 4, taskTimeoutMs: b.constants.TIME.seconds(30),
});
var digest = await pool.run({ bytes: buf });
await pool.drain();
await pool.terminate();