poolifier 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -16
- package/lib/index.d.ts +773 -15
- package/lib/index.js +1 -1
- package/package.json +33 -32
- package/CHANGELOG.md +0 -81
- package/lib/pools/abstract-pool.d.ts +0 -213
- package/lib/pools/cluster/dynamic.d.ts +0 -28
- package/lib/pools/cluster/fixed.d.ts +0 -49
- package/lib/pools/pool-internal.d.ts +0 -47
- package/lib/pools/pool.d.ts +0 -26
- package/lib/pools/selection-strategies.d.ts +0 -58
- package/lib/pools/thread/dynamic.d.ts +0 -29
- package/lib/pools/thread/fixed.d.ts +0 -40
- package/lib/utility-types.d.ts +0 -58
- package/lib/worker/abstract-worker.d.ts +0 -90
- package/lib/worker/cluster-worker.d.ts +0 -31
- package/lib/worker/thread-worker.d.ts +0 -30
- package/lib/worker/worker-options.d.ts +0 -61
package/lib/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("events"),t=require("cluster"),r=require("worker_threads"),s=require("async_hooks");function o(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var i,n=o(e),a=o(t);!function(e){e.FIXED="fixed",e.DYNAMIC="dynamic"}(i||(i={}));class h extends n.default{}const c=()=>{},l=Object.freeze({SOFT:"SOFT",HARD:"HARD"});const k=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LESS_RECENTLY_USED:"LESS_RECENTLY_USED"});class u{constructor(e){this.pool=e,this.nextWorkerIndex=0}choose(){const e=this.pool.workers[this.nextWorkerIndex];return this.nextWorkerIndex=this.pool.workers.length-1===this.nextWorkerIndex?0:this.nextWorkerIndex+1,e}}class p{constructor(e){this.pool=e}choose(){const e=this.pool.type===i.DYNAMIC;let t,r=1/0;for(const[s,o]of this.pool.tasks){if(!e&&0===o)return s;o<r&&(t=s,r=o)}return t}}class d{constructor(e,t,r=k.ROUND_ROBIN){this.pool=e,this.createDynamicallyWorkerCallback=t,this.workerChoiceStrategy=W.getWorkerChoiceStrategy(this.pool,r)}choose(){const e=this.pool.findFreeTasksMapEntry();return e?e[0]:this.pool.busy?this.workerChoiceStrategy.choose():this.createDynamicallyWorkerCallback()}}class y{constructor(e,t,r=k.ROUND_ROBIN){this.pool=e,this.createDynamicallyWorkerCallback=t,this.setWorkerChoiceStrategy(r)}getPoolWorkerChoiceStrategy(e=k.ROUND_ROBIN){return this.pool.type===i.DYNAMIC?new d(this.pool,this.createDynamicallyWorkerCallback,e):W.getWorkerChoiceStrategy(this.pool,e)}setWorkerChoiceStrategy(e){this.workerChoiceStrategy=this.getPoolWorkerChoiceStrategy(e)}execute(){return this.workerChoiceStrategy.choose()}}class W{static getWorkerChoiceStrategy(e,t=k.ROUND_ROBIN){switch(t){case k.ROUND_ROBIN:return new u(e);case k.LESS_RECENTLY_USED:return new p(e);default:throw new Error(`Worker choice strategy '${t}' not found`)}}}class m{constructor(e,t,r){if(this.numberOfWorkers=e,this.filePath=t,this.opts=r,this.workers=[],this.tasks=new Map,this.promiseMap=new Map,this.nextMessageId=0,!this.isMain())throw new Error("Cannot start a pool from a worker!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker();this.opts.enableEvents&&(this.emitter=new h),this.workerChoiceStrategyContext=new y(this,(()=>{const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(async t=>{const r=this.tasks.get(e);var s;s=l.HARD,(t.kill===s||0===r)&&await this.destroyWorker(e)})),e}),this.opts.workerChoiceStrategy)}checkFilePath(e){if(!e)throw new Error("Please specify a file with a worker implementation")}checkNumberOfWorkers(e){if(null==e)throw new Error("Cannot instantiate a pool without specifying the number of workers");if(!Number.isSafeInteger(e))throw new Error("Cannot instantiate a pool with a non integer number of workers");if(e<0)throw new Error("Cannot instantiate a pool with a negative number of workers");if(this.type===i.FIXED&&0===e)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){var t,r;this.opts.workerChoiceStrategy=null!==(t=e.workerChoiceStrategy)&&void 0!==t?t:k.ROUND_ROBIN,this.opts.enableEvents=null===(r=e.enableEvents)||void 0===r||r}get numberOfRunningTasks(){return this.promiseMap.size}setWorkerChoiceStrategy(e){this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(e)}internalGetBusyStatus(){return this.numberOfRunningTasks>=this.numberOfWorkers&&!1===this.findFreeTasksMapEntry()}findFreeTasksMapEntry(){for(const[e,t]of this.tasks)if(0===t)return[e,t];return!1}execute(e){const t=this.chooseWorker(),r=++this.nextMessageId,s=this.internalExecute(t,r);return this.checkAndEmitBusy(),this.sendToWorker(t,{data:e||{},id:r}),s}async destroy(){await Promise.all(this.workers.map((e=>this.destroyWorker(e))))}setupHook(){}increaseWorkersTask(e){this.stepWorkerNumberOfTasks(e,1)}decreaseWorkersTasks(e){this.stepWorkerNumberOfTasks(e,-1)}stepWorkerNumberOfTasks(e,t){const r=this.tasks.get(e);if(void 0===r)throw Error("Worker could not be found in tasks map");this.tasks.set(e,r+t)}removeWorker(e){const t=this.workers.indexOf(e);this.workers.splice(t,1),this.tasks.delete(e)}chooseWorker(){return this.workerChoiceStrategyContext.execute()}internalExecute(e,t){return this.increaseWorkersTask(e),new Promise(((r,s)=>{this.promiseMap.set(t,{resolve:r,reject:s,worker:e})}))}createAndSetupWorker(){var e,t,r,s;const o=this.createWorker();return o.on("message",null!==(e=this.opts.messageHandler)&&void 0!==e?e:c),o.on("error",null!==(t=this.opts.errorHandler)&&void 0!==t?t:c),o.on("online",null!==(r=this.opts.onlineHandler)&&void 0!==r?r:c),o.on("exit",null!==(s=this.opts.exitHandler)&&void 0!==s?s:c),o.once("exit",(()=>this.removeWorker(o))),this.workers.push(o),this.tasks.set(o,0),this.afterWorkerSetup(o),o}workerListener(){return e=>{if(e.id){const t=this.promiseMap.get(e.id);t&&(this.decreaseWorkersTasks(t.worker),e.error?t.reject(e.error):t.resolve(e.data),this.promiseMap.delete(e.id))}}}checkAndEmitBusy(){var e;this.opts.enableEvents&&this.busy&&(null===(e=this.emitter)||void 0===e||e.emit("busy"))}}class w extends m{constructor(e,t,r={}){super(e,t,r),this.opts=r}setupHook(){a.default.setupPrimary({exec:this.filePath})}isMain(){return a.default.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.kill()}sendToWorker(e,t){e.send(t)}registerWorkerMessageListener(e,t){e.on("message",t)}createWorker(){return a.default.fork(this.opts.env)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,super.workerListener())}get type(){return i.FIXED}get busy(){return this.internalGetBusyStatus()}}class f extends m{constructor(e,t,r={}){super(e,t,r)}isMain(){return r.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,t){e.postMessage(t)}registerWorkerMessageListener(e,t){var r;null===(r=e.port2)||void 0===r||r.on("message",t)}createWorker(){return new r.Worker(this.filePath,{env:r.SHARE_ENV})}afterWorkerSetup(e){const{port1:t,port2:s}=new r.MessageChannel;e.postMessage({parent:t},[t]),e.port1=t,e.port2=s,this.registerWorkerMessageListener(e,super.workerListener())}get type(){return i.FIXED}get busy(){return this.internalGetBusyStatus()}}const g=l.SOFT;class v extends s.AsyncResource{constructor(e,t,r,s,o={killBehavior:g,maxInactiveTime:6e4}){var i,n;super(e),this.mainWorker=s,this.opts=o,this.checkFunctionInput(r),this.checkWorkerOptions(this.opts),this.lastTaskTimestamp=Date.now(),t||(this.aliveInterval=setInterval(this.checkAlive.bind(this),(null!==(i=this.opts.maxInactiveTime)&&void 0!==i?i:6e4)/2),this.checkAlive.bind(this)()),null===(n=this.mainWorker)||void 0===n||n.on("message",(e=>{(null==e?void 0:e.data)&&e.id?this.opts.async?this.runInAsyncScope(this.runAsync.bind(this),this,r,e):this.runInAsyncScope(this.run.bind(this),this,r,e):e.parent?this.mainWorker=e.parent:e.kill&&(this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}))}checkWorkerOptions(e){var t,r;this.opts.killBehavior=null!==(t=e.killBehavior)&&void 0!==t?t:g,this.opts.maxInactiveTime=null!==(r=e.maxInactiveTime)&&void 0!==r?r:6e4,this.opts.async=!!e.async}checkFunctionInput(e){if(!e)throw new Error("fn parameter is mandatory")}getMainWorker(){if(!this.mainWorker)throw new Error("Main worker was not set");return this.mainWorker}checkAlive(){var e;Date.now()-this.lastTaskTimestamp>(null!==(e=this.opts.maxInactiveTime)&&void 0!==e?e:6e4)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}run(e,t){try{const r=e(t.data);this.sendToMainWorker({data:r,id:t.id})}catch(e){const r=this.handleError(e);this.sendToMainWorker({error:r,id:t.id})}finally{this.lastTaskTimestamp=Date.now()}}runAsync(e,t){e(t.data).then((e=>(this.sendToMainWorker({data:e,id:t.id}),null))).catch((e=>{const r=this.handleError(e);this.sendToMainWorker({error:r,id:t.id})})).finally((()=>{this.lastTaskTimestamp=Date.now()})).catch(c)}}exports.AbstractWorker=v,exports.ClusterWorker=class extends v{constructor(e,t={}){super("worker-cluster-pool:poolifier",a.default.isPrimary,e,a.default.worker,t)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}},exports.DynamicClusterPool=class extends w{constructor(e,t,r,s={}){super(e,r,s),this.max=t}get type(){return i.DYNAMIC}get busy(){return this.workers.length===this.max}},exports.DynamicThreadPool=class extends f{constructor(e,t,r,s={}){super(e,r,s),this.max=t}get type(){return i.DYNAMIC}get busy(){return this.workers.length===this.max}},exports.FixedClusterPool=w,exports.FixedThreadPool=f,exports.KillBehaviors=l,exports.ThreadWorker=class extends v{constructor(e,t={}){super("worker-thread-pool:poolifier",r.isMainThread,e,r.parentPort,t)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=k;
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poolifier",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "A fast, easy to use Node.js Worker Thread Pool and Cluster Pool implementation",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "rollup --config --environment BUILD:development",
|
|
8
|
+
"build:typedoc": "rollup --config --environment BUILD:development --environment DOCUMENTATION",
|
|
8
9
|
"build:prod": "rollup --config",
|
|
9
10
|
"benchmark": "npm run build && node -r source-map-support/register benchmarks/internal/bench.js",
|
|
10
11
|
"benchmark:debug": "npm run build && node -r source-map-support/register --inspect benchmarks/internal/bench.js",
|
|
11
12
|
"benchmark:prod": "npm run build:prod && node -r source-map-support/register benchmarks/internal/bench.js",
|
|
12
|
-
"test": "npm run build && nyc mocha
|
|
13
|
-
"test:debug": "npm run build && mocha --inspect 'tests/**/*.test.js'",
|
|
14
|
-
"test:prod": "npm run build:prod && nyc mocha
|
|
15
|
-
"sonar": "sonar-scanner",
|
|
13
|
+
"test": "npm run build && nyc mocha 'tests/**/*.test.js'",
|
|
14
|
+
"test:debug": "npm run build && mocha --no-parallel --inspect 'tests/**/*.test.js'",
|
|
15
|
+
"test:prod": "npm run build:prod && nyc mocha 'tests/**/*.test.js'",
|
|
16
16
|
"coverage": "nyc report --reporter=lcov",
|
|
17
17
|
"coverage:html": "nyc report --reporter=html",
|
|
18
18
|
"format": "prettier --loglevel silent --write .; prettierx --write .",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"repository": {
|
|
25
25
|
"type": "git",
|
|
26
|
-
"url": "git+https://github.com/
|
|
26
|
+
"url": "git+https://github.com/poolifier/poolifier.git"
|
|
27
27
|
},
|
|
28
28
|
"keywords": [
|
|
29
29
|
"node",
|
|
@@ -55,46 +55,47 @@
|
|
|
55
55
|
],
|
|
56
56
|
"license": "MIT",
|
|
57
57
|
"bugs": {
|
|
58
|
-
"url": "https://github.com/
|
|
58
|
+
"url": "https://github.com/poolifier/poolifier/issues"
|
|
59
59
|
},
|
|
60
|
-
"homepage": "https://github.com/
|
|
60
|
+
"homepage": "https://github.com/poolifier/poolifier#readme",
|
|
61
61
|
"files": [
|
|
62
62
|
"lib"
|
|
63
63
|
],
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@types/node": "^
|
|
66
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
67
|
-
"@typescript-eslint/parser": "^
|
|
65
|
+
"@types/node": "^16.11.19",
|
|
66
|
+
"@typescript-eslint/eslint-plugin": "^5.9.0",
|
|
67
|
+
"@typescript-eslint/parser": "^5.9.0",
|
|
68
68
|
"benchmark": "^2.1.4",
|
|
69
|
-
"eslint": "^
|
|
70
|
-
"eslint-config-standard": "^16.0.
|
|
71
|
-
"eslint-
|
|
72
|
-
"eslint-plugin-
|
|
69
|
+
"eslint": "^8.6.0",
|
|
70
|
+
"eslint-config-standard": "^16.0.3",
|
|
71
|
+
"eslint-define-config": "^1.2.1",
|
|
72
|
+
"eslint-plugin-import": "^2.25.4",
|
|
73
|
+
"eslint-plugin-jsdoc": "^37.5.1",
|
|
73
74
|
"eslint-plugin-node": "^11.1.0",
|
|
74
|
-
"eslint-plugin-prettierx": "^0.
|
|
75
|
-
"eslint-plugin-promise": "^
|
|
76
|
-
"eslint-plugin-spellcheck": "0.0.
|
|
77
|
-
"expect": "^
|
|
75
|
+
"eslint-plugin-prettierx": "^0.18.0",
|
|
76
|
+
"eslint-plugin-promise": "^6.0.0",
|
|
77
|
+
"eslint-plugin-spellcheck": "0.0.19",
|
|
78
|
+
"expect": "^27.4.6",
|
|
78
79
|
"microtime": "^3.0.0",
|
|
79
|
-
"mocha": "^
|
|
80
|
-
"
|
|
80
|
+
"mocha": "^9.1.3",
|
|
81
|
+
"mochawesome": "^7.0.1",
|
|
81
82
|
"nyc": "^15.1.0",
|
|
82
|
-
"prettier": "^2.
|
|
83
|
-
"prettier-plugin-organize-imports": "^
|
|
84
|
-
"prettierx": "^0.
|
|
85
|
-
"rollup": "^2.
|
|
83
|
+
"prettier": "^2.5.1",
|
|
84
|
+
"prettier-plugin-organize-imports": "^2.3.4",
|
|
85
|
+
"prettierx": "^0.18.3",
|
|
86
|
+
"rollup": "^2.63.0",
|
|
86
87
|
"rollup-plugin-analyzer": "^4.0.0",
|
|
87
88
|
"rollup-plugin-command": "^1.1.3",
|
|
88
89
|
"rollup-plugin-delete": "^2.0.0",
|
|
90
|
+
"rollup-plugin-istanbul": "^3.0.0",
|
|
89
91
|
"rollup-plugin-terser": "^7.0.2",
|
|
90
|
-
"rollup-plugin-
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"typescript": "^4.2.2"
|
|
92
|
+
"rollup-plugin-ts": "^2.0.5",
|
|
93
|
+
"source-map-support": "^0.5.21",
|
|
94
|
+
"typedoc": "^0.22.10",
|
|
95
|
+
"typescript": "^4.5.4"
|
|
95
96
|
},
|
|
96
97
|
"engines": {
|
|
97
|
-
"node": ">=
|
|
98
|
-
"npm": ">=
|
|
98
|
+
"node": ">=16.0.0",
|
|
99
|
+
"npm": ">=8.0.0"
|
|
99
100
|
}
|
|
100
101
|
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
|
|
8
|
-
## [2.0.0] - 2021-01-03
|
|
9
|
-
|
|
10
|
-
### Bug fixes
|
|
11
|
-
|
|
12
|
-
- Now a thread/process by default is not deleted when the task submitted take more time than maxInactiveTime configured (issue #70).
|
|
13
|
-
|
|
14
|
-
### Breaking Changes
|
|
15
|
-
|
|
16
|
-
- `FullPool` event is now renamed to `busy`.
|
|
17
|
-
- `maxInactiveTime` on `ThreadWorker` default behavior is now changed, if you want to keep the old behavior set `killBehavior` to `KillBehaviors.HARD`.
|
|
18
|
-
_Find more details on our JSDoc._
|
|
19
|
-
|
|
20
|
-
- `maxTasks` option on `FixedThreadPool` and `DynamicThreadPool` is now removed since is no more needed.
|
|
21
|
-
|
|
22
|
-
- We changed some internal structures, but you shouldn't be too affected by them as these are internal changes.
|
|
23
|
-
|
|
24
|
-
### Pool options types declaration merge
|
|
25
|
-
|
|
26
|
-
`FixedThreadPoolOptions` and `DynamicThreadPoolOptions` type declarations have been merged to `PoolOptions<Worker>`.
|
|
27
|
-
|
|
28
|
-
#### New `export` strategy
|
|
29
|
-
|
|
30
|
-
```js
|
|
31
|
-
// Before
|
|
32
|
-
const DynamicThreadPool = require('poolifier/lib/dynamic')
|
|
33
|
-
// After
|
|
34
|
-
const { DynamicThreadPool } = require('poolifier/lib/dynamic')
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
But you should always prefer just using
|
|
38
|
-
|
|
39
|
-
```js
|
|
40
|
-
const { DynamicThreadPool } = require('poolifier')
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
#### New type definitions for input data and response
|
|
44
|
-
|
|
45
|
-
For cluster worker and worker-thread pools, you can now only send and receive serializable data.
|
|
46
|
-
_This is not a limitation by poolifier but NodeJS._
|
|
47
|
-
|
|
48
|
-
#### Public property replacements
|
|
49
|
-
|
|
50
|
-
`numWorkers` property is now `numberOfWorkers`
|
|
51
|
-
|
|
52
|
-
#### Internal (protected) properties and methods renaming
|
|
53
|
-
|
|
54
|
-
These properties are not intended for end users
|
|
55
|
-
|
|
56
|
-
- `id` => `nextMessageId`
|
|
57
|
-
|
|
58
|
-
These methods are not intended for end users
|
|
59
|
-
|
|
60
|
-
- `_chooseWorker` => `chooseWorker`
|
|
61
|
-
- `_newWorker` => `createWorker`
|
|
62
|
-
- `_execute` => `internalExecute`
|
|
63
|
-
- `_chooseWorker` => `chooseWorker`
|
|
64
|
-
- `_checkAlive` => `checkAlive`
|
|
65
|
-
- `_run` => `run`
|
|
66
|
-
- `_runAsync` => `runAsync`
|
|
67
|
-
|
|
68
|
-
## [1.1.0] - 2020-21-05
|
|
69
|
-
|
|
70
|
-
### Added
|
|
71
|
-
|
|
72
|
-
- ThreadWorker support async functions as option
|
|
73
|
-
- Various external library patches
|
|
74
|
-
|
|
75
|
-
## [1.0.0] - 2020-24-01
|
|
76
|
-
|
|
77
|
-
### Added
|
|
78
|
-
|
|
79
|
-
- FixedThreadPool implementation
|
|
80
|
-
- DynamicThreadPool implementation
|
|
81
|
-
- WorkerThread implementation to improve developer experience
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import type { MessageValue, PromiseWorkerResponseWrapper } from '../utility-types';
|
|
2
|
-
import type { IPoolInternal } from './pool-internal';
|
|
3
|
-
import { PoolEmitter } from './pool-internal';
|
|
4
|
-
import type { WorkerChoiceStrategy } from './selection-strategies';
|
|
5
|
-
import { WorkerChoiceStrategyContext } from './selection-strategies';
|
|
6
|
-
/**
|
|
7
|
-
* Callback invoked if the worker raised an error.
|
|
8
|
-
*/
|
|
9
|
-
export declare type ErrorHandler<Worker> = (this: Worker, e: Error) => void;
|
|
10
|
-
/**
|
|
11
|
-
* Callback invoked when the worker has started successfully.
|
|
12
|
-
*/
|
|
13
|
-
export declare type OnlineHandler<Worker> = (this: Worker) => void;
|
|
14
|
-
/**
|
|
15
|
-
* Callback invoked when the worker exits successfully.
|
|
16
|
-
*/
|
|
17
|
-
export declare type ExitHandler<Worker> = (this: Worker, code: number) => void;
|
|
18
|
-
/**
|
|
19
|
-
* Basic interface that describes the minimum required implementation of listener events for a pool-worker.
|
|
20
|
-
*/
|
|
21
|
-
export interface IWorker {
|
|
22
|
-
/**
|
|
23
|
-
* Register a listener to the error event.
|
|
24
|
-
*
|
|
25
|
-
* @param event `'error'`.
|
|
26
|
-
* @param handler The error handler.
|
|
27
|
-
*/
|
|
28
|
-
on(event: 'error', handler: ErrorHandler<this>): void;
|
|
29
|
-
/**
|
|
30
|
-
* Register a listener to the online event.
|
|
31
|
-
*
|
|
32
|
-
* @param event `'online'`.
|
|
33
|
-
* @param handler The online handler.
|
|
34
|
-
*/
|
|
35
|
-
on(event: 'online', handler: OnlineHandler<this>): void;
|
|
36
|
-
/**
|
|
37
|
-
* Register a listener to the exit event.
|
|
38
|
-
*
|
|
39
|
-
* @param event `'exit'`.
|
|
40
|
-
* @param handler The exit handler.
|
|
41
|
-
*/
|
|
42
|
-
on(event: 'exit', handler: ExitHandler<this>): void;
|
|
43
|
-
/**
|
|
44
|
-
* Register a listener to the exit event that will only performed once.
|
|
45
|
-
*
|
|
46
|
-
* @param event `'exit'`.
|
|
47
|
-
* @param handler The exit handler.
|
|
48
|
-
*/
|
|
49
|
-
once(event: 'exit', handler: ExitHandler<this>): void;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Options for a poolifier pool.
|
|
53
|
-
*/
|
|
54
|
-
export interface PoolOptions<Worker> {
|
|
55
|
-
/**
|
|
56
|
-
* A function that will listen for error event on each worker.
|
|
57
|
-
*/
|
|
58
|
-
errorHandler?: ErrorHandler<Worker>;
|
|
59
|
-
/**
|
|
60
|
-
* A function that will listen for online event on each worker.
|
|
61
|
-
*/
|
|
62
|
-
onlineHandler?: OnlineHandler<Worker>;
|
|
63
|
-
/**
|
|
64
|
-
* A function that will listen for exit event on each worker.
|
|
65
|
-
*/
|
|
66
|
-
exitHandler?: ExitHandler<Worker>;
|
|
67
|
-
/**
|
|
68
|
-
* The work choice strategy to use in this pool.
|
|
69
|
-
*/
|
|
70
|
-
workerChoiceStrategy?: WorkerChoiceStrategy;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Base class containing some shared logic for all poolifier pools.
|
|
74
|
-
*
|
|
75
|
-
* @template Worker Type of worker which manages this pool.
|
|
76
|
-
* @template Data Type of data sent to the worker. This can only be serializable data.
|
|
77
|
-
* @template Response Type of response of execution. This can only be serializable data.
|
|
78
|
-
*/
|
|
79
|
-
export declare abstract class AbstractPool<Worker extends IWorker, Data = unknown, Response = unknown> implements IPoolInternal<Worker, Data, Response> {
|
|
80
|
-
readonly numberOfWorkers: number;
|
|
81
|
-
readonly filePath: string;
|
|
82
|
-
readonly opts: PoolOptions<Worker>;
|
|
83
|
-
/** @inheritdoc */
|
|
84
|
-
readonly workers: Worker[];
|
|
85
|
-
/** @inheritdoc */
|
|
86
|
-
readonly tasks: Map<Worker, number>;
|
|
87
|
-
/** @inheritdoc */
|
|
88
|
-
readonly emitter: PoolEmitter;
|
|
89
|
-
/**
|
|
90
|
-
* The promise map.
|
|
91
|
-
*
|
|
92
|
-
* - `key`: This is the message ID of each submitted task.
|
|
93
|
-
* - `value`: An object that contains the worker, the resolve function and the reject function.
|
|
94
|
-
*
|
|
95
|
-
* When we receive a message from the worker we get a map entry and resolve/reject the promise based on the message.
|
|
96
|
-
*/
|
|
97
|
-
protected promiseMap: Map<number, PromiseWorkerResponseWrapper<Worker, Response>>;
|
|
98
|
-
/**
|
|
99
|
-
* ID of the next message.
|
|
100
|
-
*/
|
|
101
|
-
protected nextMessageId: number;
|
|
102
|
-
/**
|
|
103
|
-
* Worker choice strategy instance implementing the worker choice algorithm.
|
|
104
|
-
*
|
|
105
|
-
* Default to a strategy implementing a round robin algorithm.
|
|
106
|
-
*/
|
|
107
|
-
protected workerChoiceStrategyContext: WorkerChoiceStrategyContext<Worker, Data, Response>;
|
|
108
|
-
/**
|
|
109
|
-
* Constructs a new poolifier pool.
|
|
110
|
-
*
|
|
111
|
-
* @param numberOfWorkers Number of workers that this pool should manage.
|
|
112
|
-
* @param filePath Path to the worker-file.
|
|
113
|
-
* @param opts Options for the pool.
|
|
114
|
-
*/
|
|
115
|
-
constructor(numberOfWorkers: number, filePath: string, opts: PoolOptions<Worker>);
|
|
116
|
-
private checkFilePath;
|
|
117
|
-
private checkNumberOfWorkers;
|
|
118
|
-
/** @inheritdoc */
|
|
119
|
-
get dynamic(): boolean;
|
|
120
|
-
/** @inheritdoc */
|
|
121
|
-
setWorkerChoiceStrategy(workerChoiceStrategy: WorkerChoiceStrategy): void;
|
|
122
|
-
/** @inheritdoc */
|
|
123
|
-
execute(data: Data): Promise<Response>;
|
|
124
|
-
/** @inheritdoc */
|
|
125
|
-
destroy(): Promise<void>;
|
|
126
|
-
/**
|
|
127
|
-
* Shut down given worker.
|
|
128
|
-
*
|
|
129
|
-
* @param worker A worker within `workers`.
|
|
130
|
-
*/
|
|
131
|
-
protected abstract destroyWorker(worker: Worker): void | Promise<void>;
|
|
132
|
-
/**
|
|
133
|
-
* Setup hook that can be overridden by a Poolifier pool implementation
|
|
134
|
-
* to run code before workers are created in the abstract constructor.
|
|
135
|
-
*/
|
|
136
|
-
protected setupHook(): void;
|
|
137
|
-
/**
|
|
138
|
-
* Should return whether the worker is the main worker or not.
|
|
139
|
-
*/
|
|
140
|
-
protected abstract isMain(): boolean;
|
|
141
|
-
/**
|
|
142
|
-
* Increase the number of tasks that the given workers has done.
|
|
143
|
-
*
|
|
144
|
-
* @param worker Worker whose tasks are increased.
|
|
145
|
-
*/
|
|
146
|
-
protected increaseWorkersTask(worker: Worker): void;
|
|
147
|
-
/**
|
|
148
|
-
* Decrease the number of tasks that the given workers has done.
|
|
149
|
-
*
|
|
150
|
-
* @param worker Worker whose tasks are decreased.
|
|
151
|
-
*/
|
|
152
|
-
protected decreaseWorkersTasks(worker: Worker): void;
|
|
153
|
-
/**
|
|
154
|
-
* Step the number of tasks that the given workers has done.
|
|
155
|
-
*
|
|
156
|
-
* @param worker Worker whose tasks are set.
|
|
157
|
-
* @param step Worker number of tasks step.
|
|
158
|
-
*/
|
|
159
|
-
private stepWorkerNumberOfTasks;
|
|
160
|
-
/**
|
|
161
|
-
* Removes the given worker from the pool.
|
|
162
|
-
*
|
|
163
|
-
* @param worker Worker that will be removed.
|
|
164
|
-
*/
|
|
165
|
-
protected removeWorker(worker: Worker): void;
|
|
166
|
-
/**
|
|
167
|
-
* Choose a worker for the next task.
|
|
168
|
-
*
|
|
169
|
-
* The default implementation uses a round robin algorithm to distribute the load.
|
|
170
|
-
*
|
|
171
|
-
* @returns Worker.
|
|
172
|
-
*/
|
|
173
|
-
protected chooseWorker(): Worker;
|
|
174
|
-
/**
|
|
175
|
-
* Send a message to the given worker.
|
|
176
|
-
*
|
|
177
|
-
* @param worker The worker which should receive the message.
|
|
178
|
-
* @param message The message.
|
|
179
|
-
*/
|
|
180
|
-
protected abstract sendToWorker(worker: Worker, message: MessageValue<Data>): void;
|
|
181
|
-
/**
|
|
182
|
-
* Register a listener callback on a given worker.
|
|
183
|
-
*
|
|
184
|
-
* @param worker A worker.
|
|
185
|
-
* @param listener A message listener callback.
|
|
186
|
-
*/
|
|
187
|
-
protected abstract registerWorkerMessageListener<Message extends Data | Response>(worker: Worker, listener: (message: MessageValue<Message>) => void): void;
|
|
188
|
-
protected internalExecute(worker: Worker, messageId: number): Promise<Response>;
|
|
189
|
-
/**
|
|
190
|
-
* Returns a newly created worker.
|
|
191
|
-
*/
|
|
192
|
-
protected abstract createWorker(): Worker;
|
|
193
|
-
/**
|
|
194
|
-
* Function that can be hooked up when a worker has been newly created and moved to the workers registry.
|
|
195
|
-
*
|
|
196
|
-
* Can be used to update the `maxListeners` or binding the `main-worker`<->`worker` connection if not bind by default.
|
|
197
|
-
*
|
|
198
|
-
* @param worker The newly created worker.
|
|
199
|
-
*/
|
|
200
|
-
protected abstract afterWorkerSetup(worker: Worker): void;
|
|
201
|
-
/**
|
|
202
|
-
* Creates a new worker for this pool and sets it up completely.
|
|
203
|
-
*
|
|
204
|
-
* @returns New, completely set up worker.
|
|
205
|
-
*/
|
|
206
|
-
protected createAndSetupWorker(): Worker;
|
|
207
|
-
/**
|
|
208
|
-
* This function is the listener registered for each worker.
|
|
209
|
-
*
|
|
210
|
-
* @returns The listener function to execute when a message is sent from a worker.
|
|
211
|
-
*/
|
|
212
|
-
protected workerListener(): (message: MessageValue<Response>) => void;
|
|
213
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { ClusterPoolOptions } from './fixed';
|
|
2
|
-
import { FixedClusterPool } from './fixed';
|
|
3
|
-
/**
|
|
4
|
-
* A cluster pool with a dynamic number of workers, but a guaranteed minimum number of workers.
|
|
5
|
-
*
|
|
6
|
-
* This cluster pool creates new workers when the others are busy, up to the maximum number of workers.
|
|
7
|
-
* When the maximum number of workers is reached, an event is emitted. If you want to listen to this event, use the pool's `emitter`.
|
|
8
|
-
*
|
|
9
|
-
* @template Data Type of data sent to the worker. This can only be serializable data.
|
|
10
|
-
* @template Response Type of response of execution. This can only be serializable data.
|
|
11
|
-
*
|
|
12
|
-
* @author [Christopher Quadflieg](https://github.com/Shinigami92)
|
|
13
|
-
* @since 2.0.0
|
|
14
|
-
*/
|
|
15
|
-
export declare class DynamicClusterPool<Data = unknown, Response = unknown> extends FixedClusterPool<Data, Response> {
|
|
16
|
-
readonly max: number;
|
|
17
|
-
/**
|
|
18
|
-
* Constructs a new poolifier dynamic cluster pool.
|
|
19
|
-
*
|
|
20
|
-
* @param min Minimum number of workers which are always active.
|
|
21
|
-
* @param max Maximum number of workers that can be created by this pool.
|
|
22
|
-
* @param filePath Path to an implementation of a `ClusterWorker` file, which can be relative or absolute.
|
|
23
|
-
* @param opts Options for this dynamic cluster pool. Default: `{}`
|
|
24
|
-
*/
|
|
25
|
-
constructor(min: number, max: number, filePath: string, opts?: ClusterPoolOptions);
|
|
26
|
-
/** @inheritdoc */
|
|
27
|
-
get dynamic(): boolean;
|
|
28
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import { Worker } from 'cluster';
|
|
3
|
-
import type { MessageValue } from '../../utility-types';
|
|
4
|
-
import type { PoolOptions } from '../abstract-pool';
|
|
5
|
-
import { AbstractPool } from '../abstract-pool';
|
|
6
|
-
/**
|
|
7
|
-
* Options for a poolifier cluster pool.
|
|
8
|
-
*/
|
|
9
|
-
export interface ClusterPoolOptions extends PoolOptions<Worker> {
|
|
10
|
-
/**
|
|
11
|
-
* Key/value pairs to add to worker process environment.
|
|
12
|
-
*
|
|
13
|
-
* @see https://nodejs.org/api/cluster.html#cluster_cluster_fork_env
|
|
14
|
-
*/
|
|
15
|
-
env?: any;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* A cluster pool with a fixed number of workers.
|
|
19
|
-
*
|
|
20
|
-
* It is possible to perform tasks in sync or asynchronous mode as you prefer.
|
|
21
|
-
*
|
|
22
|
-
* This pool selects the workers in a round robin fashion.
|
|
23
|
-
*
|
|
24
|
-
* @template Data Type of data sent to the worker. This can only be serializable data.
|
|
25
|
-
* @template Response Type of response of execution. This can only be serializable data.
|
|
26
|
-
*
|
|
27
|
-
* @author [Christopher Quadflieg](https://github.com/Shinigami92)
|
|
28
|
-
* @since 2.0.0
|
|
29
|
-
*/
|
|
30
|
-
export declare class FixedClusterPool<Data = unknown, Response = unknown> extends AbstractPool<Worker, Data, Response> {
|
|
31
|
-
readonly opts: ClusterPoolOptions;
|
|
32
|
-
/**
|
|
33
|
-
* Constructs a new poolifier fixed cluster pool.
|
|
34
|
-
*
|
|
35
|
-
* @param numberOfWorkers Number of workers for this pool.
|
|
36
|
-
* @param filePath Path to an implementation of a `ClusterWorker` file, which can be relative or absolute.
|
|
37
|
-
* @param opts Options for this fixed cluster pool. Default: `{}`
|
|
38
|
-
*/
|
|
39
|
-
constructor(numberOfWorkers: number, filePath: string, opts?: ClusterPoolOptions);
|
|
40
|
-
protected setupHook(): void;
|
|
41
|
-
protected isMain(): boolean;
|
|
42
|
-
/** @inheritdoc */
|
|
43
|
-
destroyWorker(worker: Worker): void;
|
|
44
|
-
protected sendToWorker(worker: Worker, message: MessageValue<Data>): void;
|
|
45
|
-
/** @inheritdoc */
|
|
46
|
-
registerWorkerMessageListener<Message extends Data | Response>(worker: Worker, listener: (message: MessageValue<Message>) => void): void;
|
|
47
|
-
protected createWorker(): Worker;
|
|
48
|
-
protected afterWorkerSetup(worker: Worker): void;
|
|
49
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import EventEmitter from 'events';
|
|
3
|
-
import type { IWorker } from './abstract-pool';
|
|
4
|
-
import type { IPool } from './pool';
|
|
5
|
-
/**
|
|
6
|
-
* Internal poolifier pool emitter.
|
|
7
|
-
*/
|
|
8
|
-
export declare class PoolEmitter extends EventEmitter {
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Internal contract definition for a poolifier pool.
|
|
12
|
-
*
|
|
13
|
-
* @template Worker Type of worker which manages this pool.
|
|
14
|
-
* @template Data Type of data sent to the worker.
|
|
15
|
-
* @template Response Type of response of execution.
|
|
16
|
-
*/
|
|
17
|
-
export interface IPoolInternal<Worker extends IWorker, Data = unknown, Response = unknown> extends IPool<Data, Response> {
|
|
18
|
-
/**
|
|
19
|
-
* List of currently available workers.
|
|
20
|
-
*/
|
|
21
|
-
readonly workers: Worker[];
|
|
22
|
-
/**
|
|
23
|
-
* The tasks map.
|
|
24
|
-
*
|
|
25
|
-
* - `key`: The `Worker`
|
|
26
|
-
* - `value`: Number of tasks currently in progress on the worker.
|
|
27
|
-
*/
|
|
28
|
-
readonly tasks: Map<Worker, number>;
|
|
29
|
-
/**
|
|
30
|
-
* Emitter on which events can be listened to.
|
|
31
|
-
*
|
|
32
|
-
* Events that can currently be listened to:
|
|
33
|
-
*
|
|
34
|
-
* - `'busy'`
|
|
35
|
-
*/
|
|
36
|
-
readonly emitter: PoolEmitter;
|
|
37
|
-
/**
|
|
38
|
-
* Whether the pool is dynamic or not.
|
|
39
|
-
*
|
|
40
|
-
* If it is dynamic, it provides the `max` property.
|
|
41
|
-
*/
|
|
42
|
-
readonly dynamic: boolean;
|
|
43
|
-
/**
|
|
44
|
-
* Maximum number of workers that can be created by this pool.
|
|
45
|
-
*/
|
|
46
|
-
readonly max?: number;
|
|
47
|
-
}
|
package/lib/pools/pool.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { WorkerChoiceStrategy } from './selection-strategies';
|
|
2
|
-
/**
|
|
3
|
-
* Contract definition for a poolifier pool.
|
|
4
|
-
*
|
|
5
|
-
* @template Data Type of data sent to the worker. This can only be serializable data.
|
|
6
|
-
* @template Response Type of response of execution. This can only be serializable data.
|
|
7
|
-
*/
|
|
8
|
-
export interface IPool<Data = unknown, Response = unknown> {
|
|
9
|
-
/**
|
|
10
|
-
* Perform the task specified in the constructor with the data parameter.
|
|
11
|
-
*
|
|
12
|
-
* @param data The input for the specified task. This can only be serializable data.
|
|
13
|
-
* @returns Promise that will be resolved when the task is successfully completed.
|
|
14
|
-
*/
|
|
15
|
-
execute(data: Data): Promise<Response>;
|
|
16
|
-
/**
|
|
17
|
-
* Shut down every current worker in this pool.
|
|
18
|
-
*/
|
|
19
|
-
destroy(): Promise<void>;
|
|
20
|
-
/**
|
|
21
|
-
* Set the worker choice strategy in this pool.
|
|
22
|
-
*
|
|
23
|
-
* @param workerChoiceStrategy The worker choice strategy.
|
|
24
|
-
*/
|
|
25
|
-
setWorkerChoiceStrategy(workerChoiceStrategy: WorkerChoiceStrategy): void;
|
|
26
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import type { IWorker } from './abstract-pool';
|
|
2
|
-
import type { IPoolInternal } from './pool-internal';
|
|
3
|
-
/**
|
|
4
|
-
* Enumeration of worker choice strategies.
|
|
5
|
-
*/
|
|
6
|
-
export declare const WorkerChoiceStrategies: Readonly<{
|
|
7
|
-
/**
|
|
8
|
-
* Round robin worker selection strategy.
|
|
9
|
-
*/
|
|
10
|
-
readonly ROUND_ROBIN: "ROUND_ROBIN";
|
|
11
|
-
/**
|
|
12
|
-
* Less recently used worker selection strategy.
|
|
13
|
-
*/
|
|
14
|
-
readonly LESS_RECENTLY_USED: "LESS_RECENTLY_USED";
|
|
15
|
-
}>;
|
|
16
|
-
/**
|
|
17
|
-
* Worker choice strategy.
|
|
18
|
-
*/
|
|
19
|
-
export declare type WorkerChoiceStrategy = keyof typeof WorkerChoiceStrategies;
|
|
20
|
-
/**
|
|
21
|
-
* The worker choice strategy context.
|
|
22
|
-
*
|
|
23
|
-
* @template Worker Type of worker.
|
|
24
|
-
* @template Data Type of data sent to the worker. This can only be serializable data.
|
|
25
|
-
* @template Response Type of response of execution. This can only be serializable data.
|
|
26
|
-
*/
|
|
27
|
-
export declare class WorkerChoiceStrategyContext<Worker extends IWorker, Data, Response> {
|
|
28
|
-
private readonly pool;
|
|
29
|
-
private createDynamicallyWorkerCallback;
|
|
30
|
-
private workerChoiceStrategy;
|
|
31
|
-
/**
|
|
32
|
-
* Worker choice strategy context constructor.
|
|
33
|
-
*
|
|
34
|
-
* @param pool The pool instance.
|
|
35
|
-
* @param createDynamicallyWorkerCallback The worker creation callback for dynamic pool.
|
|
36
|
-
* @param workerChoiceStrategy The worker choice strategy.
|
|
37
|
-
*/
|
|
38
|
-
constructor(pool: IPoolInternal<Worker, Data, Response>, createDynamicallyWorkerCallback: () => Worker, workerChoiceStrategy?: WorkerChoiceStrategy);
|
|
39
|
-
/**
|
|
40
|
-
* Get the worker choice strategy instance specific to the pool type.
|
|
41
|
-
*
|
|
42
|
-
* @param workerChoiceStrategy The worker choice strategy.
|
|
43
|
-
* @returns The worker choice strategy instance for the pool type.
|
|
44
|
-
*/
|
|
45
|
-
private getPoolWorkerChoiceStrategy;
|
|
46
|
-
/**
|
|
47
|
-
* Set the worker choice strategy to use in the context.
|
|
48
|
-
*
|
|
49
|
-
* @param workerChoiceStrategy The worker choice strategy to set.
|
|
50
|
-
*/
|
|
51
|
-
setWorkerChoiceStrategy(workerChoiceStrategy: WorkerChoiceStrategy): void;
|
|
52
|
-
/**
|
|
53
|
-
* Choose a worker with the underlying selection strategy.
|
|
54
|
-
*
|
|
55
|
-
* @returns The chosen one.
|
|
56
|
-
*/
|
|
57
|
-
execute(): Worker;
|
|
58
|
-
}
|