poolifier 2.6.5 → 2.6.6
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 +21 -15
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -1
- package/lib/index.mjs +1 -1
- package/lib/pools/abstract-pool.d.ts +1 -1
- package/lib/pools/worker.d.ts +1 -1
- package/lib/utility-types.d.ts +1 -1
- package/lib/utils.d.ts +9 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -83,7 +83,7 @@ Please consult our [general guidelines](#general-guidance).
|
|
|
83
83
|
Node pool contains two [worker-threads](https://nodejs.org/api/worker_threads.html#worker_threads_worker_threads)/[cluster worker](https://nodejs.org/api/cluster.html#cluster_class_worker) pool implementations, you don't have to deal with worker-threads/cluster worker complexity.
|
|
84
84
|
The first implementation is a static worker pool, with a defined number of workers that are started at creation time and will be reused.
|
|
85
85
|
The second implementation is a dynamic worker pool with a number of worker started at creation time (these workers will be always active and reused) and other workers created when the load will increase (with an upper limit, these workers will be reused when active), the new created workers will be stopped after a configurable period of inactivity.
|
|
86
|
-
You have to implement your worker extending the ThreadWorker or ClusterWorker class.
|
|
86
|
+
You have to implement your worker by extending the ThreadWorker or ClusterWorker class.
|
|
87
87
|
|
|
88
88
|
## Installation
|
|
89
89
|
|
|
@@ -114,29 +114,35 @@ Instantiate your pool based on your needs :
|
|
|
114
114
|
|
|
115
115
|
```js
|
|
116
116
|
'use strict'
|
|
117
|
-
const { DynamicThreadPool, FixedThreadPool, PoolEvents } = require('poolifier')
|
|
117
|
+
const { DynamicThreadPool, FixedThreadPool, PoolEvents, availableParallelism } = require('poolifier')
|
|
118
118
|
|
|
119
119
|
// a fixed worker-threads pool
|
|
120
|
-
const pool = new FixedThreadPool(
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
const pool = new FixedThreadPool(availableParallelism(), './yourWorker.js', {
|
|
121
|
+
errorHandler: e => console.error(e),
|
|
122
|
+
onlineHandler: () => console.info('worker is online')
|
|
123
|
+
})
|
|
123
124
|
|
|
124
|
-
pool.emitter.on(PoolEvents.busy, () => console.
|
|
125
|
+
pool.emitter.on(PoolEvents.busy, () => console.info('Pool is busy'))
|
|
125
126
|
|
|
126
127
|
// or a dynamic worker-threads pool
|
|
127
|
-
const pool = new DynamicThreadPool(
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
const pool = new DynamicThreadPool(availableParallelism() / 2, availableParallelism(), './yourWorker.js', {
|
|
129
|
+
errorHandler: e => console.error(e),
|
|
130
|
+
onlineHandler: () => console.info('worker is online')
|
|
131
|
+
})
|
|
130
132
|
|
|
131
|
-
pool.emitter.on(PoolEvents.full, () => console.
|
|
132
|
-
pool.emitter.on(PoolEvents.busy, () => console.
|
|
133
|
+
pool.emitter.on(PoolEvents.full, () => console.info('Pool is full'))
|
|
134
|
+
pool.emitter.on(PoolEvents.busy, () => console.info('Pool is busy'))
|
|
133
135
|
|
|
134
136
|
// the execute method signature is the same for both implementations,
|
|
135
137
|
// so you can easy switch from one to another
|
|
136
|
-
pool
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
pool
|
|
139
|
+
.execute({})
|
|
140
|
+
.then(res => {
|
|
141
|
+
console.info(res)
|
|
142
|
+
})
|
|
143
|
+
.catch(err => {
|
|
144
|
+
console.error(err)
|
|
145
|
+
})
|
|
140
146
|
```
|
|
141
147
|
|
|
142
148
|
You can do the same with the classes ClusterWorker, FixedClusterPool and DynamicClusterPool.
|
package/lib/index.d.ts
CHANGED
|
@@ -18,3 +18,4 @@ export type { TaskFunctions, WorkerAsyncFunction, WorkerFunction, WorkerSyncFunc
|
|
|
18
18
|
export type { MessageValue, PromiseResponseWrapper, TaskError, TaskPerformance, WorkerStatistics } from './utility-types';
|
|
19
19
|
export type { CircularArray } from './circular-array';
|
|
20
20
|
export type { Queue } from './queue';
|
|
21
|
+
export { availableParallelism } from './utils';
|
package/lib/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("node:events"),t=require("node:cluster"),s=require("node:crypto"),r=require("node:perf_hooks"),i=require("node:os"),o=require("node:worker_threads"),a=require("node:async_hooks");const n=Object.freeze({fixed:"fixed",dynamic:"dynamic"}),h=Object.freeze({cluster:"cluster",thread:"thread"});class u extends e{}const k=Object.freeze({full:"full",busy:"busy",error:"error",taskError:"taskError"}),c=Object.freeze((()=>{})),l={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},d=e=>{if(Array.isArray(e)&&0===e.length)return 0;if(Array.isArray(e)&&1===e.length)return e[0];const t=e.slice().sort(((e,t)=>e-t));return(t[t.length-1>>1]+t[t.length>>1])/2},g=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),m=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class p extends Array{size;constructor(e=1024,...t){super(),this.checkSize(e),this.size=e,arguments.length>1&&this.push(...t)}push(...e){const t=super.push(...e);return t>this.size&&super.splice(0,t-this.size),this.length}unshift(...e){return super.unshift(...e)>this.size&&super.splice(this.size,e.length),this.length}concat(...e){const t=super.concat(e);return t.size=this.size,t.length>t.size&&t.splice(0,t.length-t.size),t}splice(e,t,...s){let r;return arguments.length>=3&&void 0!==t?(r=super.splice(e,t),this.push(...s)):r=2===arguments.length?super.splice(e,t):super.splice(e),r}resize(e){if(this.checkSize(e),0===e)this.length=0;else if(e<this.size)for(let t=e;t<this.size;t++)super.pop();this.size=e}empty(){return 0===this.length}full(){return this.length===this.size}checkSize(e){if(!Number.isSafeInteger(e))throw new TypeError(`Invalid circular array size: ${e} is not a safe integer`);if(e<0)throw new RangeError(`Invalid circular array size: ${e} < 0`)}}class w{items;offset;size;maxSize;constructor(){this.clear()}enqueue(e){return this.items.push(e),++this.size,this.size>this.maxSize&&(this.maxSize=this.size),this.size}dequeue(){if(this.size<=0)return;const e=this.items[this.offset];return 2*++this.offset>=this.items.length&&(this.items=this.items.slice(this.offset),this.offset=0),--this.size,e}peek(){if(!(this.size<=0))return this.items[this.offset]}clear(){this.items=[],this.offset=0,this.size=0,this.maxSize=0}}const T=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LEAST_USED:"LEAST_USED",LEAST_BUSY:"LEAST_BUSY",LEAST_ELU:"LEAST_ELU",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN",INTERLEAVED_WEIGHTED_ROUND_ROBIN:"INTERLEAVED_WEIGHTED_ROUND_ROBIN"}),f=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class W{pool;opts;nextWorkerNodeId=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:{aggregate:!1,average:!1,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};constructor(e,t=l){this.pool=e,this.opts=t,this.choose=this.choose.bind(this)}setTaskStatisticsRequirements(e){this.taskStatisticsRequirements.runTime.average&&!0===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!1,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.runTime.median&&!1===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!0,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.waitTime.average&&!0===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!1,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.waitTime.median&&!1===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!0,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.elu.average&&!0===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!1,this.taskStatisticsRequirements.elu.median=e.elu.median),this.taskStatisticsRequirements.elu.median&&!1===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!0,this.taskStatisticsRequirements.elu.median=e.elu.median)}setOptions(e){this.opts=e??l,this.setTaskStatisticsRequirements(this.opts)}getWorkerTaskRunTime(e){return this.taskStatisticsRequirements.runTime.median?this.pool.workerNodes[e].workerUsage.runTime.median:this.pool.workerNodes[e].workerUsage.runTime.average}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].workerUsage.waitTime.median:this.pool.workerNodes[e].workerUsage.waitTime.average}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].workerUsage.elu.active.median:this.pool.workerNodes[e].workerUsage.elu.active.average}computeDefaultWorkerWeight(){let e=0;for(const t of i.cpus()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/i.cpus().length)}}class y extends W{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.workersVirtualTaskEndTimestamp=[],!0}update(e){return this.computeWorkerVirtualTaskEndTimestamp(e),!0}choose(){let e=1/0;for(const[t]of this.pool.workerNodes.entries()){null==this.workersVirtualTaskEndTimestamp[t]&&this.computeWorkerVirtualTaskEndTimestamp(t);const s=this.workersVirtualTaskEndTimestamp[t];s<e&&(e=s,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(e){return this.workersVirtualTaskEndTimestamp.splice(e,1),!0}computeWorkerVirtualTaskEndTimestamp(e){this.workersVirtualTaskEndTimestamp[e]=this.getWorkerVirtualTaskEndTimestamp(e,this.getWorkerVirtualTaskStartTimestamp(e))}getWorkerVirtualTaskEndTimestamp(e,t){return t+(this.opts.measurement===f.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class S extends W{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight(),this.roundWeights=this.getRoundWeights()}reset(){return this.nextWorkerNodeId=0,this.roundId=0,!0}update(){return!0}choose(){let e,t;for(let s=this.roundId;s<this.roundWeights.length;s++)for(let r=this.nextWorkerNodeId;r<this.pool.workerNodes.length;r++){if((this.opts.weights?.[r]??this.defaultWorkerWeight)>=this.roundWeights[s]){e=s,t=r;break}}this.roundId=e??0,this.nextWorkerNodeId=t??0;const s=this.nextWorkerNodeId;return this.nextWorkerNodeId===this.pool.workerNodes.length-1?(this.nextWorkerNodeId=0,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1):this.nextWorkerNodeId=this.nextWorkerNodeId+1,s}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1)),!0}setOptions(e){super.setOptions(e),this.roundWeights=this.getRoundWeights()}getRoundWeights(){return null==this.opts.weights?[this.defaultWorkerWeight]:[...new Set(Object.values(this.opts.weights).slice().sort(((e,t)=>e-t)))]}}class x extends W{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage.runTime.aggregate+s.workerUsage.waitTime.aggregate;if(0===r){this.nextWorkerNodeId=t;break}r<e&&(e=r,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class N extends W{constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage.tasks,i=r.executed+r.executing+r.queued;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class v extends W{taskStatisticsRequirements={runTime:{aggregate:!1,average:!1,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage,i=r.elu?.active.aggregate??0;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class E extends W{strategyPolicy={useDynamicWorker:!0};constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.nextWorkerNodeId=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId;return this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1)),!0}}class I extends W{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight()}reset(){return this.nextWorkerNodeId=0,this.workerVirtualTaskRunTime=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId,t=this.workerVirtualTaskRunTime;return t<(this.opts.weights?.[e]??this.defaultWorkerWeight)?this.workerVirtualTaskRunTime=t+this.getWorkerTaskRunTime(e):(this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,this.workerVirtualTaskRunTime=0),e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1),this.workerVirtualTaskRunTime=0),!0}}class R{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=T.ROUND_ROBIN,s=l){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[T.ROUND_ROBIN,new(E.bind(this))(e,s)],[T.LEAST_USED,new(N.bind(this))(e,s)],[T.LEAST_BUSY,new(x.bind(this))(e,s)],[T.LEAST_ELU,new(v.bind(this))(e,s)],[T.FAIR_SHARE,new(y.bind(this))(e,s)],[T.WEIGHTED_ROUND_ROBIN,new(I.bind(this))(e,s)],[T.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(S.bind(this))(e,s)]])}getStrategyPolicy(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).strategyPolicy}getTaskStatisticsRequirements(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).taskStatisticsRequirements}setWorkerChoiceStrategy(e){this.workerChoiceStrategy!==e&&(this.workerChoiceStrategy=e),this.workerChoiceStrategies.get(this.workerChoiceStrategy)?.reset()}update(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).update(e)}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategy).choose();if(null==e)throw new Error("Worker node key chosen is null or undefined");return e}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).remove(e)}setOptions(e){for(const t of this.workerChoiceStrategies.values())t.setOptions(e)}}class b{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;constructor(e,t,s){if(this.numberOfWorkers=e,this.filePath=t,this.opts=s,!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.chooseWorkerNode=this.chooseWorkerNode.bind(this),this.executeTask=this.executeTask.bind(this),this.enqueueTask=this.enqueueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new u),this.workerChoiceStrategyContext=new R(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker()}checkFilePath(e){if(null==e||"string"==typeof e&&0===e.trim().length)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 TypeError("Cannot instantiate a pool with a non safe integer number of workers");if(e<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===n.fixed&&0===e)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){if(!g(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??T.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??l,this.checkValidWorkerChoiceStrategyOptions(this.opts.workerChoiceStrategyOptions),this.opts.restartWorkerOnError=e.restartWorkerOnError??!0,this.opts.enableEvents=e.enableEvents??!0,this.opts.enableTasksQueue=e.enableTasksQueue??!1,this.opts.enableTasksQueue&&(this.checkValidTasksQueueOptions(e.tasksQueueOptions),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e.tasksQueueOptions))}checkValidWorkerChoiceStrategy(e){if(!Object.values(T).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}checkValidWorkerChoiceStrategyOptions(e){if(!g(e))throw new TypeError("Invalid worker choice strategy options: must be a plain object");if(null!=e.weights&&Object.keys(e.weights).length!==this.maxSize)throw new Error("Invalid worker choice strategy options: must have a weight for each worker node");if(null!=e.measurement&&!Object.values(f).includes(e.measurement))throw new Error(`Invalid worker choice strategy options: invalid measurement '${e.measurement}'`)}checkValidTasksQueueOptions(e){if(null!=e&&!g(e))throw new TypeError("Invalid tasks queue options: must be a plain object");if(null!=e?.concurrency&&!Number.isSafeInteger(e.concurrency))throw new TypeError("Invalid worker tasks concurrency: must be an integer");if(null!=e?.concurrency&&e.concurrency<=0)throw new Error(`Invalid worker tasks concurrency '${e.concurrency}'`)}get info(){return{type:this.type,worker:this.worker,minSize:this.minSize,maxSize:this.maxSize,workerNodes:this.workerNodes.length,idleWorkerNodes:this.workerNodes.reduce(((e,t)=>0===t.workerUsage.tasks.executing?e+1:e),0),busyWorkerNodes:this.workerNodes.reduce(((e,t)=>t.workerUsage.tasks.executing>0?e+1:e),0),executedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.executed),0),executingTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.executing),0),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.queued),0),maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.maxQueued),0),failedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.failed),0)}}getWorkerNodeKey(e){return this.workerNodes.findIndex((t=>t.worker===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const[e,t]of this.workerNodes.entries())this.setWorkerNodeTasksUsage(t,this.getWorkerUsage(e)),this.setWorkerStatistics(t.worker)}setWorkerChoiceStrategyOptions(e){this.checkValidWorkerChoiceStrategyOptions(e),this.opts.workerChoiceStrategyOptions=e,this.workerChoiceStrategyContext.setOptions(this.opts.workerChoiceStrategyOptions)}enableTasksQueue(e,t){!0!==this.opts.enableTasksQueue||e||this.flushTasksQueues(),this.opts.enableTasksQueue=e,this.setTasksQueueOptions(t)}setTasksQueueOptions(e){!0===this.opts.enableTasksQueue?(this.checkValidTasksQueueOptions(e),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e)):null!=this.opts.tasksQueueOptions&&delete this.opts.tasksQueueOptions}buildTasksQueueOptions(e){return{concurrency:e?.concurrency??1}}get full(){return this.workerNodes.length>=this.maxSize}internalBusy(){return-1===this.workerNodes.findIndex((e=>0===e.workerUsage.tasks.executing))}async execute(e,t){const i=r.performance.now(),o=this.chooseWorkerNode(),a={name:t,data:e??{},timestamp:i,id:s.randomUUID()},n=new Promise(((e,t)=>{this.promiseResponseMap.set(a.id,{resolve:e,reject:t,worker:this.workerNodes[o].worker})}));return!0===this.opts.enableTasksQueue&&(this.busy||this.workerNodes[o].workerUsage.tasks.executing>=this.opts.tasksQueueOptions.concurrency)?this.enqueueTask(o,a):this.executeTask(o,a),this.checkAndEmitEvents(),n}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{this.flushTasksQueue(t),await this.destroyWorker(e.worker)})))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].workerUsage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t)}afterTaskExecutionHook(e,t){const s=this.workerNodes[this.getWorkerNodeKey(e)].workerUsage;this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,++s.executed,null!=t.taskError&&++s.failed}updateRunTimeWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&(e.runTime.aggregate+=t.taskPerformance?.runTime??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&null!=t.taskPerformance?.runTime&&(e.runTime.history.push(t.taskPerformance.runTime),e.runTime.median=d(e.runTime.history)))}updateWaitTimeWorkerUsage(e,t){const s=r.performance.now(),i=s-(t.timestamp??s);this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&(e.waitTime.aggregate+=i??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&null!=i&&(e.waitTime.history.push(i),e.waitTime.median=d(e.waitTime.history)))}updateEluWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate){if(null!=e.elu&&null!=t.taskPerformance?.elu?(e.elu.idle.aggregate+=t.taskPerformance.elu.idle,e.elu.active.aggregate+=t.taskPerformance.elu.active,e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2):null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=t.taskPerformance.elu.idle,e.elu.active.aggregate=t.taskPerformance.elu.active,e.elu.utilization=t.taskPerformance.elu.utilization),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed){const t=e.tasks.executed-e.tasks.failed;e.elu.idle.average=e.elu.idle.aggregate/t,e.elu.active.average=e.elu.active.aggregate/t}this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&null!=t.taskPerformance?.elu&&(e.elu.idle.history.push(t.taskPerformance.elu.idle),e.elu.active.history.push(t.taskPerformance.elu.active),e.elu.idle.median=d(e.elu.idle.history),e.elu.active.median=d(e.elu.active.history))}}chooseWorkerNode(){if(this.shallCreateDynamicWorker()){const e=this.createAndSetupDynamicWorker();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return this.getWorkerNodeKey(e)}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===n.dynamic&&!this.full&&this.internalBusy()}registerWorkerMessageListener(e,t){e.on("message",t)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,this.workerListener())}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??c),e.on("error",this.opts.errorHandler??c),e.on("error",(e=>{null!=this.emitter&&this.emitter.emit(k.error,e),!0===this.opts.restartWorkerOnError&&this.createAndSetupWorker()})),e.on("online",this.opts.onlineHandler??c),e.on("exit",this.opts.exitHandler??c),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.pushWorkerNode(e),this.setWorkerStatistics(e),this.afterWorkerSetup(e),e}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=m.HARD,(t.kill===r||null!=t.kill&&(!1===this.opts.enableTasksQueue&&0===this.workerNodes[s].workerUsage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[s].workerUsage.tasks.executing&&0===this.tasksQueueSize(s)))&&this.destroyWorker(e)})),e}workerListener(){return e=>{if(null!=e.id){const t=this.promiseResponseMap.get(e.id);if(null!=t){null!=e.taskError?(null!=this.emitter&&this.emitter.emit(k.taskError,e.taskError),t.reject(e.taskError.message)):t.resolve(e.data),this.afterTaskExecutionHook(t.worker,e),this.promiseResponseMap.delete(e.id);const s=this.getWorkerNodeKey(t.worker);!0===this.opts.enableTasksQueue&&this.tasksQueueSize(s)>0&&this.executeTask(s,this.dequeueTask(s)),this.workerChoiceStrategyContext.update(s)}}}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter?.emit(k.busy,this.info),this.type===n.dynamic&&this.full&&this.emitter?.emit(k.full,this.info))}setWorkerNodeTasksUsage(e,t){e.workerUsage=t}pushWorkerNode(e){this.workerNodes.push({worker:e,workerUsage:this.getWorkerUsage(),tasksQueue:new w});const t=this.getWorkerNodeKey(e);return this.setWorkerNodeTasksUsage(this.workerNodes[t],this.getWorkerUsage(t)),this.workerNodes.length}removeWorkerNode(e){const t=this.getWorkerNodeKey(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(this.workerNodes[e].worker,t)}enqueueTask(e,t){return this.workerNodes[e].tasksQueue.enqueue(t)}dequeueTask(e){return this.workerNodes[e].tasksQueue.dequeue()}tasksQueueSize(e){return this.workerNodes[e].tasksQueue.size}tasksMaxQueueSize(e){return this.workerNodes[e].tasksQueue.maxSize}flushTasksQueue(e){if(this.tasksQueueSize(e)>0)for(let t=0;t<this.tasksQueueSize(e);t++)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].tasksQueue.clear()}flushTasksQueues(){for(const[e]of this.workerNodes.entries())this.flushTasksQueue(e)}setWorkerStatistics(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate}})}getWorkerUsage(e){const t=e=>null!=e?this.tasksQueueSize(e):0,s=e=>null!=e?this.tasksMaxQueueSize(e):0;return{tasks:{executed:0,executing:0,get queued(){return t(e)},get maxQueued(){return s(e)},failed:0},runTime:{aggregate:0,average:0,median:0,history:new p},waitTime:{aggregate:0,average:0,median:0,history:new p},elu:{idle:{aggregate:0,average:0,median:0,history:new p},active:{aggregate:0,average:0,median:0,history:new p},utilization:0}}}}class C extends b{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}setupHook(){t.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return t.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.on("disconnect",(()=>{e.kill()})),e.disconnect()}sendToWorker(e,t){e.send(t)}createWorker(){return t.fork(this.opts.env)}get type(){return n.fixed}get worker(){return h.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class O extends b{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return o.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,t){e.postMessage(t)}createWorker(){return new o.Worker(this.filePath,{env:o.SHARE_ENV,...this.opts.workerOptions})}get type(){return n.fixed}get worker(){return h.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}const q="default",z=6e4,U=m.SOFT;class P extends a.AsyncResource{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;aliveInterval;constructor(e,t,s,i,o={killBehavior:U,maxInactiveTime:z}){super(e),this.isMain=t,this.mainWorker=i,this.opts=o,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||(this.lastTaskTimestamp=r.performance.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??z)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",this.messageListener.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??U,this.opts.maxInactiveTime=e.maxInactiveTime??z,delete this.opts.async}checkTaskFunctions(e){if(null==e)throw new Error("taskFunctions parameter is mandatory");if(this.taskFunctions=new Map,"function"==typeof e)this.taskFunctions.set(q,e.bind(this));else{if(!g(e))throw new TypeError("taskFunctions parameter is not a function or a plain object");{let t=!0;for(const[s,r]of Object.entries(e)){if("function"!=typeof r)throw new TypeError("A taskFunctions parameter object value is not a function");this.taskFunctions.set(s,r.bind(this)),t&&(this.taskFunctions.set(q,r.bind(this)),t=!1)}if(t)throw new Error("taskFunctions parameter object is empty")}}}messageListener(e){if(null!=e.id&&null!=e.data){const t=this.getTaskFunction(e.name);"AsyncFunction"===t?.constructor.name?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}else null!=e.statistics?this.statistics=e.statistics:null!=e.kill&&(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}checkAlive(){r.performance.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??z)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}runSync(e,t){try{let s=this.beginTaskPerformance();const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})}finally{!this.isMain&&(this.lastTaskTimestamp=r.performance.now())}}runAsync(e,t){let s=this.beginTaskPerformance();e(t.data).then((e=>(s=this.endTaskPerformance(s),this.sendToMainWorker({data:e,taskPerformance:s,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})})).finally((()=>{!this.isMain&&(this.lastTaskTimestamp=r.performance.now())})).catch(c)}getTaskFunction(e){e=e??q;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(){return this.checkStatistics(),{timestamp:r.performance.now(),...this.statistics.elu&&{elu:r.performance.eventLoopUtilization()}}}endTaskPerformance(e){return this.checkStatistics(),{...e,...this.statistics.runTime&&{runTime:r.performance.now()-e.timestamp},...this.statistics.elu&&{elu:r.performance.eventLoopUtilization(e.elu)}}}checkStatistics(){if(null==this.statistics)throw new Error("Performance statistics computation requirements not set")}}exports.ClusterWorker=class extends P{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,s)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}},exports.DynamicClusterPool=class extends C{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return n.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.DynamicThreadPool=class extends O{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return n.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.FixedClusterPool=C,exports.FixedThreadPool=O,exports.KillBehaviors=m,exports.Measurements=f,exports.PoolEvents=k,exports.PoolTypes=n,exports.ThreadWorker=class extends P{constructor(e,t={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,t)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=T,exports.WorkerTypes=h;
|
|
1
|
+
"use strict";var e=require("node:events"),t=require("node:cluster"),s=require("node:crypto"),r=require("node:perf_hooks"),i=require("node:os"),o=require("node:worker_threads"),a=require("node:async_hooks");const n=Object.freeze({fixed:"fixed",dynamic:"dynamic"}),h=Object.freeze({cluster:"cluster",thread:"thread"});class u extends e{}const k=Object.freeze({full:"full",busy:"busy",error:"error",taskError:"taskError"}),c=Object.freeze((()=>{})),l={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},d={aggregate:!1,average:!1,median:!1},m=e=>{if(Array.isArray(e)&&0===e.length)return 0;if(Array.isArray(e)&&1===e.length)return e[0];const t=e.slice().sort(((e,t)=>e-t));return(t[t.length-1>>1]+t[t.length>>1])/2},g=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),p=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class w extends Array{size;constructor(e=1024,...t){super(),this.checkSize(e),this.size=e,arguments.length>1&&this.push(...t)}push(...e){const t=super.push(...e);return t>this.size&&super.splice(0,t-this.size),this.length}unshift(...e){return super.unshift(...e)>this.size&&super.splice(this.size,e.length),this.length}concat(...e){const t=super.concat(e);return t.size=this.size,t.length>t.size&&t.splice(0,t.length-t.size),t}splice(e,t,...s){let r;return arguments.length>=3&&void 0!==t?(r=super.splice(e,t),this.push(...s)):r=2===arguments.length?super.splice(e,t):super.splice(e),r}resize(e){if(this.checkSize(e),0===e)this.length=0;else if(e<this.size)for(let t=e;t<this.size;t++)super.pop();this.size=e}empty(){return 0===this.length}full(){return this.length===this.size}checkSize(e){if(!Number.isSafeInteger(e))throw new TypeError(`Invalid circular array size: ${e} is not a safe integer`);if(e<0)throw new RangeError(`Invalid circular array size: ${e} < 0`)}}class T{items;offset;size;maxSize;constructor(){this.clear()}enqueue(e){return this.items.push(e),++this.size,this.size>this.maxSize&&(this.maxSize=this.size),this.size}dequeue(){if(this.size<=0)return;const e=this.items[this.offset];return 2*++this.offset>=this.items.length&&(this.items=this.items.slice(this.offset),this.offset=0),--this.size,e}peek(){if(!(this.size<=0))return this.items[this.offset]}clear(){this.items=[],this.offset=0,this.size=0,this.maxSize=0}}const f=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LEAST_USED:"LEAST_USED",LEAST_BUSY:"LEAST_BUSY",LEAST_ELU:"LEAST_ELU",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN",INTERLEAVED_WEIGHTED_ROUND_ROBIN:"INTERLEAVED_WEIGHTED_ROUND_ROBIN"}),W=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class y{pool;opts;nextWorkerNodeId=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:d,waitTime:d,elu:d};constructor(e,t=l){this.pool=e,this.opts=t,this.choose=this.choose.bind(this)}setTaskStatisticsRequirements(e){this.taskStatisticsRequirements.runTime.average&&!0===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!1,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.runTime.median&&!1===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!0,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.waitTime.average&&!0===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!1,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.waitTime.median&&!1===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!0,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.elu.average&&!0===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!1,this.taskStatisticsRequirements.elu.median=e.elu.median),this.taskStatisticsRequirements.elu.median&&!1===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!0,this.taskStatisticsRequirements.elu.median=e.elu.median)}setOptions(e){this.opts=e??l,this.setTaskStatisticsRequirements(this.opts)}getWorkerTaskRunTime(e){return this.taskStatisticsRequirements.runTime.median?this.pool.workerNodes[e].usage.runTime.median:this.pool.workerNodes[e].usage.runTime.average}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].usage.waitTime.median:this.pool.workerNodes[e].usage.waitTime.average}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].usage.elu.active.median:this.pool.workerNodes[e].usage.elu.active.average}computeDefaultWorkerWeight(){let e=0;for(const t of i.cpus()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/i.cpus().length)}}class S extends y{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:d,elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.workersVirtualTaskEndTimestamp=[],!0}update(e){return this.computeWorkerVirtualTaskEndTimestamp(e),!0}choose(){let e=1/0;for(const[t]of this.pool.workerNodes.entries()){null==this.workersVirtualTaskEndTimestamp[t]&&this.computeWorkerVirtualTaskEndTimestamp(t);const s=this.workersVirtualTaskEndTimestamp[t];s<e&&(e=s,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(e){return this.workersVirtualTaskEndTimestamp.splice(e,1),!0}computeWorkerVirtualTaskEndTimestamp(e){this.workersVirtualTaskEndTimestamp[e]=this.getWorkerVirtualTaskEndTimestamp(e,this.getWorkerVirtualTaskStartTimestamp(e))}getWorkerVirtualTaskEndTimestamp(e,t){return t+(this.opts.measurement===W.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class x extends y{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight(),this.roundWeights=this.getRoundWeights()}reset(){return this.nextWorkerNodeId=0,this.roundId=0,!0}update(){return!0}choose(){let e,t;for(let s=this.roundId;s<this.roundWeights.length;s++)for(let r=this.nextWorkerNodeId;r<this.pool.workerNodes.length;r++){if((this.opts.weights?.[r]??this.defaultWorkerWeight)>=this.roundWeights[s]){e=s,t=r;break}}this.roundId=e??0,this.nextWorkerNodeId=t??0;const s=this.nextWorkerNodeId;return this.nextWorkerNodeId===this.pool.workerNodes.length-1?(this.nextWorkerNodeId=0,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1):this.nextWorkerNodeId=this.nextWorkerNodeId+1,s}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1)),!0}setOptions(e){super.setOptions(e),this.roundWeights=this.getRoundWeights()}getRoundWeights(){return null==this.opts.weights?[this.defaultWorkerWeight]:[...new Set(Object.values(this.opts.weights).slice().sort(((e,t)=>e-t)))]}}class N extends y{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:d};constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.usage.runTime.aggregate+s.usage.waitTime.aggregate;if(0===r){this.nextWorkerNodeId=t;break}r<e&&(e=r,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class v extends y{constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.usage.tasks,i=r.executed+r.executing+r.queued;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class E extends y{taskStatisticsRequirements={runTime:d,waitTime:d,elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.usage,i=r.elu?.active.aggregate??0;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class I extends y{strategyPolicy={useDynamicWorker:!0};constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.nextWorkerNodeId=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId;return this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1)),!0}}class R extends y{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:d,elu:d};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight()}reset(){return this.nextWorkerNodeId=0,this.workerVirtualTaskRunTime=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId,t=this.workerVirtualTaskRunTime;return t<(this.opts.weights?.[e]??this.defaultWorkerWeight)?this.workerVirtualTaskRunTime=t+this.getWorkerTaskRunTime(e):(this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,this.workerVirtualTaskRunTime=0),e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1),this.workerVirtualTaskRunTime=0),!0}}class b{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=f.ROUND_ROBIN,s=l){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[f.ROUND_ROBIN,new(I.bind(this))(e,s)],[f.LEAST_USED,new(v.bind(this))(e,s)],[f.LEAST_BUSY,new(N.bind(this))(e,s)],[f.LEAST_ELU,new(E.bind(this))(e,s)],[f.FAIR_SHARE,new(S.bind(this))(e,s)],[f.WEIGHTED_ROUND_ROBIN,new(R.bind(this))(e,s)],[f.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(x.bind(this))(e,s)]])}getStrategyPolicy(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).strategyPolicy}getTaskStatisticsRequirements(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).taskStatisticsRequirements}setWorkerChoiceStrategy(e){this.workerChoiceStrategy!==e&&(this.workerChoiceStrategy=e),this.workerChoiceStrategies.get(this.workerChoiceStrategy)?.reset()}update(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).update(e)}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategy).choose();if(null==e)throw new Error("Worker node key chosen is null or undefined");return e}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).remove(e)}setOptions(e){for(const t of this.workerChoiceStrategies.values())t.setOptions(e)}}class C{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;constructor(e,t,s){if(this.numberOfWorkers=e,this.filePath=t,this.opts=s,!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.chooseWorkerNode=this.chooseWorkerNode.bind(this),this.executeTask=this.executeTask.bind(this),this.enqueueTask=this.enqueueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new u),this.workerChoiceStrategyContext=new b(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker()}checkFilePath(e){if(null==e||"string"==typeof e&&0===e.trim().length)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 TypeError("Cannot instantiate a pool with a non safe integer number of workers");if(e<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===n.fixed&&0===e)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){if(!g(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??f.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??l,this.checkValidWorkerChoiceStrategyOptions(this.opts.workerChoiceStrategyOptions),this.opts.restartWorkerOnError=e.restartWorkerOnError??!0,this.opts.enableEvents=e.enableEvents??!0,this.opts.enableTasksQueue=e.enableTasksQueue??!1,this.opts.enableTasksQueue&&(this.checkValidTasksQueueOptions(e.tasksQueueOptions),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e.tasksQueueOptions))}checkValidWorkerChoiceStrategy(e){if(!Object.values(f).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}checkValidWorkerChoiceStrategyOptions(e){if(!g(e))throw new TypeError("Invalid worker choice strategy options: must be a plain object");if(null!=e.weights&&Object.keys(e.weights).length!==this.maxSize)throw new Error("Invalid worker choice strategy options: must have a weight for each worker node");if(null!=e.measurement&&!Object.values(W).includes(e.measurement))throw new Error(`Invalid worker choice strategy options: invalid measurement '${e.measurement}'`)}checkValidTasksQueueOptions(e){if(null!=e&&!g(e))throw new TypeError("Invalid tasks queue options: must be a plain object");if(null!=e?.concurrency&&!Number.isSafeInteger(e.concurrency))throw new TypeError("Invalid worker tasks concurrency: must be an integer");if(null!=e?.concurrency&&e.concurrency<=0)throw new Error(`Invalid worker tasks concurrency '${e.concurrency}'`)}get info(){return{type:this.type,worker:this.worker,minSize:this.minSize,maxSize:this.maxSize,workerNodes:this.workerNodes.length,idleWorkerNodes:this.workerNodes.reduce(((e,t)=>0===t.usage.tasks.executing?e+1:e),0),busyWorkerNodes:this.workerNodes.reduce(((e,t)=>t.usage.tasks.executing>0?e+1:e),0),executedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.executed),0),executingTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.executing),0),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.queued),0),maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.maxQueued),0),failedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.failed),0)}}getWorkerNodeKey(e){return this.workerNodes.findIndex((t=>t.worker===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const[e,t]of this.workerNodes.entries())this.setWorkerNodeTasksUsage(t,this.getWorkerUsage(e)),this.setWorkerStatistics(t.worker)}setWorkerChoiceStrategyOptions(e){this.checkValidWorkerChoiceStrategyOptions(e),this.opts.workerChoiceStrategyOptions=e,this.workerChoiceStrategyContext.setOptions(this.opts.workerChoiceStrategyOptions)}enableTasksQueue(e,t){!0!==this.opts.enableTasksQueue||e||this.flushTasksQueues(),this.opts.enableTasksQueue=e,this.setTasksQueueOptions(t)}setTasksQueueOptions(e){!0===this.opts.enableTasksQueue?(this.checkValidTasksQueueOptions(e),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e)):null!=this.opts.tasksQueueOptions&&delete this.opts.tasksQueueOptions}buildTasksQueueOptions(e){return{concurrency:e?.concurrency??1}}get full(){return this.workerNodes.length>=this.maxSize}internalBusy(){return-1===this.workerNodes.findIndex((e=>0===e.usage.tasks.executing))}async execute(e,t){const i=r.performance.now(),o=this.chooseWorkerNode(),a={name:t,data:e??{},timestamp:i,id:s.randomUUID()},n=new Promise(((e,t)=>{this.promiseResponseMap.set(a.id,{resolve:e,reject:t,worker:this.workerNodes[o].worker})}));return!0===this.opts.enableTasksQueue&&(this.busy||this.workerNodes[o].usage.tasks.executing>=this.opts.tasksQueueOptions.concurrency)?this.enqueueTask(o,a):this.executeTask(o,a),this.checkAndEmitEvents(),n}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{this.flushTasksQueue(t),await this.destroyWorker(e.worker)})))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t)}afterTaskExecutionHook(e,t){const s=this.workerNodes[this.getWorkerNodeKey(e)].usage;this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,++s.executed,null!=t.taskError&&++s.failed}updateRunTimeWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&(e.runTime.aggregate+=t.taskPerformance?.runTime??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&null!=t.taskPerformance?.runTime&&(e.runTime.history.push(t.taskPerformance.runTime),e.runTime.median=m(e.runTime.history)))}updateWaitTimeWorkerUsage(e,t){const s=r.performance.now(),i=s-(t.timestamp??s);this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&(e.waitTime.aggregate+=i??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&null!=i&&(e.waitTime.history.push(i),e.waitTime.median=m(e.waitTime.history)))}updateEluWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate){if(null!=e.elu&&null!=t.taskPerformance?.elu?(e.elu.idle.aggregate+=t.taskPerformance.elu.idle,e.elu.active.aggregate+=t.taskPerformance.elu.active,e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2):null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=t.taskPerformance.elu.idle,e.elu.active.aggregate=t.taskPerformance.elu.active,e.elu.utilization=t.taskPerformance.elu.utilization),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed){const t=e.tasks.executed-e.tasks.failed;e.elu.idle.average=e.elu.idle.aggregate/t,e.elu.active.average=e.elu.active.aggregate/t}this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&null!=t.taskPerformance?.elu&&(e.elu.idle.history.push(t.taskPerformance.elu.idle),e.elu.active.history.push(t.taskPerformance.elu.active),e.elu.idle.median=m(e.elu.idle.history),e.elu.active.median=m(e.elu.active.history))}}chooseWorkerNode(){if(this.shallCreateDynamicWorker()){const e=this.createAndSetupDynamicWorker();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return this.getWorkerNodeKey(e)}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===n.dynamic&&!this.full&&this.internalBusy()}registerWorkerMessageListener(e,t){e.on("message",t)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,this.workerListener())}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??c),e.on("error",this.opts.errorHandler??c),e.on("error",(e=>{null!=this.emitter&&this.emitter.emit(k.error,e),!0===this.opts.restartWorkerOnError&&this.createAndSetupWorker()})),e.on("online",this.opts.onlineHandler??c),e.on("exit",this.opts.exitHandler??c),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.pushWorkerNode(e),this.setWorkerStatistics(e),this.afterWorkerSetup(e),e}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=p.HARD,(t.kill===r||null!=t.kill&&(!1===this.opts.enableTasksQueue&&0===this.workerNodes[s].usage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[s].usage.tasks.executing&&0===this.tasksQueueSize(s)))&&this.destroyWorker(e)})),e}workerListener(){return e=>{if(null!=e.id){const t=this.promiseResponseMap.get(e.id);if(null!=t){null!=e.taskError?(null!=this.emitter&&this.emitter.emit(k.taskError,e.taskError),t.reject(e.taskError.message)):t.resolve(e.data),this.afterTaskExecutionHook(t.worker,e),this.promiseResponseMap.delete(e.id);const s=this.getWorkerNodeKey(t.worker);!0===this.opts.enableTasksQueue&&this.tasksQueueSize(s)>0&&this.executeTask(s,this.dequeueTask(s)),this.workerChoiceStrategyContext.update(s)}}}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter?.emit(k.busy,this.info),this.type===n.dynamic&&this.full&&this.emitter?.emit(k.full,this.info))}setWorkerNodeTasksUsage(e,t){e.usage=t}pushWorkerNode(e){this.workerNodes.push({worker:e,usage:this.getWorkerUsage(),tasksQueue:new T});const t=this.getWorkerNodeKey(e);return this.setWorkerNodeTasksUsage(this.workerNodes[t],this.getWorkerUsage(t)),this.workerNodes.length}removeWorkerNode(e){const t=this.getWorkerNodeKey(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(this.workerNodes[e].worker,t)}enqueueTask(e,t){return this.workerNodes[e].tasksQueue.enqueue(t)}dequeueTask(e){return this.workerNodes[e].tasksQueue.dequeue()}tasksQueueSize(e){return this.workerNodes[e].tasksQueue.size}tasksMaxQueueSize(e){return this.workerNodes[e].tasksQueue.maxSize}flushTasksQueue(e){if(this.tasksQueueSize(e)>0)for(let t=0;t<this.tasksQueueSize(e);t++)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].tasksQueue.clear()}flushTasksQueues(){for(const[e]of this.workerNodes.entries())this.flushTasksQueue(e)}setWorkerStatistics(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate}})}getWorkerUsage(e){const t=e=>null!=e?this.tasksQueueSize(e):0,s=e=>null!=e?this.tasksMaxQueueSize(e):0;return{tasks:{executed:0,executing:0,get queued(){return t(e)},get maxQueued(){return s(e)},failed:0},runTime:{aggregate:0,average:0,median:0,history:new w},waitTime:{aggregate:0,average:0,median:0,history:new w},elu:{idle:{aggregate:0,average:0,median:0,history:new w},active:{aggregate:0,average:0,median:0,history:new w},utilization:0}}}}class O extends C{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}setupHook(){t.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return t.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.on("disconnect",(()=>{e.kill()})),e.disconnect()}sendToWorker(e,t){e.send(t)}createWorker(){return t.fork(this.opts.env)}get type(){return n.fixed}get worker(){return h.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class q extends C{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return o.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,t){e.postMessage(t)}createWorker(){return new o.Worker(this.filePath,{env:o.SHARE_ENV,...this.opts.workerOptions})}get type(){return n.fixed}get worker(){return h.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}const z="default",P=6e4,A=p.SOFT;class Q extends a.AsyncResource{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;aliveInterval;constructor(e,t,s,i,o={killBehavior:A,maxInactiveTime:P}){super(e),this.isMain=t,this.mainWorker=i,this.opts=o,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||(this.lastTaskTimestamp=r.performance.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??P)/2),this.checkAlive.bind(this)(),this.mainWorker?.on("message",this.messageListener.bind(this)))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??A,this.opts.maxInactiveTime=e.maxInactiveTime??P,delete this.opts.async}checkTaskFunctions(e){if(null==e)throw new Error("taskFunctions parameter is mandatory");if(this.taskFunctions=new Map,"function"==typeof e)this.taskFunctions.set(z,e.bind(this));else{if(!g(e))throw new TypeError("taskFunctions parameter is not a function or a plain object");{let t=!0;for(const[s,r]of Object.entries(e)){if("function"!=typeof r)throw new TypeError("A taskFunctions parameter object value is not a function");this.taskFunctions.set(s,r.bind(this)),t&&(this.taskFunctions.set(z,r.bind(this)),t=!1)}if(t)throw new Error("taskFunctions parameter object is empty")}}}messageListener(e){if(null!=e.id&&null!=e.data){const t=this.getTaskFunction(e.name);"AsyncFunction"===t?.constructor.name?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}else null!=e.statistics?this.statistics=e.statistics:null!=e.kill&&(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}checkAlive(){r.performance.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??P)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}runSync(e,t){try{let s=this.beginTaskPerformance();const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})}finally{!this.isMain&&(this.lastTaskTimestamp=r.performance.now())}}runAsync(e,t){let s=this.beginTaskPerformance();e(t.data).then((e=>(s=this.endTaskPerformance(s),this.sendToMainWorker({data:e,taskPerformance:s,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})})).finally((()=>{!this.isMain&&(this.lastTaskTimestamp=r.performance.now())})).catch(c)}getTaskFunction(e){e=e??z;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(){return this.checkStatistics(),{timestamp:r.performance.now(),...this.statistics.elu&&{elu:r.performance.eventLoopUtilization()}}}endTaskPerformance(e){return this.checkStatistics(),{...e,...this.statistics.runTime&&{runTime:r.performance.now()-e.timestamp},...this.statistics.elu&&{elu:r.performance.eventLoopUtilization(e.elu)}}}checkStatistics(){if(null==this.statistics)throw new Error("Performance statistics computation requirements not set")}}exports.ClusterWorker=class extends Q{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,s)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}},exports.DynamicClusterPool=class extends O{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return n.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.DynamicThreadPool=class extends q{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return n.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.FixedClusterPool=O,exports.FixedThreadPool=q,exports.KillBehaviors=p,exports.Measurements=W,exports.PoolEvents=k,exports.PoolTypes=n,exports.ThreadWorker=class extends Q{constructor(e,t={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,t)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=f,exports.WorkerTypes=h,exports.availableParallelism=()=>{let e=1;try{e=i.availableParallelism()}catch{const t=i.cpus();Array.isArray(t)&&t.length>0&&(e=t.length)}return e};
|
package/lib/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"node:events";import t from"node:cluster";import s from"node:crypto";import{performance as r}from"node:perf_hooks";import{cpus as i}from"node:os";import{isMainThread as o,Worker as a,SHARE_ENV as n,parentPort as h}from"node:worker_threads";import{AsyncResource as u}from"node:async_hooks";const k=Object.freeze({fixed:"fixed",dynamic:"dynamic"}),c=Object.freeze({cluster:"cluster",thread:"thread"});class d extends e{}const l=Object.freeze({full:"full",busy:"busy",error:"error",taskError:"taskError"}),g=Object.freeze((()=>{})),m={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},p=e=>{if(Array.isArray(e)&&0===e.length)return 0;if(Array.isArray(e)&&1===e.length)return e[0];const t=e.slice().sort(((e,t)=>e-t));return(t[t.length-1>>1]+t[t.length>>1])/2},w=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),T=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class f extends Array{size;constructor(e=1024,...t){super(),this.checkSize(e),this.size=e,arguments.length>1&&this.push(...t)}push(...e){const t=super.push(...e);return t>this.size&&super.splice(0,t-this.size),this.length}unshift(...e){return super.unshift(...e)>this.size&&super.splice(this.size,e.length),this.length}concat(...e){const t=super.concat(e);return t.size=this.size,t.length>t.size&&t.splice(0,t.length-t.size),t}splice(e,t,...s){let r;return arguments.length>=3&&void 0!==t?(r=super.splice(e,t),this.push(...s)):r=2===arguments.length?super.splice(e,t):super.splice(e),r}resize(e){if(this.checkSize(e),0===e)this.length=0;else if(e<this.size)for(let t=e;t<this.size;t++)super.pop();this.size=e}empty(){return 0===this.length}full(){return this.length===this.size}checkSize(e){if(!Number.isSafeInteger(e))throw new TypeError(`Invalid circular array size: ${e} is not a safe integer`);if(e<0)throw new RangeError(`Invalid circular array size: ${e} < 0`)}}class W{items;offset;size;maxSize;constructor(){this.clear()}enqueue(e){return this.items.push(e),++this.size,this.size>this.maxSize&&(this.maxSize=this.size),this.size}dequeue(){if(this.size<=0)return;const e=this.items[this.offset];return 2*++this.offset>=this.items.length&&(this.items=this.items.slice(this.offset),this.offset=0),--this.size,e}peek(){if(!(this.size<=0))return this.items[this.offset]}clear(){this.items=[],this.offset=0,this.size=0,this.maxSize=0}}const y=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LEAST_USED:"LEAST_USED",LEAST_BUSY:"LEAST_BUSY",LEAST_ELU:"LEAST_ELU",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN",INTERLEAVED_WEIGHTED_ROUND_ROBIN:"INTERLEAVED_WEIGHTED_ROUND_ROBIN"}),S=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class x{pool;opts;nextWorkerNodeId=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:{aggregate:!1,average:!1,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};constructor(e,t=m){this.pool=e,this.opts=t,this.choose=this.choose.bind(this)}setTaskStatisticsRequirements(e){this.taskStatisticsRequirements.runTime.average&&!0===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!1,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.runTime.median&&!1===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!0,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.waitTime.average&&!0===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!1,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.waitTime.median&&!1===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!0,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.elu.average&&!0===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!1,this.taskStatisticsRequirements.elu.median=e.elu.median),this.taskStatisticsRequirements.elu.median&&!1===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!0,this.taskStatisticsRequirements.elu.median=e.elu.median)}setOptions(e){this.opts=e??m,this.setTaskStatisticsRequirements(this.opts)}getWorkerTaskRunTime(e){return this.taskStatisticsRequirements.runTime.median?this.pool.workerNodes[e].workerUsage.runTime.median:this.pool.workerNodes[e].workerUsage.runTime.average}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].workerUsage.waitTime.median:this.pool.workerNodes[e].workerUsage.waitTime.average}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].workerUsage.elu.active.median:this.pool.workerNodes[e].workerUsage.elu.active.average}computeDefaultWorkerWeight(){let e=0;for(const t of i()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/i().length)}}class N extends x{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.workersVirtualTaskEndTimestamp=[],!0}update(e){return this.computeWorkerVirtualTaskEndTimestamp(e),!0}choose(){let e=1/0;for(const[t]of this.pool.workerNodes.entries()){null==this.workersVirtualTaskEndTimestamp[t]&&this.computeWorkerVirtualTaskEndTimestamp(t);const s=this.workersVirtualTaskEndTimestamp[t];s<e&&(e=s,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(e){return this.workersVirtualTaskEndTimestamp.splice(e,1),!0}computeWorkerVirtualTaskEndTimestamp(e){this.workersVirtualTaskEndTimestamp[e]=this.getWorkerVirtualTaskEndTimestamp(e,this.getWorkerVirtualTaskStartTimestamp(e))}getWorkerVirtualTaskEndTimestamp(e,t){return t+(this.opts.measurement===S.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class v extends x{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight(),this.roundWeights=this.getRoundWeights()}reset(){return this.nextWorkerNodeId=0,this.roundId=0,!0}update(){return!0}choose(){let e,t;for(let s=this.roundId;s<this.roundWeights.length;s++)for(let r=this.nextWorkerNodeId;r<this.pool.workerNodes.length;r++){if((this.opts.weights?.[r]??this.defaultWorkerWeight)>=this.roundWeights[s]){e=s,t=r;break}}this.roundId=e??0,this.nextWorkerNodeId=t??0;const s=this.nextWorkerNodeId;return this.nextWorkerNodeId===this.pool.workerNodes.length-1?(this.nextWorkerNodeId=0,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1):this.nextWorkerNodeId=this.nextWorkerNodeId+1,s}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1)),!0}setOptions(e){super.setOptions(e),this.roundWeights=this.getRoundWeights()}getRoundWeights(){return null==this.opts.weights?[this.defaultWorkerWeight]:[...new Set(Object.values(this.opts.weights).slice().sort(((e,t)=>e-t)))]}}class E extends x{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage.runTime.aggregate+s.workerUsage.waitTime.aggregate;if(0===r){this.nextWorkerNodeId=t;break}r<e&&(e=r,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class I extends x{constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage.tasks,i=r.executed+r.executing+r.queued;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class R extends x{taskStatisticsRequirements={runTime:{aggregate:!1,average:!1,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage,i=r.elu?.active.aggregate??0;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class b extends x{strategyPolicy={useDynamicWorker:!0};constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.nextWorkerNodeId=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId;return this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1)),!0}}class O extends x{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight()}reset(){return this.nextWorkerNodeId=0,this.workerVirtualTaskRunTime=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId,t=this.workerVirtualTaskRunTime;return t<(this.opts.weights?.[e]??this.defaultWorkerWeight)?this.workerVirtualTaskRunTime=t+this.getWorkerTaskRunTime(e):(this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,this.workerVirtualTaskRunTime=0),e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1),this.workerVirtualTaskRunTime=0),!0}}class C{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=y.ROUND_ROBIN,s=m){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[y.ROUND_ROBIN,new(b.bind(this))(e,s)],[y.LEAST_USED,new(I.bind(this))(e,s)],[y.LEAST_BUSY,new(E.bind(this))(e,s)],[y.LEAST_ELU,new(R.bind(this))(e,s)],[y.FAIR_SHARE,new(N.bind(this))(e,s)],[y.WEIGHTED_ROUND_ROBIN,new(O.bind(this))(e,s)],[y.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(v.bind(this))(e,s)]])}getStrategyPolicy(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).strategyPolicy}getTaskStatisticsRequirements(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).taskStatisticsRequirements}setWorkerChoiceStrategy(e){this.workerChoiceStrategy!==e&&(this.workerChoiceStrategy=e),this.workerChoiceStrategies.get(this.workerChoiceStrategy)?.reset()}update(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).update(e)}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategy).choose();if(null==e)throw new Error("Worker node key chosen is null or undefined");return e}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).remove(e)}setOptions(e){for(const t of this.workerChoiceStrategies.values())t.setOptions(e)}}class z{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;constructor(e,t,s){if(this.numberOfWorkers=e,this.filePath=t,this.opts=s,!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.chooseWorkerNode=this.chooseWorkerNode.bind(this),this.executeTask=this.executeTask.bind(this),this.enqueueTask=this.enqueueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new d),this.workerChoiceStrategyContext=new C(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker()}checkFilePath(e){if(null==e||"string"==typeof e&&0===e.trim().length)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 TypeError("Cannot instantiate a pool with a non safe integer number of workers");if(e<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===k.fixed&&0===e)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){if(!w(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??y.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??m,this.checkValidWorkerChoiceStrategyOptions(this.opts.workerChoiceStrategyOptions),this.opts.restartWorkerOnError=e.restartWorkerOnError??!0,this.opts.enableEvents=e.enableEvents??!0,this.opts.enableTasksQueue=e.enableTasksQueue??!1,this.opts.enableTasksQueue&&(this.checkValidTasksQueueOptions(e.tasksQueueOptions),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e.tasksQueueOptions))}checkValidWorkerChoiceStrategy(e){if(!Object.values(y).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}checkValidWorkerChoiceStrategyOptions(e){if(!w(e))throw new TypeError("Invalid worker choice strategy options: must be a plain object");if(null!=e.weights&&Object.keys(e.weights).length!==this.maxSize)throw new Error("Invalid worker choice strategy options: must have a weight for each worker node");if(null!=e.measurement&&!Object.values(S).includes(e.measurement))throw new Error(`Invalid worker choice strategy options: invalid measurement '${e.measurement}'`)}checkValidTasksQueueOptions(e){if(null!=e&&!w(e))throw new TypeError("Invalid tasks queue options: must be a plain object");if(null!=e?.concurrency&&!Number.isSafeInteger(e.concurrency))throw new TypeError("Invalid worker tasks concurrency: must be an integer");if(null!=e?.concurrency&&e.concurrency<=0)throw new Error(`Invalid worker tasks concurrency '${e.concurrency}'`)}get info(){return{type:this.type,worker:this.worker,minSize:this.minSize,maxSize:this.maxSize,workerNodes:this.workerNodes.length,idleWorkerNodes:this.workerNodes.reduce(((e,t)=>0===t.workerUsage.tasks.executing?e+1:e),0),busyWorkerNodes:this.workerNodes.reduce(((e,t)=>t.workerUsage.tasks.executing>0?e+1:e),0),executedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.executed),0),executingTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.executing),0),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.queued),0),maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.maxQueued),0),failedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.failed),0)}}getWorkerNodeKey(e){return this.workerNodes.findIndex((t=>t.worker===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const[e,t]of this.workerNodes.entries())this.setWorkerNodeTasksUsage(t,this.getWorkerUsage(e)),this.setWorkerStatistics(t.worker)}setWorkerChoiceStrategyOptions(e){this.checkValidWorkerChoiceStrategyOptions(e),this.opts.workerChoiceStrategyOptions=e,this.workerChoiceStrategyContext.setOptions(this.opts.workerChoiceStrategyOptions)}enableTasksQueue(e,t){!0!==this.opts.enableTasksQueue||e||this.flushTasksQueues(),this.opts.enableTasksQueue=e,this.setTasksQueueOptions(t)}setTasksQueueOptions(e){!0===this.opts.enableTasksQueue?(this.checkValidTasksQueueOptions(e),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e)):null!=this.opts.tasksQueueOptions&&delete this.opts.tasksQueueOptions}buildTasksQueueOptions(e){return{concurrency:e?.concurrency??1}}get full(){return this.workerNodes.length>=this.maxSize}internalBusy(){return-1===this.workerNodes.findIndex((e=>0===e.workerUsage.tasks.executing))}async execute(e,t){const i=r.now(),o=this.chooseWorkerNode(),a={name:t,data:e??{},timestamp:i,id:s.randomUUID()},n=new Promise(((e,t)=>{this.promiseResponseMap.set(a.id,{resolve:e,reject:t,worker:this.workerNodes[o].worker})}));return!0===this.opts.enableTasksQueue&&(this.busy||this.workerNodes[o].workerUsage.tasks.executing>=this.opts.tasksQueueOptions.concurrency)?this.enqueueTask(o,a):this.executeTask(o,a),this.checkAndEmitEvents(),n}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{this.flushTasksQueue(t),await this.destroyWorker(e.worker)})))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].workerUsage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t)}afterTaskExecutionHook(e,t){const s=this.workerNodes[this.getWorkerNodeKey(e)].workerUsage;this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,++s.executed,null!=t.taskError&&++s.failed}updateRunTimeWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&(e.runTime.aggregate+=t.taskPerformance?.runTime??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&null!=t.taskPerformance?.runTime&&(e.runTime.history.push(t.taskPerformance.runTime),e.runTime.median=p(e.runTime.history)))}updateWaitTimeWorkerUsage(e,t){const s=r.now(),i=s-(t.timestamp??s);this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&(e.waitTime.aggregate+=i??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&null!=i&&(e.waitTime.history.push(i),e.waitTime.median=p(e.waitTime.history)))}updateEluWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate){if(null!=e.elu&&null!=t.taskPerformance?.elu?(e.elu.idle.aggregate+=t.taskPerformance.elu.idle,e.elu.active.aggregate+=t.taskPerformance.elu.active,e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2):null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=t.taskPerformance.elu.idle,e.elu.active.aggregate=t.taskPerformance.elu.active,e.elu.utilization=t.taskPerformance.elu.utilization),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed){const t=e.tasks.executed-e.tasks.failed;e.elu.idle.average=e.elu.idle.aggregate/t,e.elu.active.average=e.elu.active.aggregate/t}this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&null!=t.taskPerformance?.elu&&(e.elu.idle.history.push(t.taskPerformance.elu.idle),e.elu.active.history.push(t.taskPerformance.elu.active),e.elu.idle.median=p(e.elu.idle.history),e.elu.active.median=p(e.elu.active.history))}}chooseWorkerNode(){if(this.shallCreateDynamicWorker()){const e=this.createAndSetupDynamicWorker();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return this.getWorkerNodeKey(e)}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===k.dynamic&&!this.full&&this.internalBusy()}registerWorkerMessageListener(e,t){e.on("message",t)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,this.workerListener())}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??g),e.on("error",this.opts.errorHandler??g),e.on("error",(e=>{null!=this.emitter&&this.emitter.emit(l.error,e),!0===this.opts.restartWorkerOnError&&this.createAndSetupWorker()})),e.on("online",this.opts.onlineHandler??g),e.on("exit",this.opts.exitHandler??g),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.pushWorkerNode(e),this.setWorkerStatistics(e),this.afterWorkerSetup(e),e}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=T.HARD,(t.kill===r||null!=t.kill&&(!1===this.opts.enableTasksQueue&&0===this.workerNodes[s].workerUsage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[s].workerUsage.tasks.executing&&0===this.tasksQueueSize(s)))&&this.destroyWorker(e)})),e}workerListener(){return e=>{if(null!=e.id){const t=this.promiseResponseMap.get(e.id);if(null!=t){null!=e.taskError?(null!=this.emitter&&this.emitter.emit(l.taskError,e.taskError),t.reject(e.taskError.message)):t.resolve(e.data),this.afterTaskExecutionHook(t.worker,e),this.promiseResponseMap.delete(e.id);const s=this.getWorkerNodeKey(t.worker);!0===this.opts.enableTasksQueue&&this.tasksQueueSize(s)>0&&this.executeTask(s,this.dequeueTask(s)),this.workerChoiceStrategyContext.update(s)}}}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter?.emit(l.busy,this.info),this.type===k.dynamic&&this.full&&this.emitter?.emit(l.full,this.info))}setWorkerNodeTasksUsage(e,t){e.workerUsage=t}pushWorkerNode(e){this.workerNodes.push({worker:e,workerUsage:this.getWorkerUsage(),tasksQueue:new W});const t=this.getWorkerNodeKey(e);return this.setWorkerNodeTasksUsage(this.workerNodes[t],this.getWorkerUsage(t)),this.workerNodes.length}removeWorkerNode(e){const t=this.getWorkerNodeKey(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(this.workerNodes[e].worker,t)}enqueueTask(e,t){return this.workerNodes[e].tasksQueue.enqueue(t)}dequeueTask(e){return this.workerNodes[e].tasksQueue.dequeue()}tasksQueueSize(e){return this.workerNodes[e].tasksQueue.size}tasksMaxQueueSize(e){return this.workerNodes[e].tasksQueue.maxSize}flushTasksQueue(e){if(this.tasksQueueSize(e)>0)for(let t=0;t<this.tasksQueueSize(e);t++)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].tasksQueue.clear()}flushTasksQueues(){for(const[e]of this.workerNodes.entries())this.flushTasksQueue(e)}setWorkerStatistics(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate}})}getWorkerUsage(e){const t=e=>null!=e?this.tasksQueueSize(e):0,s=e=>null!=e?this.tasksMaxQueueSize(e):0;return{tasks:{executed:0,executing:0,get queued(){return t(e)},get maxQueued(){return s(e)},failed:0},runTime:{aggregate:0,average:0,median:0,history:new f},waitTime:{aggregate:0,average:0,median:0,history:new f},elu:{idle:{aggregate:0,average:0,median:0,history:new f},active:{aggregate:0,average:0,median:0,history:new f},utilization:0}}}}class q extends z{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}setupHook(){t.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return t.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.on("disconnect",(()=>{e.kill()})),e.disconnect()}sendToWorker(e,t){e.send(t)}createWorker(){return t.fork(this.opts.env)}get type(){return k.fixed}get worker(){return c.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class U extends q{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return k.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}class Q extends z{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return o}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,t){e.postMessage(t)}createWorker(){return new a(this.filePath,{env:n,...this.opts.workerOptions})}get type(){return k.fixed}get worker(){return c.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class A extends Q{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return k.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}const P="default",D=6e4,M=T.SOFT;class _ extends u{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;aliveInterval;constructor(e,t,s,i,o={killBehavior:M,maxInactiveTime:D}){super(e),this.isMain=t,this.mainWorker=i,this.opts=o,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||(this.lastTaskTimestamp=r.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??D)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",this.messageListener.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??M,this.opts.maxInactiveTime=e.maxInactiveTime??D,delete this.opts.async}checkTaskFunctions(e){if(null==e)throw new Error("taskFunctions parameter is mandatory");if(this.taskFunctions=new Map,"function"==typeof e)this.taskFunctions.set(P,e.bind(this));else{if(!w(e))throw new TypeError("taskFunctions parameter is not a function or a plain object");{let t=!0;for(const[s,r]of Object.entries(e)){if("function"!=typeof r)throw new TypeError("A taskFunctions parameter object value is not a function");this.taskFunctions.set(s,r.bind(this)),t&&(this.taskFunctions.set(P,r.bind(this)),t=!1)}if(t)throw new Error("taskFunctions parameter object is empty")}}}messageListener(e){if(null!=e.id&&null!=e.data){const t=this.getTaskFunction(e.name);"AsyncFunction"===t?.constructor.name?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}else null!=e.statistics?this.statistics=e.statistics:null!=e.kill&&(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}checkAlive(){r.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??D)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}runSync(e,t){try{let s=this.beginTaskPerformance();const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})}finally{!this.isMain&&(this.lastTaskTimestamp=r.now())}}runAsync(e,t){let s=this.beginTaskPerformance();e(t.data).then((e=>(s=this.endTaskPerformance(s),this.sendToMainWorker({data:e,taskPerformance:s,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})})).finally((()=>{!this.isMain&&(this.lastTaskTimestamp=r.now())})).catch(g)}getTaskFunction(e){e=e??P;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(){return this.checkStatistics(),{timestamp:r.now(),...this.statistics.elu&&{elu:r.eventLoopUtilization()}}}endTaskPerformance(e){return this.checkStatistics(),{...e,...this.statistics.runTime&&{runTime:r.now()-e.timestamp},...this.statistics.elu&&{elu:r.eventLoopUtilization(e.elu)}}}checkStatistics(){if(null==this.statistics)throw new Error("Performance statistics computation requirements not set")}}class V extends _{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,s)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}}class j extends _{constructor(e,t={}){super("worker-thread-pool:poolifier",o,e,h,t)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}}export{V as ClusterWorker,U as DynamicClusterPool,A as DynamicThreadPool,q as FixedClusterPool,Q as FixedThreadPool,T as KillBehaviors,S as Measurements,l as PoolEvents,k as PoolTypes,j as ThreadWorker,y as WorkerChoiceStrategies,c as WorkerTypes};
|
|
1
|
+
import e from"node:events";import t from"node:cluster";import s from"node:crypto";import{performance as r}from"node:perf_hooks";import i,{cpus as o}from"node:os";import{isMainThread as a,Worker as n,SHARE_ENV as h,parentPort as u}from"node:worker_threads";import{AsyncResource as k}from"node:async_hooks";const c=Object.freeze({fixed:"fixed",dynamic:"dynamic"}),l=Object.freeze({cluster:"cluster",thread:"thread"});class d extends e{}const m=Object.freeze({full:"full",busy:"busy",error:"error",taskError:"taskError"}),g=Object.freeze((()=>{})),p={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},w={aggregate:!1,average:!1,median:!1},T=()=>{let e=1;try{e=i.availableParallelism()}catch{const t=i.cpus();Array.isArray(t)&&t.length>0&&(e=t.length)}return e},f=e=>{if(Array.isArray(e)&&0===e.length)return 0;if(Array.isArray(e)&&1===e.length)return e[0];const t=e.slice().sort(((e,t)=>e-t));return(t[t.length-1>>1]+t[t.length>>1])/2},W=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),y=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class S extends Array{size;constructor(e=1024,...t){super(),this.checkSize(e),this.size=e,arguments.length>1&&this.push(...t)}push(...e){const t=super.push(...e);return t>this.size&&super.splice(0,t-this.size),this.length}unshift(...e){return super.unshift(...e)>this.size&&super.splice(this.size,e.length),this.length}concat(...e){const t=super.concat(e);return t.size=this.size,t.length>t.size&&t.splice(0,t.length-t.size),t}splice(e,t,...s){let r;return arguments.length>=3&&void 0!==t?(r=super.splice(e,t),this.push(...s)):r=2===arguments.length?super.splice(e,t):super.splice(e),r}resize(e){if(this.checkSize(e),0===e)this.length=0;else if(e<this.size)for(let t=e;t<this.size;t++)super.pop();this.size=e}empty(){return 0===this.length}full(){return this.length===this.size}checkSize(e){if(!Number.isSafeInteger(e))throw new TypeError(`Invalid circular array size: ${e} is not a safe integer`);if(e<0)throw new RangeError(`Invalid circular array size: ${e} < 0`)}}class x{items;offset;size;maxSize;constructor(){this.clear()}enqueue(e){return this.items.push(e),++this.size,this.size>this.maxSize&&(this.maxSize=this.size),this.size}dequeue(){if(this.size<=0)return;const e=this.items[this.offset];return 2*++this.offset>=this.items.length&&(this.items=this.items.slice(this.offset),this.offset=0),--this.size,e}peek(){if(!(this.size<=0))return this.items[this.offset]}clear(){this.items=[],this.offset=0,this.size=0,this.maxSize=0}}const N=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LEAST_USED:"LEAST_USED",LEAST_BUSY:"LEAST_BUSY",LEAST_ELU:"LEAST_ELU",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN",INTERLEAVED_WEIGHTED_ROUND_ROBIN:"INTERLEAVED_WEIGHTED_ROUND_ROBIN"}),v=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class E{pool;opts;nextWorkerNodeId=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:w,waitTime:w,elu:w};constructor(e,t=p){this.pool=e,this.opts=t,this.choose=this.choose.bind(this)}setTaskStatisticsRequirements(e){this.taskStatisticsRequirements.runTime.average&&!0===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!1,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.runTime.median&&!1===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!0,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.waitTime.average&&!0===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!1,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.waitTime.median&&!1===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!0,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.elu.average&&!0===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!1,this.taskStatisticsRequirements.elu.median=e.elu.median),this.taskStatisticsRequirements.elu.median&&!1===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!0,this.taskStatisticsRequirements.elu.median=e.elu.median)}setOptions(e){this.opts=e??p,this.setTaskStatisticsRequirements(this.opts)}getWorkerTaskRunTime(e){return this.taskStatisticsRequirements.runTime.median?this.pool.workerNodes[e].usage.runTime.median:this.pool.workerNodes[e].usage.runTime.average}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].usage.waitTime.median:this.pool.workerNodes[e].usage.waitTime.average}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].usage.elu.active.median:this.pool.workerNodes[e].usage.elu.active.average}computeDefaultWorkerWeight(){let e=0;for(const t of o()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/o().length)}}class I extends E{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:w,elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=p){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.workersVirtualTaskEndTimestamp=[],!0}update(e){return this.computeWorkerVirtualTaskEndTimestamp(e),!0}choose(){let e=1/0;for(const[t]of this.pool.workerNodes.entries()){null==this.workersVirtualTaskEndTimestamp[t]&&this.computeWorkerVirtualTaskEndTimestamp(t);const s=this.workersVirtualTaskEndTimestamp[t];s<e&&(e=s,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(e){return this.workersVirtualTaskEndTimestamp.splice(e,1),!0}computeWorkerVirtualTaskEndTimestamp(e){this.workersVirtualTaskEndTimestamp[e]=this.getWorkerVirtualTaskEndTimestamp(e,this.getWorkerVirtualTaskStartTimestamp(e))}getWorkerVirtualTaskEndTimestamp(e,t){return t+(this.opts.measurement===v.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class R extends E{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=p){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight(),this.roundWeights=this.getRoundWeights()}reset(){return this.nextWorkerNodeId=0,this.roundId=0,!0}update(){return!0}choose(){let e,t;for(let s=this.roundId;s<this.roundWeights.length;s++)for(let r=this.nextWorkerNodeId;r<this.pool.workerNodes.length;r++){if((this.opts.weights?.[r]??this.defaultWorkerWeight)>=this.roundWeights[s]){e=s,t=r;break}}this.roundId=e??0,this.nextWorkerNodeId=t??0;const s=this.nextWorkerNodeId;return this.nextWorkerNodeId===this.pool.workerNodes.length-1?(this.nextWorkerNodeId=0,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1):this.nextWorkerNodeId=this.nextWorkerNodeId+1,s}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1)),!0}setOptions(e){super.setOptions(e),this.roundWeights=this.getRoundWeights()}getRoundWeights(){return null==this.opts.weights?[this.defaultWorkerWeight]:[...new Set(Object.values(this.opts.weights).slice().sort(((e,t)=>e-t)))]}}class b extends E{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:w};constructor(e,t=p){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.usage.runTime.aggregate+s.usage.waitTime.aggregate;if(0===r){this.nextWorkerNodeId=t;break}r<e&&(e=r,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class O extends E{constructor(e,t=p){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.usage.tasks,i=r.executed+r.executing+r.queued;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class C extends E{taskStatisticsRequirements={runTime:w,waitTime:w,elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=p){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.usage,i=r.elu?.active.aggregate??0;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class z extends E{strategyPolicy={useDynamicWorker:!0};constructor(e,t=p){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.nextWorkerNodeId=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId;return this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1)),!0}}class q extends E{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:w,elu:w};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=p){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight()}reset(){return this.nextWorkerNodeId=0,this.workerVirtualTaskRunTime=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId,t=this.workerVirtualTaskRunTime;return t<(this.opts.weights?.[e]??this.defaultWorkerWeight)?this.workerVirtualTaskRunTime=t+this.getWorkerTaskRunTime(e):(this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,this.workerVirtualTaskRunTime=0),e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1),this.workerVirtualTaskRunTime=0),!0}}class Q{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=N.ROUND_ROBIN,s=p){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[N.ROUND_ROBIN,new(z.bind(this))(e,s)],[N.LEAST_USED,new(O.bind(this))(e,s)],[N.LEAST_BUSY,new(b.bind(this))(e,s)],[N.LEAST_ELU,new(C.bind(this))(e,s)],[N.FAIR_SHARE,new(I.bind(this))(e,s)],[N.WEIGHTED_ROUND_ROBIN,new(q.bind(this))(e,s)],[N.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(R.bind(this))(e,s)]])}getStrategyPolicy(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).strategyPolicy}getTaskStatisticsRequirements(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).taskStatisticsRequirements}setWorkerChoiceStrategy(e){this.workerChoiceStrategy!==e&&(this.workerChoiceStrategy=e),this.workerChoiceStrategies.get(this.workerChoiceStrategy)?.reset()}update(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).update(e)}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategy).choose();if(null==e)throw new Error("Worker node key chosen is null or undefined");return e}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).remove(e)}setOptions(e){for(const t of this.workerChoiceStrategies.values())t.setOptions(e)}}class A{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;constructor(e,t,s){if(this.numberOfWorkers=e,this.filePath=t,this.opts=s,!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.chooseWorkerNode=this.chooseWorkerNode.bind(this),this.executeTask=this.executeTask.bind(this),this.enqueueTask=this.enqueueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new d),this.workerChoiceStrategyContext=new Q(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker()}checkFilePath(e){if(null==e||"string"==typeof e&&0===e.trim().length)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 TypeError("Cannot instantiate a pool with a non safe integer number of workers");if(e<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===c.fixed&&0===e)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){if(!W(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??N.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??p,this.checkValidWorkerChoiceStrategyOptions(this.opts.workerChoiceStrategyOptions),this.opts.restartWorkerOnError=e.restartWorkerOnError??!0,this.opts.enableEvents=e.enableEvents??!0,this.opts.enableTasksQueue=e.enableTasksQueue??!1,this.opts.enableTasksQueue&&(this.checkValidTasksQueueOptions(e.tasksQueueOptions),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e.tasksQueueOptions))}checkValidWorkerChoiceStrategy(e){if(!Object.values(N).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}checkValidWorkerChoiceStrategyOptions(e){if(!W(e))throw new TypeError("Invalid worker choice strategy options: must be a plain object");if(null!=e.weights&&Object.keys(e.weights).length!==this.maxSize)throw new Error("Invalid worker choice strategy options: must have a weight for each worker node");if(null!=e.measurement&&!Object.values(v).includes(e.measurement))throw new Error(`Invalid worker choice strategy options: invalid measurement '${e.measurement}'`)}checkValidTasksQueueOptions(e){if(null!=e&&!W(e))throw new TypeError("Invalid tasks queue options: must be a plain object");if(null!=e?.concurrency&&!Number.isSafeInteger(e.concurrency))throw new TypeError("Invalid worker tasks concurrency: must be an integer");if(null!=e?.concurrency&&e.concurrency<=0)throw new Error(`Invalid worker tasks concurrency '${e.concurrency}'`)}get info(){return{type:this.type,worker:this.worker,minSize:this.minSize,maxSize:this.maxSize,workerNodes:this.workerNodes.length,idleWorkerNodes:this.workerNodes.reduce(((e,t)=>0===t.usage.tasks.executing?e+1:e),0),busyWorkerNodes:this.workerNodes.reduce(((e,t)=>t.usage.tasks.executing>0?e+1:e),0),executedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.executed),0),executingTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.executing),0),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.queued),0),maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.maxQueued),0),failedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.failed),0)}}getWorkerNodeKey(e){return this.workerNodes.findIndex((t=>t.worker===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const[e,t]of this.workerNodes.entries())this.setWorkerNodeTasksUsage(t,this.getWorkerUsage(e)),this.setWorkerStatistics(t.worker)}setWorkerChoiceStrategyOptions(e){this.checkValidWorkerChoiceStrategyOptions(e),this.opts.workerChoiceStrategyOptions=e,this.workerChoiceStrategyContext.setOptions(this.opts.workerChoiceStrategyOptions)}enableTasksQueue(e,t){!0!==this.opts.enableTasksQueue||e||this.flushTasksQueues(),this.opts.enableTasksQueue=e,this.setTasksQueueOptions(t)}setTasksQueueOptions(e){!0===this.opts.enableTasksQueue?(this.checkValidTasksQueueOptions(e),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e)):null!=this.opts.tasksQueueOptions&&delete this.opts.tasksQueueOptions}buildTasksQueueOptions(e){return{concurrency:e?.concurrency??1}}get full(){return this.workerNodes.length>=this.maxSize}internalBusy(){return-1===this.workerNodes.findIndex((e=>0===e.usage.tasks.executing))}async execute(e,t){const i=r.now(),o=this.chooseWorkerNode(),a={name:t,data:e??{},timestamp:i,id:s.randomUUID()},n=new Promise(((e,t)=>{this.promiseResponseMap.set(a.id,{resolve:e,reject:t,worker:this.workerNodes[o].worker})}));return!0===this.opts.enableTasksQueue&&(this.busy||this.workerNodes[o].usage.tasks.executing>=this.opts.tasksQueueOptions.concurrency)?this.enqueueTask(o,a):this.executeTask(o,a),this.checkAndEmitEvents(),n}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{this.flushTasksQueue(t),await this.destroyWorker(e.worker)})))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t)}afterTaskExecutionHook(e,t){const s=this.workerNodes[this.getWorkerNodeKey(e)].usage;this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,++s.executed,null!=t.taskError&&++s.failed}updateRunTimeWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&(e.runTime.aggregate+=t.taskPerformance?.runTime??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&null!=t.taskPerformance?.runTime&&(e.runTime.history.push(t.taskPerformance.runTime),e.runTime.median=f(e.runTime.history)))}updateWaitTimeWorkerUsage(e,t){const s=r.now(),i=s-(t.timestamp??s);this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&(e.waitTime.aggregate+=i??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&null!=i&&(e.waitTime.history.push(i),e.waitTime.median=f(e.waitTime.history)))}updateEluWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate){if(null!=e.elu&&null!=t.taskPerformance?.elu?(e.elu.idle.aggregate+=t.taskPerformance.elu.idle,e.elu.active.aggregate+=t.taskPerformance.elu.active,e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2):null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=t.taskPerformance.elu.idle,e.elu.active.aggregate=t.taskPerformance.elu.active,e.elu.utilization=t.taskPerformance.elu.utilization),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed){const t=e.tasks.executed-e.tasks.failed;e.elu.idle.average=e.elu.idle.aggregate/t,e.elu.active.average=e.elu.active.aggregate/t}this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&null!=t.taskPerformance?.elu&&(e.elu.idle.history.push(t.taskPerformance.elu.idle),e.elu.active.history.push(t.taskPerformance.elu.active),e.elu.idle.median=f(e.elu.idle.history),e.elu.active.median=f(e.elu.active.history))}}chooseWorkerNode(){if(this.shallCreateDynamicWorker()){const e=this.createAndSetupDynamicWorker();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return this.getWorkerNodeKey(e)}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===c.dynamic&&!this.full&&this.internalBusy()}registerWorkerMessageListener(e,t){e.on("message",t)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,this.workerListener())}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??g),e.on("error",this.opts.errorHandler??g),e.on("error",(e=>{null!=this.emitter&&this.emitter.emit(m.error,e),!0===this.opts.restartWorkerOnError&&this.createAndSetupWorker()})),e.on("online",this.opts.onlineHandler??g),e.on("exit",this.opts.exitHandler??g),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.pushWorkerNode(e),this.setWorkerStatistics(e),this.afterWorkerSetup(e),e}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=y.HARD,(t.kill===r||null!=t.kill&&(!1===this.opts.enableTasksQueue&&0===this.workerNodes[s].usage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[s].usage.tasks.executing&&0===this.tasksQueueSize(s)))&&this.destroyWorker(e)})),e}workerListener(){return e=>{if(null!=e.id){const t=this.promiseResponseMap.get(e.id);if(null!=t){null!=e.taskError?(null!=this.emitter&&this.emitter.emit(m.taskError,e.taskError),t.reject(e.taskError.message)):t.resolve(e.data),this.afterTaskExecutionHook(t.worker,e),this.promiseResponseMap.delete(e.id);const s=this.getWorkerNodeKey(t.worker);!0===this.opts.enableTasksQueue&&this.tasksQueueSize(s)>0&&this.executeTask(s,this.dequeueTask(s)),this.workerChoiceStrategyContext.update(s)}}}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter?.emit(m.busy,this.info),this.type===c.dynamic&&this.full&&this.emitter?.emit(m.full,this.info))}setWorkerNodeTasksUsage(e,t){e.usage=t}pushWorkerNode(e){this.workerNodes.push({worker:e,usage:this.getWorkerUsage(),tasksQueue:new x});const t=this.getWorkerNodeKey(e);return this.setWorkerNodeTasksUsage(this.workerNodes[t],this.getWorkerUsage(t)),this.workerNodes.length}removeWorkerNode(e){const t=this.getWorkerNodeKey(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(this.workerNodes[e].worker,t)}enqueueTask(e,t){return this.workerNodes[e].tasksQueue.enqueue(t)}dequeueTask(e){return this.workerNodes[e].tasksQueue.dequeue()}tasksQueueSize(e){return this.workerNodes[e].tasksQueue.size}tasksMaxQueueSize(e){return this.workerNodes[e].tasksQueue.maxSize}flushTasksQueue(e){if(this.tasksQueueSize(e)>0)for(let t=0;t<this.tasksQueueSize(e);t++)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].tasksQueue.clear()}flushTasksQueues(){for(const[e]of this.workerNodes.entries())this.flushTasksQueue(e)}setWorkerStatistics(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate}})}getWorkerUsage(e){const t=e=>null!=e?this.tasksQueueSize(e):0,s=e=>null!=e?this.tasksMaxQueueSize(e):0;return{tasks:{executed:0,executing:0,get queued(){return t(e)},get maxQueued(){return s(e)},failed:0},runTime:{aggregate:0,average:0,median:0,history:new S},waitTime:{aggregate:0,average:0,median:0,history:new S},elu:{idle:{aggregate:0,average:0,median:0,history:new S},active:{aggregate:0,average:0,median:0,history:new S},utilization:0}}}}class P extends A{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}setupHook(){t.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return t.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.on("disconnect",(()=>{e.kill()})),e.disconnect()}sendToWorker(e,t){e.send(t)}createWorker(){return t.fork(this.opts.env)}get type(){return c.fixed}get worker(){return l.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class D extends P{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return c.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}class U extends A{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return a}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,t){e.postMessage(t)}createWorker(){return new n(this.filePath,{env:h,...this.opts.workerOptions})}get type(){return c.fixed}get worker(){return l.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class M extends U{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return c.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}const _="default",V=6e4,j=y.SOFT;class B extends k{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;aliveInterval;constructor(e,t,s,i,o={killBehavior:j,maxInactiveTime:V}){super(e),this.isMain=t,this.mainWorker=i,this.opts=o,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||(this.lastTaskTimestamp=r.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??V)/2),this.checkAlive.bind(this)(),this.mainWorker?.on("message",this.messageListener.bind(this)))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??j,this.opts.maxInactiveTime=e.maxInactiveTime??V,delete this.opts.async}checkTaskFunctions(e){if(null==e)throw new Error("taskFunctions parameter is mandatory");if(this.taskFunctions=new Map,"function"==typeof e)this.taskFunctions.set(_,e.bind(this));else{if(!W(e))throw new TypeError("taskFunctions parameter is not a function or a plain object");{let t=!0;for(const[s,r]of Object.entries(e)){if("function"!=typeof r)throw new TypeError("A taskFunctions parameter object value is not a function");this.taskFunctions.set(s,r.bind(this)),t&&(this.taskFunctions.set(_,r.bind(this)),t=!1)}if(t)throw new Error("taskFunctions parameter object is empty")}}}messageListener(e){if(null!=e.id&&null!=e.data){const t=this.getTaskFunction(e.name);"AsyncFunction"===t?.constructor.name?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}else null!=e.statistics?this.statistics=e.statistics:null!=e.kill&&(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}checkAlive(){r.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??V)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}runSync(e,t){try{let s=this.beginTaskPerformance();const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})}finally{!this.isMain&&(this.lastTaskTimestamp=r.now())}}runAsync(e,t){let s=this.beginTaskPerformance();e(t.data).then((e=>(s=this.endTaskPerformance(s),this.sendToMainWorker({data:e,taskPerformance:s,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})})).finally((()=>{!this.isMain&&(this.lastTaskTimestamp=r.now())})).catch(g)}getTaskFunction(e){e=e??_;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(){return this.checkStatistics(),{timestamp:r.now(),...this.statistics.elu&&{elu:r.eventLoopUtilization()}}}endTaskPerformance(e){return this.checkStatistics(),{...e,...this.statistics.runTime&&{runTime:r.now()-e.timestamp},...this.statistics.elu&&{elu:r.eventLoopUtilization(e.elu)}}}checkStatistics(){if(null==this.statistics)throw new Error("Performance statistics computation requirements not set")}}class L extends B{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,s)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}}class F extends B{constructor(e,t={}){super("worker-thread-pool:poolifier",a,e,u,t)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}}export{L as ClusterWorker,D as DynamicClusterPool,M as DynamicThreadPool,P as FixedClusterPool,U as FixedThreadPool,y as KillBehaviors,v as Measurements,m as PoolEvents,c as PoolTypes,F as ThreadWorker,N as WorkerChoiceStrategies,l as WorkerTypes,T as availableParallelism};
|
|
@@ -69,7 +69,7 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
|
|
|
69
69
|
* Gets the given worker its worker node key.
|
|
70
70
|
*
|
|
71
71
|
* @param worker - The worker.
|
|
72
|
-
* @returns The worker node key if
|
|
72
|
+
* @returns The worker node key if found in the pool worker nodes, `-1` otherwise.
|
|
73
73
|
*/
|
|
74
74
|
private getWorkerNodeKey;
|
|
75
75
|
/** @inheritDoc */
|
package/lib/pools/worker.d.ts
CHANGED
package/lib/utility-types.d.ts
CHANGED
|
@@ -62,7 +62,7 @@ export interface MessageValue<Data = unknown, ErrorData = unknown> extends Task<
|
|
|
62
62
|
*/
|
|
63
63
|
readonly taskPerformance?: TaskPerformance;
|
|
64
64
|
/**
|
|
65
|
-
* Whether
|
|
65
|
+
* Whether the worker computes the given statistics or not.
|
|
66
66
|
*/
|
|
67
67
|
readonly statistics?: WorkerStatistics;
|
|
68
68
|
}
|
package/lib/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WorkerChoiceStrategyOptions } from './pools/selection-strategies/selection-strategies-types';
|
|
1
|
+
import type { MeasurementStatisticsRequirements, WorkerChoiceStrategyOptions } from './pools/selection-strategies/selection-strategies-types';
|
|
2
2
|
/**
|
|
3
3
|
* An intentional empty function.
|
|
4
4
|
*/
|
|
@@ -7,6 +7,14 @@ export declare const EMPTY_FUNCTION: () => void;
|
|
|
7
7
|
* Default worker choice strategy options.
|
|
8
8
|
*/
|
|
9
9
|
export declare const DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS: WorkerChoiceStrategyOptions;
|
|
10
|
+
/**
|
|
11
|
+
* Default measurement statistics requirements.
|
|
12
|
+
*/
|
|
13
|
+
export declare const DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS: MeasurementStatisticsRequirements;
|
|
14
|
+
/**
|
|
15
|
+
* Safe helper to get the host OS optimized maximum pool size.
|
|
16
|
+
*/
|
|
17
|
+
export declare const availableParallelism: () => number;
|
|
10
18
|
/**
|
|
11
19
|
* Compute the median of the given data set.
|
|
12
20
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poolifier",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.6",
|
|
4
4
|
"description": "A fast, easy to use Node.js Worker Thread Pool and Cluster Pool implementation",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"@release-it/bumper": "^4.0.2",
|
|
84
84
|
"@release-it/keep-a-changelog": "^3.1.0",
|
|
85
85
|
"@rollup/plugin-terser": "^0.4.3",
|
|
86
|
-
"@rollup/plugin-typescript": "^11.1.
|
|
86
|
+
"@rollup/plugin-typescript": "^11.1.2",
|
|
87
87
|
"@types/node": "^20.3.2",
|
|
88
88
|
"@typescript-eslint/eslint-plugin": "^5.60.1",
|
|
89
89
|
"@typescript-eslint/parser": "^5.60.1",
|
|
@@ -91,24 +91,24 @@
|
|
|
91
91
|
"c8": "^8.0.0",
|
|
92
92
|
"eslint": "^8.43.0",
|
|
93
93
|
"eslint-config-standard": "^17.1.0",
|
|
94
|
-
"eslint-config-standard-with-typescript": "^
|
|
94
|
+
"eslint-config-standard-with-typescript": "^36.0.0",
|
|
95
95
|
"eslint-define-config": "^1.21.0",
|
|
96
96
|
"eslint-import-resolver-typescript": "^3.5.5",
|
|
97
97
|
"eslint-plugin-import": "^2.27.5",
|
|
98
|
-
"eslint-plugin-jsdoc": "^46.3
|
|
98
|
+
"eslint-plugin-jsdoc": "^46.4.3",
|
|
99
99
|
"eslint-plugin-n": "^16.0.1",
|
|
100
100
|
"eslint-plugin-promise": "^6.1.1",
|
|
101
101
|
"eslint-plugin-spellcheck": "^0.0.20",
|
|
102
102
|
"eslint-plugin-tsdoc": "^0.2.17",
|
|
103
103
|
"expect": "^29.5.0",
|
|
104
104
|
"husky": "^8.0.3",
|
|
105
|
-
"lint-staged": "^13.2.
|
|
105
|
+
"lint-staged": "^13.2.3",
|
|
106
106
|
"microtime": "^3.1.1",
|
|
107
107
|
"mocha": "^10.2.0",
|
|
108
108
|
"mochawesome": "^7.1.3",
|
|
109
109
|
"prettier": "^2.8.8",
|
|
110
110
|
"release-it": "^15.11.0",
|
|
111
|
-
"rollup": "^3.
|
|
111
|
+
"rollup": "^3.26.0",
|
|
112
112
|
"rollup-plugin-analyzer": "^4.0.0",
|
|
113
113
|
"rollup-plugin-command": "^1.1.3",
|
|
114
114
|
"rollup-plugin-delete": "^2.0.0",
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
"source-map-support": "^0.5.21",
|
|
117
117
|
"ts-standard": "^12.0.2",
|
|
118
118
|
"typedoc": "^0.24.8",
|
|
119
|
-
"typescript": "^5.1.
|
|
119
|
+
"typescript": "^5.1.6"
|
|
120
120
|
},
|
|
121
121
|
"scripts": {
|
|
122
122
|
"preinstall": "npx --yes only-allow pnpm",
|