piscina
repository·current·Indexed 26 days ago
https://github.com/piscinajs/piscinaA fast, efficient Node.js Worker Thread Pool implementation designed to handle fixed and variable task scenarios. It features flexible pool sizing, efficient thread communication, and support for ECMAScript Modules (ESM). Key capabilities include task cancellation via AbortController, backpressure management with maxQueue, custom task queues like FixedQueue, and detailed performance monitoring metrics such as utilization and execution time histograms.
What's inside piscina
- Piscina.js is a Node.js worker pool library designed to run CPU-intensive tasks in parallel using worker threads. It provides an API to offload computationally expensive tasks to a managed pool of workers, improving the performance and scalability of Node.js applications by preventing the overhead and performance issues associated with manually managing thousands of concurrent worker threads.
Key features of Piscina.js
currentPiscina provides several advanced capabilities for managing worker threads:
- Performance: Fast communication between threads and support for flexible pool sizes.
- Task Management: Covers both fixed-task and variable-task scenarios, including custom task queues and cancellation support.
- Observability: Proper async tracking integration and tracking statistics for run and wait times.
- Resource Control: Supports enforcing memory resource limits and optional CPU scheduling priorities on Linux.
- Compatibility: Supports CommonJS, ESM, and TypeScript.
Understand Piscina's default load balancing algorithm
currentBy default, Piscina uses a Dynamic load balancing algorithm based on a Resource Based (least-busy) approach. This algorithm distributes tasks to the worker that is currently the least busy, aiming to adapt to the environment and minimize the time required to finish individual workloads by making better use of available resources.Install Piscina via npm, yarn, pnpm, or Bun
currentTo install the latest version of Piscina.js and its dependencies, use your preferred package manager. Note that you must have Node.js version 22 or higher installed on your system.
:::note While Bun can be used to install Piscina, its behavior while running within Bun is not assured. :::
npm install piscina # or yarn add piscina # or pnpm add piscina # or bun add piscinaSet worker thread priority on Linux
currentOn Linux systems, you can set the CPU scheduling priority of workers using the
niceIncrementoption. This requires the@napi-rs/nicenative addon.- Install the dependency:
npm i @napi-rs/nice - Pass
niceIncrementto thePiscinaconstructor.
A higher
niceIncrementresults in lower CPU priority for workers, which helps prevent them from starving the main Node.js event loop thread.const Piscina = require("piscina"); const pool = new Piscina({ worker: "/absolute/path/to/worker.js", niceIncrement: 20, });- Install the dependency:
Delay worker availability during initialization
currentBy default, Piscina makes a worker available as soon as it loads the exported handler. If your worker requires asynchronous initialization (e.g., connecting to a database), you can export a
Promisethat resolves to the handler function. Piscina will await this Promise before marking the worker as ready to process tasks.async function initialize() { await someAsyncInitializationActivity(); return ({ a, b }) => a + b; } module.exports = initialize();Support ECMAScript Modules (ESM)
currentTo use ESM with Piscina, ensure the
filenameprovided to thePiscinaconstructor is a validfile://URL. In your worker module, useexport defaultor named exports.import { Piscina } from "piscina"; const piscina = new Piscina({ // The URL must be a file:// URL filename: new URL("./worker.mjs", import.meta.url).href, }); const result = await piscina.run({ a: 4, b: 6 });Avoid worker thread thrashing
currentIf the rate of incoming tasks is inconsistent, Piscina may experience 'thrashing'—the excessive creation and termination of workers. To prevent this, use one or a combination of the following strategies:
- Maintain Queue Pressure: Ensure the rate of new tasks is sufficient to keep workers busy.
- Increase
idleTimeout: Increase the time a worker stays alive while idle before being terminated. This prevents immediate shutdown when a brief lull in tasks occurs. - Increase
minThreads: Increase the minimum number of threads that Piscina always maintains. This ensures a baseline of workers is always available, even during low pressure.
Use Inline Worker Code with TypeScript
currentYou can combine your main application and worker code into a single file by using the
isMainThreadflag fromworker_threads. IfisMainThreadis true, you initializePiscinausing__filename. Otherwise, you export the worker function as the default export.import Piscina from 'piscina'; import { isMainThread } from 'worker_threads'; interface Inputs { a: number; b: number; } if (isMainThread) { const piscina = new Piscina({ filename: __filename }); (async () => { const task: Inputs = { a: 1, b: 2 }; console.log(await piscina.run(task)); })(); } else { export default ({ a, b }: Inputs): number => { return a + b; }; }Manage task overload with backpressure using `maxQueue` and `drain`
currentWhen using the
maxQueueoption, you can prevent memory exhaustion by implementing backpressure. Whenpool.queueSizereachespool.options.maxQueue, you should pause your data source (e.g., a Node.js stream). Listen for the'drain'event on the Piscina pool instance to know when the queue has been cleared and it is safe to resume the data source.const Pool = require("piscina"); const { resolve } = require("path"); const pool = new Pool({ filename: resolve(__dirname, "worker.js"), maxQueue: "auto", }); const stream = getStreamSomehow(); // Resume stream when queue is empty pool.on("drain", () => { if (stream.isPaused()) { stream.resume(); } }); stream .on("data", (data) => { pool.run(data); // Pause stream if queue is full if (pool.queueSize === pool.options.maxQueue) { stream.pause(); } }) .on("error", console.error) .on("end", () => { console.log("done"); });Set up Piscina in JavaScript
currentTo use Piscina in a JavaScript project, instantiate
Piscinaby providing the absolute path to your worker file via thefilenameoption. You can then execute tasks using the.run()method, which returns a Promise that resolves to the worker's result.const path = require("path"); const Piscina = require("piscina"); // Create a new Piscina instance pointing to your worker file const piscina = new Piscina({ filename: path.resolve(__dirname, "worker.js"), }); // Run a task using Piscina (async () => { const result = await piscina.run({ a: 4, b: 6 }); console.log(result); // prints 10 })();Build the documentation website for production
currentTo generate static content for the documentation website, run
npm run build. The output will be placed in thebuilddirectory and can be hosted on any static content hosting service.$ npm run build