Library / SDK
tinylibs/tinypool avatar
tinylibs/tinypool

Tinypool: A Minimal Node.js Worker Pool for Controlled Parallelism

🧵 A minimal and tiny Node.js Worker Thread Pool implementation (38KB)

1,604 stars53 forksTypeScriptNOASSERTION

At a glance

What is it?
Tinypool is a TypeScript worker pool for Node.js that supports worker_threads and child_process while keeping the package small and dependency-free. It suits applications that need task isolation or background execution without Piscina's utilization and OS priority features.
Who is it for?
Adopt Tinypool when the application needs a small ESM worker pool with either worker_threads or child_process and can accept its narrower Piscina feature set. Do not choose it when utilization reporting or OS-specific thread priorities are required.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 42 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • tinylibs/tinypool1,604 ★ · +1
Star history
Repository2026-10-162026-10-24
tinylibs/tinypool1.6k1.6k

1 Stars in the last 8 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Tinypool's Problem Scope and Intended Users

Tinypool solves a specific concurrency problem in Node.js: moving work away from the main execution context and distributing it across a managed pool of workers. It is relevant to developers building CPU-bound jobs, test-related workers, or services that need each task to execute in a separate JavaScript environment. The README names Vitest as the project's main user, which explains the deliberate choice to remove features that may not be needed by that use case.

The package is a fork of Piscina, but its goal is not to reproduce every Piscina capability. Tinypool prioritizes a smaller install size and a minimal dependency surface. The README reports an install size of 38KB and states that Tinypool has no dependencies. It also says that the project uses physical cores rather than logical cores through `physical-cpu-count`. This can matter when applications want worker concurrency to reflect the machine's physical processors instead of counting hyperthreaded logical processors.

Tinypool is not a general-purpose job system with scheduling analytics or operating-system control. It does not provide a `utilization` property, and it does not support OS-specific thread priority settings. That narrower scope makes it a practical choice for code that mainly needs worker creation, task submission, isolation, communication, and cleanup.

Worker Execution and Task Flow

The basic flow is straightforward. The main module imports `Tinypool`, creates a pool with a worker `filename`, submits data through `pool.run()`, and receives the worker's return value as a promise. The worker file exports a function that receives the submitted value and returns its result. In the README example, `{ a: 4, b: 6 }` becomes `10` after the worker adds the two values.

By default, Tinypool uses the `worker_threads` runtime. The package can also use `child_process` when `runtime: 'child_process'` is supplied. These are materially different execution models. Worker threads share a process while providing isolated JavaScript execution threads, whereas child processes provide process isolation. The choice affects how the main context communicates with work, how data is serialized, and which cleanup and resource boundaries apply.

Tinypool manages the workers rather than requiring application code to create each worker directly. Its pool API includes worker recycling and task cancellation controls. `cancelPendingTasks()` cancels queued work without stopping tasks that are already running. The README also refers to a signal option for forceful cancellation of ongoing work, but the visible constructor documentation does not provide the full option example, so the exact cancellation configuration should be checked against the package API before relying on it.

Installation and First ESM Worker

The README points users to the Tinypool package on npm and identifies `tinypool` as the package name, but the visible documentation does not give an installation command. The package is ESM-only and written in TypeScript. The repository metadata for version `2.2.0` declares Node.js `^20.0.0 || >=22.0.0`, although the README describes support for Node.js 18.x and higher. These statements differ, so the `package.json` engine constraint is the more specific compatibility declaration for this release and should be checked before installation.

The first real use requires two ESM files. Create the pool in `main.mjs` and point it at the worker file. The worker receives the submitted object and returns its result.

js
import Tinypool from 'tinypool'

const pool = new Tinypool({
  filename: new URL('./worker.mjs', import.meta.url),
})
const result = await pool.run({ a: 4, b: 6 })
console.log(result)
await pool.destroy()

The worker exports the function that Tinypool invokes:

js
export default ({ a, b }) => {
  return a + b
}

Running the main file should print `10`. The `destroy()` call is part of the documented lifecycle because it terminates idle workers when the pool is no longer needed.

Communication Across Worker Runtimes

Tinypool supports direct communication between the main context and a worker, but the mechanism depends on the runtime. With `worker_threads`, the README uses a `MessageChannel` from `node:worker_threads`. The `port1` endpoint is passed as task data and included in `transferList`, while the main thread listens and sends through `port2`. This transfers ownership of the port through Node.js structured messaging rather than copying the endpoint.

js
import Tinypool from 'tinypool'
import { MessageChannel } from 'node:worker_threads'

const pool = new Tinypool({
  filename: new URL('./worker.mjs', import.meta.url),
})
const { port1, port2 } = new MessageChannel()
const promise = pool.run({ port: port1 }, { transferList: [port1] })

port2.on('message', (message) => console.log('Main thread received:', message))
setTimeout(() => port2.postMessage('Hello from main thread!'), 1000)

await promise
port1.close()
port2.close()

With `child_process`, Tinypool uses a `TinypoolChannel` supplied through the `channel` option. The channel exposes `onMessage` and `postMessage`. The child process worker listens for `process` messages and must ignore messages marked with `__tinypool_worker_message__`, because Tinypool uses those messages internally. The package also supports `json` and `advanced` serialization for `child_process`. The README does not explain the serialization trade-offs, so applications with complex payloads should inspect the Node.js behavior and their own data shapes before selecting one.

Isolation, Memory Recycling, and Teardown Controls

Tinypool offers several controls for long-running pools, but each control changes worker lifecycle behavior. `isolateWorkers` is disabled by default. The README says it always starts with a fresh worker when running tasks, which isolates the environment between tasks and reduces the chance that state persists from one job to the next. That isolation comes at the cost of worker replacement and startup overhead, although the repository does not quantify the cost.

`maxMemoryLimitBeforeRecycle` is disabled by default. When configured, Tinypool compares a worker's heap memory usage with the configured value after a task finishes. A worker that exceeds the limit is terminated and replaced. The README presents this as an alternative to enabling worker isolation when tasks leak memory. It is a targeted control, not a complete memory-management strategy. The documentation does not define sampling behavior, threshold granularity, or how quickly memory is reclaimed after termination.

The `teardown` option names an exported function that should run before a worker is terminated. This gives worker code a place for application-specific cleanup, but the visible README does not specify what happens if teardown throws or blocks. `terminateTimeout` can raise an error when worker termination exceeds its configured number of milliseconds. That option is also disabled by default. These features are useful for services with recurring jobs, but they require careful handling of external resources and asynchronous cleanup.

Piscina, Runtime Choice, and Upgrade Boundaries

Piscina is the closest documented alternative because Tinypool explicitly describes itself as a fork and says its API is similar. The practical difference is scope. Piscina provides a `utilization` property and OS-specific thread priority setting, both absent from Tinypool. The README recommends Piscina when those capabilities are needed. It also gives a historical package-size comparison, describing Piscina as approximately 800KB at the time of the comparison and Tinypool as 38KB. Package sizes can change across releases, so that figure should not be treated as a current installation measurement.

The larger decision is whether worker threads or child processes match the workload. Worker threads are the default and support `MessagePort` transfer. Child processes require a different channel abstraction and offer a separate serialization model. A library that needs detailed pool utilization should not select Tinypool merely because its package is smaller. A codebase that needs a small, dependency-free pool and already targets ESM has a better fit.

Tinypool is not archived, and the last push was on 2026-09-13. Recent releases include `v2.2.0` on 2026-09-13, `v2.1.2` on 2026-08-23, and `v2.1.1` on 2026-08-23. This indicates recent repository activity, but the visible release information does not define a long-term support policy or compatibility guarantees for every Node.js version. Upgrades should therefore be checked against the package's engine declaration and API changes.

Licensing and Operational Costs

The repository description labels the license as a custom license that GitHub cannot classify, while `package.json` declares `"license": "MIT"` for version `2.2.0`. That discrepancy needs resolution before distribution. The `LICENSE` file should be read directly, along with the package metadata used by the publishing process. This is a licensing review, not a claim about legal interpretation.

The package has no runtime dependencies, which reduces installation and dependency-resolution work. It does require Node.js applications to manage worker lifecycles. Callers must create the pool, close communication ports when applicable, and call `destroy()` when the pool is no longer needed. Long-running applications may also need to decide when to use `isolateWorkers`, `maxMemoryLimitBeforeRecycle`, `cancelPendingTasks()`, or `recycleWorkers()`. The README does not document whether `destroy()` can safely be called while a task is still active, so that boundary should be verified in the package API before shutdown logic is designed.

The operational cost is therefore not a large dependency graph. It is careful control of process and thread resources, especially for tasks that retain memory, open external connections, or communicate through channels.

Editorial conclusion

Adopt Tinypool when the application needs a small ESM worker pool with either worker_threads or child_process and can accept its narrower Piscina feature set. Do not choose it when utilization reporting or OS-specific thread priorities are required. Verify runtime compatibility, task cancellation behavior, worker recycling semantics, and the repository LICENSE before adding it to a production dependency.

Frequently asked questions

How does Tinypool differ from Piscina?

Tinypool is a smaller fork of Piscina with no dependencies and a narrower feature set. It does not provide Piscina's `utilization` property or OS-specific thread priority setting, so the README recommends Piscina when those features are required.

Does Tinypool support Node.js child processes as well as worker threads?

Yes. The default runtime is `worker_threads`, and setting `runtime: 'child_process'` selects child processes. The two runtimes use different communication mechanisms and serialization options.

What Node.js versions does Tinypool support?

The README describes support for Node.js 18.x and higher, but version `2.2.0` in `package.json` declares `^20.0.0 || >=22.0.0`. Check the package engine field for the version being adopted.

How can Tinypool recycle workers that use too much memory?

Set `maxMemoryLimitBeforeRecycle` to the desired heap limit. Tinypool checks worker memory after a task finishes and replaces a worker that exceeds the configured value. The README also describes `isolateWorkers` as a broader isolation option.

How do I stop an idle Tinypool pool?

Call `await pool.destroy()` when the pool is no longer needed. The README states that this terminates all idle workers.

Official sources

  1. Issues
  2. Project website
  3. README
  4. Releases
  5. tinylibs/tinypool on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tinylibs-tinypool.svg)](https://hysenlabs.com/projects/tinylibs-tinypool)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/tinylibs-tinypool.svg)](https://hysenlabs.com/projects/tinylibs-tinypool)

Was this useful?