source: trip-planner-front/node_modules/piscina/README.md@ 8d391a1

Last change on this file since 8d391a1 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 29.5 KB
Line 
1![Piscina Logo](https://avatars1.githubusercontent.com/u/65627548?s=200&v=4)
2
3# piscina - the node.js worker pool
4
5![CI](https://github.com/jasnell/piscina/workflows/CI/badge.svg)
6
7* ✔ Fast communication between threads
8* ✔ Covers both fixed-task and variable-task scenarios
9* ✔ Supports flexible pool sizes
10* ✔ Proper async tracking integration
11* ✔ Tracking statistics for run and wait times
12* ✔ Cancellation Support
13* ✔ Supports enforcing memory resource limits
14* ✔ Supports CommonJS, ESM, and TypeScript
15* ✔ Custom task queues
16* ✔ Optional CPU scheduling priorities on Linux
17
18Written in TypeScript.
19
20For Node.js 12.x and higher.
21
22[MIT Licensed][].
23
24## Piscina API
25
26### Example
27
28In `main.js`:
29
30```js
31const path = require('path');
32const Piscina = require('piscina');
33
34const piscina = new Piscina({
35 filename: path.resolve(__dirname, 'worker.js')
36});
37
38(async function() {
39 const result = await piscina.run({ a: 4, b: 6 });
40 console.log(result); // Prints 10
41})();
42```
43
44In `worker.js`:
45
46```js
47module.exports = ({ a, b }) => {
48 return a + b;
49};
50```
51
52The worker may also be an async function or may return a Promise:
53
54```js
55const { promisify } = require('util');
56
57// Awaitable timers are available in Node.js 15.x+
58// For Node.js 12 and 14, use promisify(setTimeout)
59const { setTimeout } = require('timers/promises');
60
61module.exports = async ({ a, b }) => {
62 // Fake some async activity
63 await setTimeout(100);
64 return a + b;
65};
66```
67
68ESM is also supported for both Piscina and workers:
69
70```js
71import { Piscina } from 'piscina';
72
73const piscina = new Piscina({
74 // The URL must be a file:// URL
75 filename: new URL('./worker.mjs', import.meta.url).href
76});
77
78const result = await piscina.run({ a: 4, b: 6 });
79console.log(result); // Prints 10
80```
81
82In `worker.mjs`:
83
84```js
85export default ({ a, b }) => {
86 return a + b;
87};
88```
89
90### Exporting multiple worker functions
91
92A single worker file may export multiple named handler functions.
93
94```js
95'use strict';
96
97function add({ a, b }) { return a + b; }
98
99function multiply({ a, b }) { return a * b; }
100
101add.add = add;
102add.multiply = multiply;
103
104module.exports = add;
105```
106
107The export to target can then be specified when the task is submitted:
108
109```js
110'use strict';
111
112const Piscina = require('piscina');
113const { resolve } = require('path');
114
115const piscina = new Piscina({
116 filename: resolve(__dirname, 'worker.js')
117});
118
119(async function() {
120 const res = await Promise.all([
121 piscina.run({ a: 4, b: 6 }, { name: 'add' }),
122 piscina.run({ a: 4, b: 6 }, { name: 'multiply' })
123 ]);
124})();
125```
126
127### Cancelable Tasks
128
129Submitted tasks may be canceled using either an `AbortController` or
130an `EventEmitter`:
131
132```js
133'use strict';
134
135const Piscina = require('piscina');
136const { AbortController } = require('abort-controller');
137const { resolve } = require('path');
138
139const piscina = new Piscina({
140 filename: resolve(__dirname, 'worker.js')
141});
142
143(async function() {
144 const abortController = new AbortController();
145 try {
146 const { signal } = abortController;
147 const task = piscina.run({ a: 4, b: 6 }, { signal });
148 abortController.abort();
149 await task;
150 } catch (err) {
151 console.log('The task was canceled');
152 }
153})();
154```
155
156To use `AbortController`, you will need to `npm i abort-controller`
157(or `yarn add abort-controller`).
158
159(In Node.js 15.0.0 or higher, there is a new built-in `AbortController`
160implementation that can be used here as well.)
161
162Alternatively, any `EventEmitter` that emits an `'abort'` event
163may be used as an abort controller:
164
165```js
166'use strict';
167
168const Piscina = require('piscina');
169const EventEmitter = require('events');
170const { resolve } = require('path');
171
172const piscina = new Piscina({
173 filename: resolve(__dirname, 'worker.js')
174});
175
176(async function() {
177 const ee = new EventEmitter();
178 try {
179 const task = piscina.run({ a: 4, b: 6 }, { signal: ee });
180 ee.emit('abort');
181 await task;
182 } catch (err) {
183 console.log('The task was canceled');
184 }
185})();
186```
187
188### Delaying Availability of Workers
189
190A worker thread will not be made available to process tasks until Piscina
191determines that it is "ready". By default, a worker is ready as soon as
192Piscina loads it and acquires a reference to the exported handler function.
193
194There may be times when the availability of a worker may need to be delayed
195longer while the worker initializes any resources it may need to operate.
196To support this case, the worker module may export a `Promise` that resolves
197the handler function as opposed to exporting the function directly:
198
199```js
200async function initialize() {
201 await someAsyncInitializationActivity();
202 return ({ a, b }) => a + b;
203}
204
205module.exports = initialize();
206```
207
208Piscina will await the resolution of the exported Promise before marking
209the worker thread available.
210
211### Backpressure
212
213When the `maxQueue` option is set, once the `Piscina` queue is full, no
214additional tasks may be submitted until the queue size falls below the
215limit. The `'drain'` event may be used to receive notification when the
216queue is empty and all tasks have been submitted to workers for processing.
217
218Example: Using a Node.js stream to feed a Piscina worker pool:
219
220```js
221'use strict';
222
223const { resolve } = require('path');
224const Pool = require('../..');
225
226const pool = new Pool({
227 filename: resolve(__dirname, 'worker.js'),
228 maxQueue: 'auto'
229});
230
231const stream = getStreamSomehow();
232stream.setEncoding('utf8');
233
234pool.on('drain', () => {
235 if (stream.isPaused()) {
236 console.log('resuming...', counter, pool.queueSize);
237 stream.resume();
238 }
239});
240
241stream
242 .on('data', (data) => {
243 pool.run(data);
244 if (pool.queueSize === pool.options.maxQueue) {
245 console.log('pausing...', counter, pool.queueSize);
246 stream.pause();
247 }
248 })
249 .on('error', console.error)
250 .on('end', () => {
251 console.log('done');
252 });
253```
254
255### Additional Examples
256
257Additional examples can be found in the GitHub repo at
258https://github.com/jasnell/piscina/tree/master/examples
259
260## Class: `Piscina`
261
262Piscina works by creating a pool of Node.js Worker Threads to which
263one or more tasks may be dispatched. Each worker thread executes a
264single exported function defined in a separate file. Whenever a
265task is dispatched to a worker, the worker invokes the exported
266function and reports the return value back to Piscina when the
267function completes.
268
269This class extends [`EventEmitter`][] from Node.js.
270
271### Constructor: `new Piscina([options])`
272
273* The following optional configuration is supported:
274 * `filename`: (`string | null`) Provides the default source for the code that
275 runs the tasks on Worker threads. This should be an absolute path or an
276 absolute `file://` URL to a file that exports a JavaScript `function` or
277 `async function` as its default export or `module.exports`. [ES modules][]
278 are supported.
279 * `name`: (`string | null`) Provides the name of the default exported worker
280 function. The default is `'default'`, indicating the default export of the
281 worker module.
282 * `minThreads`: (`number`) Sets the minimum number of threads that are always
283 running for this thread pool. The default is based on the number of
284 available CPUs.
285 * `maxThreads`: (`number`) Sets the maximum number of threads that are
286 running for this thread pool. The default is based on the number of
287 available CPUs.
288 * `idleTimeout`: (`number`) A timeout in milliseconds that specifies how long
289 a `Worker` is allowed to be idle, i.e. not handling any tasks, before it is
290 shut down. By default, this is immediate. **Tip**: *The default `idleTimeout`
291 can lead to some performance loss in the application because of the overhead
292 involved with stopping and starting new worker threads. To improve performance,
293 try setting the `idleTimeout` explicitly.*
294 * `maxQueue`: (`number` | `string`) The maximum number of tasks that may be
295 scheduled to run, but not yet running due to lack of available threads, at
296 a given time. By default, there is no limit. The special value `'auto'`
297 may be used to have Piscina calculate the maximum as the square of `maxThreads`.
298 When `'auto'` is used, the calculated `maxQueue` value may be found by checking
299 the [`options.maxQueue`](#property-options-readonly) property.
300 * `concurrentTasksPerWorker`: (`number`) Specifies how many tasks can share
301 a single Worker thread simultaneously. The default is `1`. This generally
302 only makes sense to specify if there is some kind of asynchronous component
303 to the task. Keep in mind that Worker threads are generally not built for
304 handling I/O in parallel.
305 * `useAtomics`: (`boolean`) Use the [`Atomics`][] API for faster communication
306 between threads. This is on by default.
307 * `resourceLimits`: (`object`) See [Node.js new Worker options][]
308 * `maxOldGenerationSizeMb`: (`number`) The maximum size of each worker threads
309 main heap in MB.
310 * `maxYoungGenerationSizeMb`: (`number`) The maximum size of a heap space for
311 recently created objects.
312 * `codeRangeSizeMb`: (`number`) The size of a pre-allocated memory range used
313 for generated code.
314 * `stackSizeMb` : (`number`) The default maximum stack size for the thread.
315 Small values may lead to unusable Worker instances. Default: 4
316 * `env`: (`object`) If set, specifies the initial value of `process.env` inside
317 the worker threads. See [Node.js new Worker options][] for details.
318 * `argv`: (`any[]`) List of arguments that will be stringified and appended to
319 `process.argv` in the worker. See [Node.js new Worker options][] for details.
320 * `execArgv`: (`string[]`) List of Node.js CLI options passed to the worker.
321 See [Node.js new Worker options][] for details.
322 * `workerData`: (`any`) Any JavaScript value that can be cloned and made
323 available as `require('piscina').workerData`. See [Node.js new Worker options][]
324 for details. Unlike regular Node.js Worker Threads, `workerData` must not
325 specify any value requiring a `transferList`. This is because the `workerData`
326 will be cloned for each pooled worker.
327 * `taskQueue`: (`TaskQueue`) By default, Piscina uses a first-in-first-out
328 queue for submitted tasks. The `taskQueue` option can be used to provide an
329 alternative implementation. See [Custom Task Queues][] for additional detail.
330 * `niceIncrement`: (`number`) An optional value that decreases priority for
331 the individual threads, i.e. the higher the value, the lower the priority
332 of the Worker threads. This value is only used on Linux and requires the
333 optional [`nice-napi`][] module to be installed.
334 See [`nice(2)`][] for more details.
335 * `trackUnmanagedFds`: (`boolean`) An optional setting that, when `true`, will
336 cause Workers to track file descriptors managed using `fs.open()` and
337 `fs.close()`, and will close them automatically when the Worker exits.
338 Defaults to `true`. (This option is only supported on Node.js 12.19+ and
339 all Node.js versions higher than 14.6.0).
340
341Use caution when setting resource limits. Setting limits that are too low may
342result in the `Piscina` worker threads being unusable.
343
344### Method: `run(task[, options])`
345
346Schedules a task to be run on a Worker thread.
347
348* `task`: Any value. This will be passed to the function that is exported from
349 `filename`.
350* `options`:
351 * `transferList`: An optional lists of objects that is passed to
352 [`postMessage()`] when posting `task` to the Worker, which are transferred
353 rather than cloned.
354 * `filename`: Optionally overrides the `filename` option passed to the
355 constructor for this task. If no `filename` was specified to the constructor,
356 this is mandatory.
357 * `name`: Optionally overrides the exported worker function used for the task.
358 * `abortSignal`: An [`AbortSignal`][] instance. If passed, this can be used to
359 cancel a task. If the task is already running, the corresponding `Worker`
360 thread will be stopped.
361 (More generally, any `EventEmitter` or `EventTarget` that emits `'abort'`
362 events can be passed here.) Abortable tasks cannot share threads regardless
363 of the `concurrentTasksPerWorker` options.
364
365This returns a `Promise` for the return value of the (async) function call
366made to the function exported from `filename`. If the (async) function throws
367an error, the returned `Promise` will be rejected with that error.
368If the task is aborted, the returned `Promise` is rejected with an error
369as well.
370
371### Method: `runTask(task[, transferList][, filename][, abortSignal])`
372
373**Deprecated** -- Use `run(task, options)` instead.
374
375Schedules a task to be run on a Worker thread.
376
377* `task`: Any value. This will be passed to the function that is exported from
378 `filename`.
379* `transferList`: An optional lists of objects that is passed to
380 [`postMessage()`] when posting `task` to the Worker, which are transferred
381 rather than cloned.
382* `filename`: Optionally overrides the `filename` option passed to the
383 constructor for this task. If no `filename` was specified to the constructor,
384 this is mandatory.
385* `abortSignal`: An [`AbortSignal`][] instance. If passed, this can be used to
386 cancel a task. If the task is already running, the corresponding `Worker`
387 thread will be stopped.
388 (More generally, any `EventEmitter` or `EventTarget` that emits `'abort'`
389 events can be passed here.) Abortable tasks cannot share threads regardless
390 of the `concurrentTasksPerWorker` options.
391
392This returns a `Promise` for the return value of the (async) function call
393made to the function exported from `filename`. If the (async) function throws
394an error, the returned `Promise` will be rejected with that error.
395If the task is aborted, the returned `Promise` is rejected with an error
396as well.
397
398### Method: `destroy()`
399
400Stops all Workers and rejects all `Promise`s for pending tasks.
401
402This returns a `Promise` that is fulfilled once all threads have stopped.
403
404### Event: `'error'`
405
406An `'error'` event is emitted by instances of this class when:
407
408- Uncaught exceptions occur inside Worker threads that do not currently handle
409 tasks.
410- Unexpected messages are sent from from Worker threads.
411
412All other errors are reported by rejecting the `Promise` returned from
413`run()` or `runTask()`, including rejections reported by the handler function
414itself.
415
416### Event: `'drain'`
417
418A `'drain'` event is emitted whenever the `queueSize` reaches `0`.
419
420### Property: `completed` (readonly)
421
422The current number of completed tasks.
423
424### Property: `duration` (readonly)
425
426The length of time (in milliseconds) since this `Piscina` instance was
427created.
428
429### Property: `options` (readonly)
430
431A copy of the options that are currently being used by this instance. This
432object has the same properties as the options object passed to the constructor.
433
434### Property: `runTime` (readonly)
435
436A histogram summary object summarizing the collected run times of completed
437tasks. All values are expressed in milliseconds.
438
439* `runTime.average` {`number`} The average run time of all tasks
440* `runTime.mean` {`number`} The mean run time of all tasks
441* `runTime.stddev` {`number`} The standard deviation of collected run times
442* `runTime.min` {`number`} The fastest recorded run time
443* `runTime.max` {`number`} The slowest recorded run time
444
445All properties following the pattern `p{N}` where N is a number (e.g. `p1`, `p99`)
446represent the percentile distributions of run time observations. For example,
447`p99` is the 99th percentile indicating that 99% of the observed run times were
448faster or equal to the given value.
449
450```js
451{
452 average: 1880.25,
453 mean: 1880.25,
454 stddev: 1.93,
455 min: 1877,
456 max: 1882.0190887451172,
457 p0_001: 1877,
458 p0_01: 1877,
459 p0_1: 1877,
460 p1: 1877,
461 p2_5: 1877,
462 p10: 1877,
463 p25: 1877,
464 p50: 1881,
465 p75: 1881,
466 p90: 1882,
467 p97_5: 1882,
468 p99: 1882,
469 p99_9: 1882,
470 p99_99: 1882,
471 p99_999: 1882
472}
473```
474
475### Property: `threads` (readonly)
476
477An Array of the `Worker` instances used by this pool.
478
479### Property: `queueSize` (readonly)
480
481The current number of tasks waiting to be assigned to a Worker thread.
482
483### Property: `utilization` (readonly)
484
485A point-in-time ratio comparing the approximate total mean run time
486of completed tasks to the total runtime capacity of the pool.
487
488A pools runtime capacity is determined by multiplying the `duration`
489by the `options.maxThread` count. This provides an absolute theoretical
490maximum aggregate compute time that the pool would be capable of.
491
492The approximate total mean run time is determined by multiplying the
493mean run time of all completed tasks by the total number of completed
494tasks. This number represents the approximate amount of time the
495pool as been actively processing tasks.
496
497The utilization is then calculated by dividing the approximate total
498mean run time by the capacity, yielding a fraction between `0` and `1`.
499
500### Property: `waitTime` (readonly)
501
502A histogram summary object summarizing the collected times tasks spent
503waiting in the queue. All values are expressed in milliseconds.
504
505* `waitTime.average` {`number`} The average wait time of all tasks
506* `waitTime.mean` {`number`} The mean wait time of all tasks
507* `waitTime.stddev` {`number`} The standard deviation of collected wait times
508* `waitTime.min` {`number`} The fastest recorded wait time
509* `waitTime.max` {`number`} The longest recorded wait time
510
511All properties following the pattern `p{N}` where N is a number (e.g. `p1`, `p99`)
512represent the percentile distributions of wait time observations. For example,
513`p99` is the 99th percentile indicating that 99% of the observed wait times were
514faster or equal to the given value.
515
516```js
517{
518 average: 1880.25,
519 mean: 1880.25,
520 stddev: 1.93,
521 min: 1877,
522 max: 1882.0190887451172,
523 p0_001: 1877,
524 p0_01: 1877,
525 p0_1: 1877,
526 p1: 1877,
527 p2_5: 1877,
528 p10: 1877,
529 p25: 1877,
530 p50: 1881,
531 p75: 1881,
532 p90: 1882,
533 p97_5: 1882,
534 p99: 1882,
535 p99_9: 1882,
536 p99_99: 1882,
537 p99_999: 1882
538}
539```
540
541### Static property: `isWorkerThread` (readonly)
542
543Is `true` if this code runs inside a `Piscina` threadpool as a Worker.
544
545### Static property: `version` (readonly)
546
547Provides the current version of this library as a semver string.
548
549### Static method: `move(value)`
550
551By default, any value returned by a worker function will be cloned when
552returned back to the Piscina pool, even if that object is capable of
553being transfered. The `Piscina.move()` method can be used to wrap and
554mark transferable values such that they will by transfered rather than
555cloned.
556
557The `value` may be any object supported by Node.js to be transferable
558(e.g. `ArrayBuffer`, any `TypedArray`, or `MessagePort`), or any object
559implementing the `Transferable` interface.
560
561```js
562const { move } = require('piscina');
563
564module.exports = () => {
565 return move(new ArrayBuffer(10));
566}
567```
568
569The `move()` method will throw if the `value` is not transferable.
570
571The object returned by the `move()` method should not be set as a
572nested value in an object. If it is used, the `move()` object itself
573will be cloned as opposed to transfering the object it wraps.
574
575#### Interface: `Transferable`
576
577Objects may implement the `Transferable` interface to create their own
578custom transferable objects. This is useful when an object being
579passed into or from a worker contains a deeply nested transferable
580object such as an `ArrayBuffer` or `MessagePort`.
581
582`Transferable` objects expose two properties inspected by Piscina
583to determine how to transfer the object. These properties are
584named using the special static `Piscina.transferableSymbol` and
585`Piscina.valueSymbol` properties:
586
587* The `Piscina.transferableSymbol` property provides the object
588 (or objects) that are to be included in the `transferList`.
589
590* The `Piscina.valueSymbol` property provides a surrogate value
591 to transmit in place of the `Transferable` itself.
592
593Both properties are required.
594
595For example,
596
597```js
598const {
599 move,
600 transferableSymbol,
601 valueSymbol
602} = require('piscina');
603
604module.exports = () => {
605 const obj = {
606 a: { b: new Uint8Array(5); },
607 c: { new Uint8Array(10); },
608
609 get [transferableSymbol]() {
610 // Transfer the two underlying ArrayBuffers
611 return [this.a.b.buffer, this.c.buffer];
612 }
613
614 get [valueSymbol]() {
615 return { a: { b: this.a.b }, c: this.c };
616 }
617 };
618 return move(obj);
619};
620```
621
622## Custom Task Queues
623
624By default, Piscina uses a simple array-based first-in-first-out (fifo)
625task queue. When a new task is submitted and there are no available
626workers, tasks are pushed on to the queue until a worker becomes
627available.
628
629If the default fifo queue is not sufficient, user code may replace the
630task queue implementation with a custom implementation using the
631`taskQueue` option on the Piscina constructor.
632
633Custom task queue objects *must* implement the `TaskQueue` interface,
634described below using TypeScript syntax:
635
636```ts
637interface Task {
638 readonly [Piscina.queueOptionsSymbol] : object | null;
639}
640
641interface TaskQueue {
642 readonly size : number;
643 shift () : Task | null;
644 remove (task : Task) : void;
645 push (task : Task) : void;
646}
647```
648
649An example of a custom task queue that uses a shuffled priority queue
650is available in [`examples/task-queue`](./examples/task-queue/index.js);
651
652The special symbol `Piscina.queueOptionsSymbol` may be set as a property
653on tasks submitted to `run()` or `runTask()` as a way of passing additional
654options on to the custom `TaskQueue` implementation. (Note that because the
655queue options are set as a property on the task, tasks with queue
656options cannot be submitted as JavaScript primitives).
657
658## Current Limitations (Things we're working on / would love help with)
659
660* Improved Documentation
661* Benchmarks
662
663## Performance Notes
664
665Workers are generally optimized for offloading synchronous,
666compute-intensive operations off the main Node.js event loop thread.
667While it is possible to perform asynchronous operations and I/O
668within a Worker, the performance advantages of doing so will be
669minimal.
670
671Specifically, it is worth noting that asynchronous operations
672within Node.js, including I/O such as file system operations
673or CPU-bound tasks such as crypto operations or compression
674algorithms, are already performed in parallel by Node.js and
675libuv on a per-process level. This means that there will be
676little performance impact on moving such async operations into
677a Piscina worker (see examples/scrypt for example).
678
679### Queue Size
680
681Piscina provides the ability to configure the minimum and
682maximum number of worker threads active in the pool, as well as
683set limits on the number of tasks that may be queued up waiting
684for a free worker. It is important to note that setting the
685`maxQueue` size too high relative to the number of worker threads
686can have a detrimental impact on performance and memory usage.
687Setting the `maxQueue` size too small can also be problematic
688as doing so could cause your worker threads to become idle and
689be shutdown. Our testing has shown that a `maxQueue` size of
690approximately the square of the maximum number of threads is
691generally sufficient and performs well for many cases, but this
692will vary significantly depending on your workload. It will be
693important to test and benchmark your worker pools to ensure you've
694effectively balanced queue wait times, memory usage, and worker
695pool utilization.
696
697### Queue Pressure and Idle Threads
698
699The thread pool maintained by Piscina has both a minimum and maximum
700limit to the number of threads that may be created. When a Piscina
701instance is created, it will spawn the minimum number of threads
702immediately, then create additional threads as needed up to the
703limit set by `maxThreads`. Whenever a worker completes a task, a
704check is made to determine if there is additional work for it to
705perform. If there is no additional work, the thread is marked idle.
706By default, idle threads are shutdown immediately, with Piscina
707ensuring that the pool always maintains at least the minimum.
708
709When a Piscina pool is processing a stream of tasks (for instance,
710processing http server requests as in the React server-side
711rendering example in examples/react-ssr), if the rate in which
712new tasks are received and queued is not sufficient to keep workers
713from going idle and terminating, the pool can experience a thrashing
714effect -- excessively creating and terminating workers that will
715cause a net performance loss. There are a couple of strategies to
716avoid this churn:
717
718Strategy 1: Ensure that the queue rate of new tasks is sufficient to
719keep workers from going idle. We refer to this as "queue pressure".
720If the queue pressure is too low, workers will go idle and terminate.
721If the queue pressure is too high, tasks will stack up, experience
722increased wait latency, and consume additional memory.
723
724Strategy 2: Increase the `idleTimeout` configuration option. By
725default, idle threads terminate immediately. The `idleTimeout` option
726can be used to specify a longer period of time to wait for additional
727tasks to be submitted before terminating the worker. If the queue
728pressure is not maintained, this could result in workers sitting idle
729but those will have less of a performance impact than the thrashing
730that occurs when threads are repeatedly terminated and recreated.
731
732Strategy 3: Increase the `minThreads` configuration option. This has
733the same basic effect as increasing the `idleTimeout`. If the queue
734pressure is not high enough, workers may sit idle indefinitely but
735there will be less of a performance hit.
736
737In applications using Piscina, it will be most effective to use a
738combination of these three approaches and tune the various configuration
739parameters to find the optimum combination both for the application
740workload and the capabilities of the deployment environment. There
741are no one set of options that are going to work best.
742
743### Thread priority on Linux systems
744
745On Linux systems that support [`nice(2)`][], Piscina is capable of setting
746the priority of every worker in the pool. To use this mechanism, an additional
747optional native addon dependency (`nice-napi`, `npm i nice-napi`) is required.
748Once [`nice-napi`][] is installed, creating a `Piscina` instance with the
749`niceIncrement` configuration option will set the priority for the pool:
750
751```js
752const Piscina = require('piscina');
753const pool = new Piscina({
754 worker: '/absolute/path/to/worker.js',
755 niceIncrement: 20
756});
757```
758
759The higher the `niceIncrement`, the lower the CPU scheduling priority will be
760for the pooled workers which will generally extend the execution time of
761CPU-bound tasks but will help prevent those threads from stealing CPU time from
762the main Node.js event loop thread. Whether this is a good thing or not depends
763entirely on your application and will require careful profiling to get correct.
764
765The key metrics to pay attention to when tuning the `niceIncrement` are the
766sampled run times of the tasks in the worker pool (using the [`runTime`][]
767property) and the [delay of the Node.js main thread event loop][].
768
769### Multiple Thread Pools and Embedding Piscina as a Dependency
770
771Every `Piscina` instance creates a separate pool of threads and operates
772without any awareness of the other. When multiple pools are created in a
773single application the various threads may contend with one another, and
774with the Node.js main event loop thread, and may cause an overall reduction
775in system performance.
776
777Modules that embed Piscina as a dependency *should* make it clear via
778documentation that threads are being used. It would be ideal if those
779would make it possible for users to provide an existing `Piscina` instance
780as a configuration option in lieu of always creating their own.
781
782
783## Release Notes
784
785### 3.1.0
786
787* Deprecates `piscina.runTask()`; adds `piscina.run()` as an alternative.
788 https://github.com/piscinajs/piscina/commit/d7fa24d7515789001f7237ad6ae9ad42d582fc75
789* Allows multiple exported handler functions from a single file.
790 https://github.com/piscinajs/piscina/commit/d7fa24d7515789001f7237ad6ae9ad42d582fc75
791
792### 3.0.0
793
794* Drops Node.js 10.x support
795* Updates minimum TypeScript target to ES2019
796
797### 2.1.0
798
799* Adds name property to indicate `AbortError` when tasks are
800 canceled using an `AbortController` (or similar)
801* More examples
802
803### 2.0.0
804
805* Added unmanaged file descriptor tracking
806* Updated dependencies
807
808### 1.6.1
809
810* Bug fix: Reject if AbortSignal is already aborted
811* Bug Fix: Use once listener for abort event
812
813### 1.6.0
814
815* Add the `niceIncrement` configuration parameter.
816
817### 1.5.1
818
819* Bug fixes around abortable task selection.
820
821### 1.5.0
822
823* Added `Piscina.move()`
824* Added Custom Task Queues
825* Added utilization metric
826* Wait for workers to be ready before considering them as candidates
827* Additional examples
828
829### 1.4.0
830
831* Added `maxQueue = 'auto'` to autocalculate the maximum queue size.
832* Added more examples, including an example of implementing a worker
833 as a Node.js native addon.
834
835### 1.3.0
836
837* Added the `'drain'` event
838
839### 1.2.0
840
841* Added support for ESM and file:// URLs
842* Added `env`, `argv`, `execArgv`, and `workerData` options
843* More examples
844
845### 1.1.0
846
847* Added support for Worker Thread `resourceLimits`
848
849### 1.0.0
850
851* Initial release!
852
853## The Team
854
855* James M Snell <jasnell@gmail.com>
856* Anna Henningsen <anna@addaleax.net>
857* Matteo Collina <matteo.collina@gmail.com>
858
859## Acknowledgements
860
861Piscina development is sponsored by [NearForm Research][].
862
863[`Atomics`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics
864[`EventEmitter`]: https://nodejs.org/api/events.html
865[`postMessage`]: https://nodejs.org/api/worker_threads.html#worker_threads_port_postmessage_value_transferlist
866[`examples/task-queue`]: https://github.com/jasnell/piscina/blob/master/examples/task-queue/index.js
867[`nice(2)`]: https://linux.die.net/man/2/nice
868[`nice-napi`]: https://npmjs.org/package/nice-napi
869[`runTime`]: #property-runtime-readonly
870[Custom Task Queues]: #custom_task_queues
871[ES modules]: https://nodejs.org/api/esm.html
872[Node.js new Worker options]: https://nodejs.org/api/worker_threads.html#worker_threads_new_worker_filename_options
873[MIT Licensed]: LICENSE.md
874[NearForm Research]: https://www.nearform.com/research/
875[delay of the Node.js main thread event loop]: https://nodejs.org/dist/latest-v14.x/docs/api/perf_hooks.html#perf_hooks_perf_hooks_monitoreventloopdelay_options
Note: See TracBrowser for help on using the repository browser.