DeutschGitHub
prototypepackages/worker-pool/src

Worker Pool

@ralphschuler/worker-pool

Executes trusted self-contained functions on bounded persistent workers with timeout recovery.

worker_threadsparallelismbackpressure

01 · Problem

What is it for?

A thread per task wastes startup resources; an unbounded task queue risks memory exhaustion.

02 · Mental model

How should you think about it?

The main thread owns task promises and assignment. A worker executes one request and replies with an explicit success or error.

In this repository

Executes trusted self-contained functions on bounded persistent workers with timeout recovery. Function source cannot capture closures and must never be untrusted. A preinstalled handler registry and transferables are future extensions.

03 · Control flow

What happens in which order?

  1. Start workers once.
  2. Queue task identity and serializable input.
  3. Assign one task per idle worker.
  4. Settle its promise on result or error.
  5. Release or replace the worker.
  6. Reject pending work on shutdown.

04 · Building blocks

The important contracts

WorkerPool.executeMethodEnqueue a trusted self-contained function and arguments.
WorkerPoolOptionsSet queue capacity and task timeout.
destroyReject pending promises and await worker termination.

05 · Build it yourself

Implement a small core

Start with this small independent implementation, then use the steps below to make its contract explicit.

  1. Start workers once.
  2. Queue task identity and serializable input.
  3. Assign one task per idle worker.
  4. Settle its promise on result or error.
  5. Release or replace the worker.
  6. Reject pending work on shutdown.
Concept sketch · may reference omitted helpers
// worker.ts
const handlers = {
  square: (value: number) => value * value,
};
parentPort?.on("message", async ({ id, task, payload }) => {
  try {
    const result = await handlers[task as keyof typeof handlers](payload);
    parentPort?.postMessage({ id, ok: true, result });
  } catch (error) {
    parentPort?.postMessage({ id, ok: false, error: String(error) });
  }
});

06 · Verify

What should you test?

  • Resolve 0, false, empty string and undefined.
  • Reject thrown errors and crashed workers.
  • Reject queued and active work during shutdown.

Try it yourself

Turn the first verification claim into a failing test, implement the smallest fix, then add a boundary case.

Resolve 0, false, empty string and undefined.

Compare your approach

Your check should fail when the stated invariant is deliberately broken, and pass again after the fix. Keep setup deterministic and assert an observable result.

07 · Limits

Trade-offs and failure modes

  • Serialized functions cannot capture closures.
  • Never execute untrusted function source.
  • A handler registry is a stronger extension for reusable applications.
Important

Function source cannot capture closures and must never be untrusted. A preinstalled handler registry and transferables are future extensions.

08 · Keep exploring

Source code and related ideas

yarn tsx packages/worker-pool/examples/basic.ts