poolifier 2.6.9 → 2.6.10
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 +2 -0
- package/lib/index.d.ts +4 -3
- package/lib/index.js +1 -1
- package/lib/index.mjs +1 -1
- package/lib/pools/abstract-pool.d.ts +8 -21
- package/lib/pools/cluster/fixed.d.ts +2 -1
- package/lib/pools/pool.d.ts +38 -45
- package/lib/pools/selection-strategies/selection-strategies-types.d.ts +5 -5
- package/lib/pools/thread/fixed.d.ts +2 -1
- package/lib/pools/version.d.ts +1 -1
- package/lib/pools/worker-node.d.ts +46 -0
- package/lib/pools/worker.d.ts +49 -8
- package/lib/utility-types.d.ts +5 -13
- package/lib/worker/abstract-worker.d.ts +15 -4
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -122,6 +122,7 @@ const pool = new FixedThreadPool(availableParallelism(), './yourWorker.js', {
|
|
|
122
122
|
onlineHandler: () => console.info('worker is online')
|
|
123
123
|
})
|
|
124
124
|
|
|
125
|
+
pool.emitter.on(PoolEvents.ready, () => console.info('Pool is ready'))
|
|
125
126
|
pool.emitter.on(PoolEvents.busy, () => console.info('Pool is busy'))
|
|
126
127
|
|
|
127
128
|
// or a dynamic worker-threads pool
|
|
@@ -131,6 +132,7 @@ const pool = new DynamicThreadPool(Math.floor(availableParallelism() / 2), avail
|
|
|
131
132
|
})
|
|
132
133
|
|
|
133
134
|
pool.emitter.on(PoolEvents.full, () => console.info('Pool is full'))
|
|
135
|
+
pool.emitter.on(PoolEvents.ready, () => console.info('Pool is ready'))
|
|
134
136
|
pool.emitter.on(PoolEvents.busy, () => console.info('Pool is busy'))
|
|
135
137
|
|
|
136
138
|
// the execute method signature is the same for both implementations,
|
package/lib/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export type { AbstractPool } from './pools/abstract-pool';
|
|
2
2
|
export { DynamicClusterPool } from './pools/cluster/dynamic';
|
|
3
3
|
export { FixedClusterPool, type ClusterPoolOptions } from './pools/cluster/fixed';
|
|
4
|
-
export { PoolEvents, PoolTypes
|
|
5
|
-
export type { IPool, PoolEmitter, PoolEvent, PoolInfo, PoolOptions, PoolType, TasksQueueOptions
|
|
6
|
-
export
|
|
4
|
+
export { PoolEvents, PoolTypes } from './pools/pool';
|
|
5
|
+
export type { IPool, PoolEmitter, PoolEvent, PoolInfo, PoolOptions, PoolType, TasksQueueOptions } from './pools/pool';
|
|
6
|
+
export { WorkerTypes } from './pools/worker';
|
|
7
|
+
export type { ErrorHandler, EventLoopUtilizationMeasurementStatistics, ExitHandler, IWorker, IWorkerNode, MeasurementStatistics, MessageHandler, OnlineHandler, Task, TaskStatistics, WorkerInfo, WorkerType, WorkerUsage } from './pools/worker';
|
|
7
8
|
export { Measurements, WorkerChoiceStrategies } from './pools/selection-strategies/selection-strategies-types';
|
|
8
9
|
export type { IWorkerChoiceStrategy, Measurement, MeasurementOptions, MeasurementStatisticsRequirements, StrategyPolicy, TaskStatisticsRequirements, WorkerChoiceStrategy, WorkerChoiceStrategyOptions } from './pools/selection-strategies/selection-strategies-types';
|
|
9
10
|
export type { WorkerChoiceStrategyContext } from './pools/selection-strategies/worker-choice-strategy-context';
|
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"}),u=Object.freeze({cluster:"cluster",thread:"thread"});class h extends e.EventEmitter{}const k=Object.freeze({full:"full",busy:"busy",error:"error",taskError:"taskError"}),c=Object.freeze((()=>{})),d={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},m={aggregate:!1,average:!1,median:!1},l=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,t=2)=>{const s=Math.pow(10,t);return Math.round(e*s*(1+Number.EPSILON))/s},p=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),w=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class T 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 f{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 W=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"}),y=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class S{pool;opts;nextWorkerNodeId=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:m,waitTime:m,elu:m};constructor(e,t=d){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??d,this.setTaskStatisticsRequirements(this.opts)}getWorkerTaskRunTime(e){return this.taskStatisticsRequirements.runTime.median?this.pool.workerNodes[e].usage.runTime?.median??0:this.pool.workerNodes[e].usage.runTime?.average??0}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].usage.waitTime?.median??0:this.pool.workerNodes[e].usage.waitTime?.average??0}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].usage.elu.active?.median??0:this.pool.workerNodes[e].usage.elu.active?.average??0}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 x extends S{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:m,elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=d){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===y.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class N extends S{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=d){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 I extends S{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:m};constructor(e,t=d){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??0)+(s.usage.waitTime?.aggregate??0);if(0===r){this.nextWorkerNodeId=t;break}r<e&&(e=r,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class v extends S{constructor(e,t=d){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 S{taskStatisticsRequirements={runTime:m,waitTime:m,elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=d){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 R extends S{strategyPolicy={useDynamicWorker:!0};constructor(e,t=d){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 b extends S{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:m,elu:m};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=d){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=W.ROUND_ROBIN,s=d){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[W.ROUND_ROBIN,new(R.bind(this))(e,s)],[W.LEAST_USED,new(v.bind(this))(e,s)],[W.LEAST_BUSY,new(I.bind(this))(e,s)],[W.LEAST_ELU,new(E.bind(this))(e,s)],[W.FAIR_SHARE,new(x.bind(this))(e,s)],[W.WEIGHTED_ROUND_ROBIN,new(b.bind(this))(e,s)],[W.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(N.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 O{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;startTimestamp;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!");for(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 h),this.workerChoiceStrategyContext=new C(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();this.workerNodes.length<this.numberOfWorkers;)this.createAndSetupWorker();this.startTimestamp=r.performance.now()}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(!p(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??W.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??d,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(W).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}checkValidWorkerChoiceStrategyOptions(e){if(!p(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(y).includes(e.measurement))throw new Error(`Invalid worker choice strategy options: invalid measurement '${e.measurement}'`)}checkValidTasksQueueOptions(e){if(null!=e&&!p(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{version:"2.6.9",type:this.type,worker:this.worker,minSize:this.minSize,maxSize:this.maxSize,...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{utilization:g(this.utilization)},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),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&{runTime:{minimum:g(Math.min(...this.workerNodes.map((e=>e.usage.runTime?.minimum??1/0)))),maximum:g(Math.max(...this.workerNodes.map((e=>e.usage.runTime?.maximum??-1/0)))),average:g(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&{median:g(l(this.workerNodes.map((e=>e.usage.runTime?.median??0))))}}},...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{waitTime:{minimum:g(Math.min(...this.workerNodes.map((e=>e.usage.waitTime?.minimum??1/0)))),maximum:g(Math.max(...this.workerNodes.map((e=>e.usage.waitTime?.maximum??-1/0)))),average:g(this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&{median:g(l(this.workerNodes.map((e=>e.usage.waitTime?.median??0))))}}}}}get utilization(){const e=(r.performance.now()-this.startTimestamp)*this.maxSize;return(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)+this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0))/e}getWorkerById(e){return this.workerNodes.find((t=>t.info.id===e))?.worker}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 of this.workerNodes)this.setWorkerNodeTasksUsage(e,this.getInitialWorkerUsage(e.worker)),this.setWorkerStatistics(e.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);const s=new Promise((t=>{e.worker.on("exit",(()=>{t()}))}));await this.destroyWorker(e.worker),await s})))}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,null==t.taskError?++s.executed:++s.failed}updateRunTimeWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate){const s=t.taskPerformance?.runTime??0;e.runTime.aggregate=(e.runTime.aggregate??0)+s,e.runTime.minimum=Math.min(s,e.runTime?.minimum??1/0),e.runTime.maximum=Math.max(s,e.runTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&null!=t.taskPerformance?.runTime&&(e.runTime.history.push(t.taskPerformance.runTime),e.runTime.median=l(e.runTime.history))}}updateWaitTimeWorkerUsage(e,t){const s=r.performance.now(),i=s-(t.timestamp??s);this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&(e.waitTime.aggregate=(e.waitTime?.aggregate??0)+i,e.waitTime.minimum=Math.min(i,e.waitTime?.minimum??1/0),e.waitTime.maximum=Math.max(i,e.waitTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&(e.waitTime.history.push(i),e.waitTime.median=l(e.waitTime.history)))}updateEluWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate&&null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=(e.elu.idle?.aggregate??0)+t.taskPerformance.elu.idle,e.elu.active.aggregate=(e.elu.active?.aggregate??0)+t.taskPerformance.elu.active,null!=e.elu.utilization?e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2:e.elu.utilization=t.taskPerformance.elu.utilization,e.elu.idle.minimum=Math.min(t.taskPerformance.elu.idle,e.elu.idle?.minimum??1/0),e.elu.idle.maximum=Math.max(t.taskPerformance.elu.idle,e.elu.idle?.maximum??-1/0),e.elu.active.minimum=Math.min(t.taskPerformance.elu.active,e.elu.active?.minimum??1/0),e.elu.active.maximum=Math.max(t.taskPerformance.elu.active,e.elu.active?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed&&(e.elu.idle.average=e.elu.idle.aggregate/e.tasks.executed,e.elu.active.average=e.elu.active.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&(e.elu.idle.history.push(t.taskPerformance.elu.idle),e.elu.active.history.push(t.taskPerformance.elu.active),e.elu.idle.median=l(e.elu.idle.history),e.elu.active.median=l(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",(t=>{null!=this.emitter&&this.emitter.emit(k.error,t),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(e),!0===this.opts.restartWorkerOnError&&(this.getWorkerInfo(this.getWorkerNodeKey(e)).dynamic?this.createAndSetupDynamicWorker():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}redistributeQueuedTasks(e){const t=this.getWorkerNodeKey(e);for(;this.tasksQueueSize(t)>0;){let e=t,s=1/0;for(const[r,i]of this.workerNodes.entries()){if(r!==t&&0===i.usage.tasks.queued){e=r;break}r!==t&&i.usage.tasks.queued<s&&(s=i.usage.tasks.queued,e=r)}this.enqueueTask(e,this.dequeueTask(t))}}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();return this.getWorkerInfo(this.getWorkerNodeKey(e)).dynamic=!0,this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=w.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)})),this.sendToWorker(e,{dynamic:!0}),e}workerListener(){return e=>{null!=e.workerId&&null!=e.started?this.handleWorkerStartedMessage(e):null!=e.id&&this.handleTaskExecutionResponse(e)}}handleWorkerStartedMessage(e){const t=this.getWorkerById(e.workerId);if(null==t)throw new Error(`Worker started message received from unknown worker '${e.workerId}'`);this.workerNodes[this.getWorkerNodeKey(t)].info.started=e.started}handleTaskExecutionResponse(e){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}getWorkerInfo(e){return this.workerNodes[e].info}pushWorkerNode(e){return this.workerNodes.push({worker:e,info:this.getInitialWorkerInfo(e),usage:this.getInitialWorkerUsage(),tasksQueue:new f}),this.setWorkerNodeTasksUsage(this.workerNodes[this.getWorkerNodeKey(e)],this.getInitialWorkerUsage(e)),this.workerNodes.length}getWorkerId(e){return this.worker===u.thread?e.threadId:this.worker===u.cluster?e.id:void 0}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){for(;this.tasksQueueSize(e)>0;)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}})}getInitialWorkerUsage(e){const t=e=>null==e?0:this.tasksQueueSize(this.getWorkerNodeKey(e)),s=e=>null==e?0:this.tasksMaxQueueSize(this.getWorkerNodeKey(e));return{tasks:{executed:0,executing:0,get queued(){return t(e)},get maxQueued(){return s(e)},failed:0},runTime:{history:new T},waitTime:{history:new T},elu:{idle:{history:new T},active:{history:new T}}}}getInitialWorkerInfo(e){return{id:this.getWorkerId(e),dynamic:!1,started:!0}}}class q extends O{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 u.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class z extends O{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 u.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}const M="default",P=6e4,A=w.SOFT;class Q extends a.AsyncResource{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;aliveInterval;constructor(e,t,s,r,i={killBehavior:A,maxInactiveTime:P}){super(e),this.isMain=t,this.mainWorker=r,this.opts=i,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||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(M,e.bind(this));else{if(!p(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(M,r.bind(this)),t=!1)}if(t)throw new Error("taskFunctions parameter object is empty")}}}messageListener(e){if(null!=e.statistics)this.statistics=e.statistics;else if(!0===e.dynamic)this.startCheckAlive();else if(null!=e.id&&null!=e.data){const t=this.getTaskFunction(e.name);(e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name)(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}else null!=e.kill&&(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}startCheckAlive(){this.lastTaskTimestamp=r.performance.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??P)/2),this.checkAlive.bind(this)()}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 instanceof Error?e.message:e}runSync(e,t){try{let s=this.beginTaskPerformance();const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,workerId:this.id,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{workerId:this.id,message:s,data:t.data},id:t.id})}finally{this.isMain||null==this.aliveInterval||(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,workerId:this.id,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{workerId:this.id,message:s,data:t.data},id:t.id})})).finally((()=>{this.isMain||null==this.aliveInterval||(this.lastTaskTimestamp=r.performance.now())})).catch(c)}getTaskFunction(e){e=e??M;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)}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}},exports.DynamicClusterPool=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.DynamicThreadPool=class extends z{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=q,exports.FixedThreadPool=z,exports.KillBehaviors=w,exports.Measurements=y,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)}get id(){return o.threadId}sendToMainWorker(e){this.getMainWorker().postMessage(e)}handleError(e){return e}},exports.WorkerChoiceStrategies=W,exports.WorkerTypes=u,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};
|
|
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"});class u extends e.EventEmitter{}const h=Object.freeze({full:"full",ready:"ready",busy:"busy",error:"error",taskError:"taskError"}),k=Object.freeze((()=>{})),c={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},l=(e,t=2)=>{const s=Math.pow(10,t);return Math.round(e*s*(1+Number.EPSILON))/s},g=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),p=Object.freeze({SOFT:"SOFT",HARD:"HARD"}),w=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"}),T=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=c){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??c,this.setTaskStatisticsRequirements(this.opts)}getWorkerTaskRunTime(e){return this.taskStatisticsRequirements.runTime.median?this.pool.workerNodes[e].usage.runTime?.median??0:this.pool.workerNodes[e].usage.runTime?.average??0}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].usage.waitTime?.median??0:this.pool.workerNodes[e].usage.waitTime?.average??0}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].usage.elu.active?.median??0:this.pool.workerNodes[e].usage.elu.active?.average??0}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 f extends y{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:d,elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=c){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===T.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class W extends y{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=c){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 S extends y{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:d};constructor(e,t=c){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??0)+(s.usage.waitTime?.aggregate??0);if(0===r){this.nextWorkerNodeId=t;break}r<e&&(e=r,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class x extends y{constructor(e,t=c){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 N extends y{taskStatisticsRequirements={runTime:d,waitTime:d,elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=c){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=c){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 v extends y{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:d,elu:d};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=c){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 E{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=w.ROUND_ROBIN,s=c){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[w.ROUND_ROBIN,new(I.bind(this))(e,s)],[w.LEAST_USED,new(x.bind(this))(e,s)],[w.LEAST_BUSY,new(S.bind(this))(e,s)],[w.LEAST_ELU,new(N.bind(this))(e,s)],[w.FAIR_SHARE,new(f.bind(this))(e,s)],[w.WEIGHTED_ROUND_ROBIN,new(v.bind(this))(e,s)],[w.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(W.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 R 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 C{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 b=Object.freeze({cluster:"cluster",thread:"thread"});class O{worker;info;usage;tasksQueue;constructor(e,t){this.worker=e,this.info=this.initWorkerInfo(e,t),this.usage=this.initWorkerUsage(),this.tasksQueue=new C}tasksQueueSize(){return this.tasksQueue.size}tasksQueueMaxSize(){return this.tasksQueue.maxSize}enqueueTask(e){return this.tasksQueue.enqueue(e)}dequeueTask(){return this.tasksQueue.dequeue()}clearTasksQueue(){this.tasksQueue.clear()}resetUsage(){this.usage=this.initWorkerUsage()}initWorkerInfo(e,t){return{id:this.getWorkerId(e,t),type:t,dynamic:!1,ready:!1}}initWorkerUsage(){const e=()=>this.tasksQueueSize(),t=()=>this.tasksQueueMaxSize();return{tasks:{executed:0,executing:0,get queued(){return e()},get maxQueued(){return t()},failed:0},runTime:{history:new R},waitTime:{history:new R},elu:{idle:{history:new R},active:{history:new R}}}}getWorkerId(e,t){return t===b.thread?e.threadId:t===b.cluster?e.id:void 0}}class q{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;startTimestamp;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!");for(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 E(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();this.workerNodes.length<this.numberOfWorkers;)this.createAndSetupWorker();this.startTimestamp=r.performance.now()}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 RangeError("Cannot instantiate a fixed pool with zero worker")}checkDynamicPoolSize(e,t){if(this.type===n.dynamic&&e>t)throw new RangeError("Cannot instantiate a dynamic pool with a maximum pool size inferior to the minimum pool size");if(this.type===n.dynamic&&0===e&&0===t)throw new RangeError("Cannot instantiate a dynamic pool with a minimum pool size and a maximum pool size equal to zero");if(this.type===n.dynamic&&e===t)throw new RangeError("Cannot instantiate a dynamic pool with a minimum pool size equal to the maximum pool size. Use a fixed pool instead")}checkPoolOptions(e){if(!g(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??w.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??c,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(w).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(T).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{version:"2.6.10",type:this.type,worker:this.worker,ready:this.ready,strategy:this.opts.workerChoiceStrategy,minSize:this.minSize,maxSize:this.maxSize,...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{utilization:l(this.utilization)},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),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&{runTime:{minimum:l(Math.min(...this.workerNodes.map((e=>e.usage.runTime?.minimum??1/0)))),maximum:l(Math.max(...this.workerNodes.map((e=>e.usage.runTime?.maximum??-1/0)))),average:l(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&{median:l(m(this.workerNodes.map((e=>e.usage.runTime?.median??0))))}}},...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{waitTime:{minimum:l(Math.min(...this.workerNodes.map((e=>e.usage.waitTime?.minimum??1/0)))),maximum:l(Math.max(...this.workerNodes.map((e=>e.usage.waitTime?.maximum??-1/0)))),average:l(this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&{median:l(m(this.workerNodes.map((e=>e.usage.waitTime?.median??0))))}}}}}get starting(){return!this.full||this.full&&this.workerNodes.some((e=>!e.info.ready))}get ready(){return this.full&&this.workerNodes.every((e=>e.info.ready))}get utilization(){const e=(r.performance.now()-this.startTimestamp)*this.maxSize;return(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)+this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0))/e}getWorkerById(e){return this.workerNodes.find((t=>t.info.id===e))?.worker}checkMessageWorkerId(e){if(null!=e.workerId&&null==this.getWorkerById(e.workerId))throw new Error(`Worker message received from unknown worker '${e.workerId}'`)}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 of this.workerNodes)e.resetUsage(),this.setWorkerStatistics(e.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,workerId:this.getWorkerInfo(o).id,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);const s=new Promise((t=>{e.worker.on("exit",(()=>{t()}))}));await this.destroyWorker(e.worker),await s})))}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,null==t.taskError?++s.executed:++s.failed}updateRunTimeWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate){const s=t.taskPerformance?.runTime??0;e.runTime.aggregate=(e.runTime.aggregate??0)+s,e.runTime.minimum=Math.min(s,e.runTime?.minimum??1/0),e.runTime.maximum=Math.max(s,e.runTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/e.tasks.executed),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=(e.waitTime?.aggregate??0)+i,e.waitTime.minimum=Math.min(i,e.waitTime?.minimum??1/0),e.waitTime.maximum=Math.max(i,e.waitTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&(e.waitTime.history.push(i),e.waitTime.median=m(e.waitTime.history)))}updateEluWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate&&null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=(e.elu.idle?.aggregate??0)+t.taskPerformance.elu.idle,e.elu.active.aggregate=(e.elu.active?.aggregate??0)+t.taskPerformance.elu.active,null!=e.elu.utilization?e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2:e.elu.utilization=t.taskPerformance.elu.utilization,e.elu.idle.minimum=Math.min(t.taskPerformance.elu.idle,e.elu.idle?.minimum??1/0),e.elu.idle.maximum=Math.max(t.taskPerformance.elu.idle,e.elu.idle?.maximum??-1/0),e.elu.active.minimum=Math.min(t.taskPerformance.elu.active,e.elu.active?.minimum??1/0),e.elu.active.maximum=Math.max(t.taskPerformance.elu.active,e.elu.active?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed&&(e.elu.idle.average=e.elu.idle.aggregate/e.tasks.executed,e.elu.active.average=e.elu.active.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&(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()),this.sendToWorker(e,{ready:!1,workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id}),this.setWorkerStatistics(e)}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??k),e.on("error",this.opts.errorHandler??k),e.on("error",(t=>{null!=this.emitter&&this.emitter.emit(h.error,t),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(e),!0!==this.opts.restartWorkerOnError||this.starting||(this.getWorkerInfo(this.getWorkerNodeKey(e)).dynamic?this.createAndSetupDynamicWorker():this.createAndSetupWorker())})),e.on("online",this.opts.onlineHandler??k),e.on("exit",this.opts.exitHandler??k),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.pushWorkerNode(e),this.afterWorkerSetup(e),e}redistributeQueuedTasks(e){const t=this.getWorkerNodeKey(e);for(;this.tasksQueueSize(t)>0;){let e=t,s=1/0;for(const[r,i]of this.workerNodes.entries()){if(r!==t&&0===i.usage.tasks.queued){e=r;break}r!==t&&i.usage.tasks.queued<s&&(s=i.usage.tasks.queued,e=r)}this.enqueueTask(e,this.dequeueTask(t))}}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();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)}));const t=this.getWorkerInfo(this.getWorkerNodeKey(e));return t.dynamic=!0,this.sendToWorker(e,{checkAlive:!0,workerId:t.id}),e}workerListener(){return e=>{this.checkMessageWorkerId(e),null!=e.ready&&null!=e.workerId?this.handleWorkerReadyMessage(e):null!=e.id&&this.handleTaskExecutionResponse(e)}}handleWorkerReadyMessage(e){const t=this.getWorkerById(e.workerId);this.getWorkerInfo(this.getWorkerNodeKey(t)).ready=e.ready,null!=this.emitter&&this.ready&&this.emitter.emit(h.ready,this.info)}handleTaskExecutionResponse(e){const t=this.promiseResponseMap.get(e.id);if(null!=t){null!=e.taskError?(null!=this.emitter&&this.emitter.emit(h.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(h.busy,this.info),this.type===n.dynamic&&this.full&&this.emitter.emit(h.full,this.info))}getWorkerInfo(e){return this.workerNodes[e].info}pushWorkerNode(e){return this.workerNodes.push(new O(e,this.worker))}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].enqueueTask(t)}dequeueTask(e){return this.workerNodes[e].dequeueTask()}tasksQueueSize(e){return this.workerNodes[e].tasksQueueSize()}flushTasksQueue(e){for(;this.tasksQueueSize(e)>0;)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].clearTasksQueue()}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},workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id})}}class z extends q{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:!0,workerId:e.id}),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 b.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class M extends q{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return o.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:!0,workerId:e.threadId}),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 b.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}const P="default",A=6e4,Q=p.SOFT;class D extends a.AsyncResource{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;aliveInterval;constructor(e,t,s,r,i={killBehavior:Q,maxInactiveTime:A}){super(e),this.isMain=t,this.mainWorker=r,this.opts=i,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||this.mainWorker?.on("message",this.messageListener.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??Q,this.opts.maxInactiveTime=e.maxInactiveTime??A,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(!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(P,r.bind(this)),t=!1)}if(t)throw new Error("taskFunctions parameter object is empty")}}}messageListener(e){if(null!=e.ready&&e.workerId===this.id)this.workerReady();else if(null!=e.statistics&&e.workerId===this.id)this.statistics=e.statistics;else if(null!=e.checkAlive&&e.workerId===this.id)e.checkAlive?this.startCheckAlive():this.stopCheckAlive();else if(null!=e.id&&null!=e.data&&e.workerId===this.id){const t=this.getTaskFunction(e.name);(e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name)(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}else!0===e.kill&&e.workerId===this.id&&(this.stopCheckAlive(),this.emitDestroy())}workerReady(){!this.isMain&&this.sendToMainWorker({ready:!0,workerId:this.id})}startCheckAlive(){this.lastTaskTimestamp=r.performance.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??A)/2),this.checkAlive.bind(this)()}stopCheckAlive(){null!=this.aliveInterval&&clearInterval(this.aliveInterval)}checkAlive(){r.performance.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??A)&&this.sendToMainWorker({kill:this.opts.killBehavior,workerId:this.id})}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}handleError(e){return e instanceof Error?e.message:e}runSync(e,t){try{let s=this.beginTaskPerformance();const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,workerId:this.id,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},workerId:this.id,id:t.id})}finally{this.isMain||null==this.aliveInterval||(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,workerId:this.id,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},workerId:this.id,id:t.id})})).finally((()=>{this.isMain||null==this.aliveInterval||(this.lastTaskTimestamp=r.performance.now())})).catch(k)}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.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 D{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,s)}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}},exports.DynamicClusterPool=class extends z{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return n.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.DynamicThreadPool=class extends M{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return n.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.FixedClusterPool=z,exports.FixedThreadPool=M,exports.KillBehaviors=p,exports.Measurements=T,exports.PoolEvents=h,exports.PoolTypes=n,exports.ThreadWorker=class extends D{constructor(e,t={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,t)}get id(){return o.threadId}sendToMainWorker(e){this.getMainWorker().postMessage(e)}handleError(e){return e}},exports.WorkerChoiceStrategies=w,exports.WorkerTypes=b,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{EventEmitter as e}from"node:events";import t from"node:cluster";import{randomUUID as s}from"node:crypto";import{performance as r}from"node:perf_hooks";import{availableParallelism as i,cpus as o}from"node:os";import{isMainThread as a,Worker as n,SHARE_ENV as u,parentPort as h,threadId as k}from"node:worker_threads";import{AsyncResource as c}from"node:async_hooks";const d=Object.freeze({fixed:"fixed",dynamic:"dynamic"}),m=Object.freeze({cluster:"cluster",thread:"thread"});class l extends e{}const g=Object.freeze({full:"full",busy:"busy",error:"error",taskError:"taskError"}),p=Object.freeze((()=>{})),w={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},T={aggregate:!1,average:!1,median:!1},f=()=>{let e=1;try{e=i()}catch{const t=o();Array.isArray(t)&&t.length>0&&(e=t.length)}return e},W=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},y=(e,t=2)=>{const s=Math.pow(10,t);return Math.round(e*s*(1+Number.EPSILON))/s},S=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),x=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class N 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 I{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 v=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"}),E=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class R{pool;opts;nextWorkerNodeId=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:T,waitTime:T,elu:T};constructor(e,t=w){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??w,this.setTaskStatisticsRequirements(this.opts)}getWorkerTaskRunTime(e){return this.taskStatisticsRequirements.runTime.median?this.pool.workerNodes[e].usage.runTime?.median??0:this.pool.workerNodes[e].usage.runTime?.average??0}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].usage.waitTime?.median??0:this.pool.workerNodes[e].usage.waitTime?.average??0}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].usage.elu.active?.median??0:this.pool.workerNodes[e].usage.elu.active?.average??0}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 b extends R{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:T,elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=w){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===E.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class C extends R{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=w){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 O extends R{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:T};constructor(e,t=w){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??0)+(s.usage.waitTime?.aggregate??0);if(0===r){this.nextWorkerNodeId=t;break}r<e&&(e=r,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class q extends R{constructor(e,t=w){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 z extends R{taskStatisticsRequirements={runTime:T,waitTime:T,elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=w){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 M extends R{strategyPolicy={useDynamicWorker:!0};constructor(e,t=w){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 R{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:T,elu:T};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=w){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 A{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=v.ROUND_ROBIN,s=w){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[v.ROUND_ROBIN,new(M.bind(this))(e,s)],[v.LEAST_USED,new(q.bind(this))(e,s)],[v.LEAST_BUSY,new(O.bind(this))(e,s)],[v.LEAST_ELU,new(z.bind(this))(e,s)],[v.FAIR_SHARE,new(b.bind(this))(e,s)],[v.WEIGHTED_ROUND_ROBIN,new(Q.bind(this))(e,s)],[v.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(C.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 P{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;startTimestamp;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!");for(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 l),this.workerChoiceStrategyContext=new A(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();this.workerNodes.length<this.numberOfWorkers;)this.createAndSetupWorker();this.startTimestamp=r.now()}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===d.fixed&&0===e)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){if(!S(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??v.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??w,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(v).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}checkValidWorkerChoiceStrategyOptions(e){if(!S(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(E).includes(e.measurement))throw new Error(`Invalid worker choice strategy options: invalid measurement '${e.measurement}'`)}checkValidTasksQueueOptions(e){if(null!=e&&!S(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{version:"2.6.9",type:this.type,worker:this.worker,minSize:this.minSize,maxSize:this.maxSize,...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{utilization:y(this.utilization)},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),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&{runTime:{minimum:y(Math.min(...this.workerNodes.map((e=>e.usage.runTime?.minimum??1/0)))),maximum:y(Math.max(...this.workerNodes.map((e=>e.usage.runTime?.maximum??-1/0)))),average:y(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&{median:y(W(this.workerNodes.map((e=>e.usage.runTime?.median??0))))}}},...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{waitTime:{minimum:y(Math.min(...this.workerNodes.map((e=>e.usage.waitTime?.minimum??1/0)))),maximum:y(Math.max(...this.workerNodes.map((e=>e.usage.waitTime?.maximum??-1/0)))),average:y(this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&{median:y(W(this.workerNodes.map((e=>e.usage.waitTime?.median??0))))}}}}}get utilization(){const e=(r.now()-this.startTimestamp)*this.maxSize;return(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)+this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0))/e}getWorkerById(e){return this.workerNodes.find((t=>t.info.id===e))?.worker}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 of this.workerNodes)this.setWorkerNodeTasksUsage(e,this.getInitialWorkerUsage(e.worker)),this.setWorkerStatistics(e.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()},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);const s=new Promise((t=>{e.worker.on("exit",(()=>{t()}))}));await this.destroyWorker(e.worker),await s})))}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,null==t.taskError?++s.executed:++s.failed}updateRunTimeWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate){const s=t.taskPerformance?.runTime??0;e.runTime.aggregate=(e.runTime.aggregate??0)+s,e.runTime.minimum=Math.min(s,e.runTime?.minimum??1/0),e.runTime.maximum=Math.max(s,e.runTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&null!=t.taskPerformance?.runTime&&(e.runTime.history.push(t.taskPerformance.runTime),e.runTime.median=W(e.runTime.history))}}updateWaitTimeWorkerUsage(e,t){const s=r.now(),i=s-(t.timestamp??s);this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&(e.waitTime.aggregate=(e.waitTime?.aggregate??0)+i,e.waitTime.minimum=Math.min(i,e.waitTime?.minimum??1/0),e.waitTime.maximum=Math.max(i,e.waitTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&(e.waitTime.history.push(i),e.waitTime.median=W(e.waitTime.history)))}updateEluWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate&&null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=(e.elu.idle?.aggregate??0)+t.taskPerformance.elu.idle,e.elu.active.aggregate=(e.elu.active?.aggregate??0)+t.taskPerformance.elu.active,null!=e.elu.utilization?e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2:e.elu.utilization=t.taskPerformance.elu.utilization,e.elu.idle.minimum=Math.min(t.taskPerformance.elu.idle,e.elu.idle?.minimum??1/0),e.elu.idle.maximum=Math.max(t.taskPerformance.elu.idle,e.elu.idle?.maximum??-1/0),e.elu.active.minimum=Math.min(t.taskPerformance.elu.active,e.elu.active?.minimum??1/0),e.elu.active.maximum=Math.max(t.taskPerformance.elu.active,e.elu.active?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed&&(e.elu.idle.average=e.elu.idle.aggregate/e.tasks.executed,e.elu.active.average=e.elu.active.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&(e.elu.idle.history.push(t.taskPerformance.elu.idle),e.elu.active.history.push(t.taskPerformance.elu.active),e.elu.idle.median=W(e.elu.idle.history),e.elu.active.median=W(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===d.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??p),e.on("error",this.opts.errorHandler??p),e.on("error",(t=>{null!=this.emitter&&this.emitter.emit(g.error,t),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(e),!0===this.opts.restartWorkerOnError&&(this.getWorkerInfo(this.getWorkerNodeKey(e)).dynamic?this.createAndSetupDynamicWorker():this.createAndSetupWorker())})),e.on("online",this.opts.onlineHandler??p),e.on("exit",this.opts.exitHandler??p),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.pushWorkerNode(e),this.setWorkerStatistics(e),this.afterWorkerSetup(e),e}redistributeQueuedTasks(e){const t=this.getWorkerNodeKey(e);for(;this.tasksQueueSize(t)>0;){let e=t,s=1/0;for(const[r,i]of this.workerNodes.entries()){if(r!==t&&0===i.usage.tasks.queued){e=r;break}r!==t&&i.usage.tasks.queued<s&&(s=i.usage.tasks.queued,e=r)}this.enqueueTask(e,this.dequeueTask(t))}}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();return this.getWorkerInfo(this.getWorkerNodeKey(e)).dynamic=!0,this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=x.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)})),this.sendToWorker(e,{dynamic:!0}),e}workerListener(){return e=>{null!=e.workerId&&null!=e.started?this.handleWorkerStartedMessage(e):null!=e.id&&this.handleTaskExecutionResponse(e)}}handleWorkerStartedMessage(e){const t=this.getWorkerById(e.workerId);if(null==t)throw new Error(`Worker started message received from unknown worker '${e.workerId}'`);this.workerNodes[this.getWorkerNodeKey(t)].info.started=e.started}handleTaskExecutionResponse(e){const t=this.promiseResponseMap.get(e.id);if(null!=t){null!=e.taskError?(null!=this.emitter&&this.emitter.emit(g.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(g.busy,this.info),this.type===d.dynamic&&this.full&&this.emitter.emit(g.full,this.info))}setWorkerNodeTasksUsage(e,t){e.usage=t}getWorkerInfo(e){return this.workerNodes[e].info}pushWorkerNode(e){return this.workerNodes.push({worker:e,info:this.getInitialWorkerInfo(e),usage:this.getInitialWorkerUsage(),tasksQueue:new I}),this.setWorkerNodeTasksUsage(this.workerNodes[this.getWorkerNodeKey(e)],this.getInitialWorkerUsage(e)),this.workerNodes.length}getWorkerId(e){return this.worker===m.thread?e.threadId:this.worker===m.cluster?e.id:void 0}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){for(;this.tasksQueueSize(e)>0;)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}})}getInitialWorkerUsage(e){const t=e=>null==e?0:this.tasksQueueSize(this.getWorkerNodeKey(e)),s=e=>null==e?0:this.tasksMaxQueueSize(this.getWorkerNodeKey(e));return{tasks:{executed:0,executing:0,get queued(){return t(e)},get maxQueued(){return s(e)},failed:0},runTime:{history:new N},waitTime:{history:new N},elu:{idle:{history:new N},active:{history:new N}}}}getInitialWorkerInfo(e){return{id:this.getWorkerId(e),dynamic:!1,started:!0}}}class D extends P{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 d.fixed}get worker(){return m.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class U extends D{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return d.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}class _ extends P{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:u,...this.opts.workerOptions})}get type(){return d.fixed}get worker(){return m.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class V extends _{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return d.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}const B="default",j=6e4,L=x.SOFT;class F extends c{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;aliveInterval;constructor(e,t,s,r,i={killBehavior:L,maxInactiveTime:j}){super(e),this.isMain=t,this.mainWorker=r,this.opts=i,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||this.mainWorker?.on("message",this.messageListener.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??L,this.opts.maxInactiveTime=e.maxInactiveTime??j,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(B,e.bind(this));else{if(!S(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(B,r.bind(this)),t=!1)}if(t)throw new Error("taskFunctions parameter object is empty")}}}messageListener(e){if(null!=e.statistics)this.statistics=e.statistics;else if(!0===e.dynamic)this.startCheckAlive();else if(null!=e.id&&null!=e.data){const t=this.getTaskFunction(e.name);(e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name)(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}else null!=e.kill&&(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}startCheckAlive(){this.lastTaskTimestamp=r.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??j)/2),this.checkAlive.bind(this)()}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}checkAlive(){r.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??j)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e instanceof Error?e.message:e}runSync(e,t){try{let s=this.beginTaskPerformance();const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,workerId:this.id,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{workerId:this.id,message:s,data:t.data},id:t.id})}finally{this.isMain||null==this.aliveInterval||(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,workerId:this.id,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{workerId:this.id,message:s,data:t.data},id:t.id})})).finally((()=>{this.isMain||null==this.aliveInterval||(this.lastTaskTimestamp=r.now())})).catch(p)}getTaskFunction(e){e=e??B;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 H extends F{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,s)}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}}class K extends F{constructor(e,t={}){super("worker-thread-pool:poolifier",a,e,h,t)}get id(){return k}sendToMainWorker(e){this.getMainWorker().postMessage(e)}handleError(e){return e}}export{H as ClusterWorker,U as DynamicClusterPool,V as DynamicThreadPool,D as FixedClusterPool,_ as FixedThreadPool,x as KillBehaviors,E as Measurements,g as PoolEvents,d as PoolTypes,K as ThreadWorker,v as WorkerChoiceStrategies,m as WorkerTypes,f as availableParallelism};
|
|
1
|
+
import{EventEmitter as e}from"node:events";import t from"node:cluster";import{randomUUID as s}from"node:crypto";import{performance as r}from"node:perf_hooks";import{availableParallelism as i,cpus as o}from"node:os";import{isMainThread as a,Worker as n,SHARE_ENV as h,parentPort as u,threadId as k}from"node:worker_threads";import{AsyncResource as c}from"node:async_hooks";const d=Object.freeze({fixed:"fixed",dynamic:"dynamic"});class m extends e{}const l=Object.freeze({full:"full",ready:"ready",busy:"busy",error:"error",taskError:"taskError"}),g=Object.freeze((()=>{})),w={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},p={aggregate:!1,average:!1,median:!1},T=()=>{let e=1;try{e=i()}catch{const t=o();Array.isArray(t)&&t.length>0&&(e=t.length)}return e},y=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},f=(e,t=2)=>{const s=Math.pow(10,t);return Math.round(e*s*(1+Number.EPSILON))/s},W=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),S=Object.freeze({SOFT:"SOFT",HARD:"HARD"}),x=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"}),N=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class I{pool;opts;nextWorkerNodeId=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:p,waitTime:p,elu:p};constructor(e,t=w){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??w,this.setTaskStatisticsRequirements(this.opts)}getWorkerTaskRunTime(e){return this.taskStatisticsRequirements.runTime.median?this.pool.workerNodes[e].usage.runTime?.median??0:this.pool.workerNodes[e].usage.runTime?.average??0}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].usage.waitTime?.median??0:this.pool.workerNodes[e].usage.waitTime?.average??0}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].usage.elu.active?.median??0:this.pool.workerNodes[e].usage.elu.active?.average??0}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 v extends I{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:p,elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=w){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===N.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class E extends I{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=w){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 R extends I{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:p};constructor(e,t=w){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??0)+(s.usage.waitTime?.aggregate??0);if(0===r){this.nextWorkerNodeId=t;break}r<e&&(e=r,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class b extends I{constructor(e,t=w){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 I{taskStatisticsRequirements={runTime:p,waitTime:p,elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=w){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 O extends I{strategyPolicy={useDynamicWorker:!0};constructor(e,t=w){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 z extends I{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:p,elu:p};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=w){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=x.ROUND_ROBIN,s=w){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[x.ROUND_ROBIN,new(O.bind(this))(e,s)],[x.LEAST_USED,new(b.bind(this))(e,s)],[x.LEAST_BUSY,new(R.bind(this))(e,s)],[x.LEAST_ELU,new(C.bind(this))(e,s)],[x.FAIR_SHARE,new(v.bind(this))(e,s)],[x.WEIGHTED_ROUND_ROBIN,new(z.bind(this))(e,s)],[x.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(E.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 M 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 A{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 Q=Object.freeze({cluster:"cluster",thread:"thread"});class P{worker;info;usage;tasksQueue;constructor(e,t){this.worker=e,this.info=this.initWorkerInfo(e,t),this.usage=this.initWorkerUsage(),this.tasksQueue=new A}tasksQueueSize(){return this.tasksQueue.size}tasksQueueMaxSize(){return this.tasksQueue.maxSize}enqueueTask(e){return this.tasksQueue.enqueue(e)}dequeueTask(){return this.tasksQueue.dequeue()}clearTasksQueue(){this.tasksQueue.clear()}resetUsage(){this.usage=this.initWorkerUsage()}initWorkerInfo(e,t){return{id:this.getWorkerId(e,t),type:t,dynamic:!1,ready:!1}}initWorkerUsage(){const e=()=>this.tasksQueueSize(),t=()=>this.tasksQueueMaxSize();return{tasks:{executed:0,executing:0,get queued(){return e()},get maxQueued(){return t()},failed:0},runTime:{history:new M},waitTime:{history:new M},elu:{idle:{history:new M},active:{history:new M}}}}getWorkerId(e,t){return t===Q.thread?e.threadId:t===Q.cluster?e.id:void 0}}class D{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;startTimestamp;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!");for(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 m),this.workerChoiceStrategyContext=new q(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();this.workerNodes.length<this.numberOfWorkers;)this.createAndSetupWorker();this.startTimestamp=r.now()}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===d.fixed&&0===e)throw new RangeError("Cannot instantiate a fixed pool with zero worker")}checkDynamicPoolSize(e,t){if(this.type===d.dynamic&&e>t)throw new RangeError("Cannot instantiate a dynamic pool with a maximum pool size inferior to the minimum pool size");if(this.type===d.dynamic&&0===e&&0===t)throw new RangeError("Cannot instantiate a dynamic pool with a minimum pool size and a maximum pool size equal to zero");if(this.type===d.dynamic&&e===t)throw new RangeError("Cannot instantiate a dynamic pool with a minimum pool size equal to the maximum pool size. Use a fixed pool instead")}checkPoolOptions(e){if(!W(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??x.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??w,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(x).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(N).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{version:"2.6.10",type:this.type,worker:this.worker,ready:this.ready,strategy:this.opts.workerChoiceStrategy,minSize:this.minSize,maxSize:this.maxSize,...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{utilization:f(this.utilization)},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),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&{runTime:{minimum:f(Math.min(...this.workerNodes.map((e=>e.usage.runTime?.minimum??1/0)))),maximum:f(Math.max(...this.workerNodes.map((e=>e.usage.runTime?.maximum??-1/0)))),average:f(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&{median:f(y(this.workerNodes.map((e=>e.usage.runTime?.median??0))))}}},...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{waitTime:{minimum:f(Math.min(...this.workerNodes.map((e=>e.usage.waitTime?.minimum??1/0)))),maximum:f(Math.max(...this.workerNodes.map((e=>e.usage.waitTime?.maximum??-1/0)))),average:f(this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&{median:f(y(this.workerNodes.map((e=>e.usage.waitTime?.median??0))))}}}}}get starting(){return!this.full||this.full&&this.workerNodes.some((e=>!e.info.ready))}get ready(){return this.full&&this.workerNodes.every((e=>e.info.ready))}get utilization(){const e=(r.now()-this.startTimestamp)*this.maxSize;return(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)+this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0))/e}getWorkerById(e){return this.workerNodes.find((t=>t.info.id===e))?.worker}checkMessageWorkerId(e){if(null!=e.workerId&&null==this.getWorkerById(e.workerId))throw new Error(`Worker message received from unknown worker '${e.workerId}'`)}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 of this.workerNodes)e.resetUsage(),this.setWorkerStatistics(e.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,workerId:this.getWorkerInfo(o).id,id:s()},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);const s=new Promise((t=>{e.worker.on("exit",(()=>{t()}))}));await this.destroyWorker(e.worker),await s})))}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,null==t.taskError?++s.executed:++s.failed}updateRunTimeWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate){const s=t.taskPerformance?.runTime??0;e.runTime.aggregate=(e.runTime.aggregate??0)+s,e.runTime.minimum=Math.min(s,e.runTime?.minimum??1/0),e.runTime.maximum=Math.max(s,e.runTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&null!=t.taskPerformance?.runTime&&(e.runTime.history.push(t.taskPerformance.runTime),e.runTime.median=y(e.runTime.history))}}updateWaitTimeWorkerUsage(e,t){const s=r.now(),i=s-(t.timestamp??s);this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&(e.waitTime.aggregate=(e.waitTime?.aggregate??0)+i,e.waitTime.minimum=Math.min(i,e.waitTime?.minimum??1/0),e.waitTime.maximum=Math.max(i,e.waitTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&(e.waitTime.history.push(i),e.waitTime.median=y(e.waitTime.history)))}updateEluWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate&&null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=(e.elu.idle?.aggregate??0)+t.taskPerformance.elu.idle,e.elu.active.aggregate=(e.elu.active?.aggregate??0)+t.taskPerformance.elu.active,null!=e.elu.utilization?e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2:e.elu.utilization=t.taskPerformance.elu.utilization,e.elu.idle.minimum=Math.min(t.taskPerformance.elu.idle,e.elu.idle?.minimum??1/0),e.elu.idle.maximum=Math.max(t.taskPerformance.elu.idle,e.elu.idle?.maximum??-1/0),e.elu.active.minimum=Math.min(t.taskPerformance.elu.active,e.elu.active?.minimum??1/0),e.elu.active.maximum=Math.max(t.taskPerformance.elu.active,e.elu.active?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed&&(e.elu.idle.average=e.elu.idle.aggregate/e.tasks.executed,e.elu.active.average=e.elu.active.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&(e.elu.idle.history.push(t.taskPerformance.elu.idle),e.elu.active.history.push(t.taskPerformance.elu.active),e.elu.idle.median=y(e.elu.idle.history),e.elu.active.median=y(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===d.dynamic&&!this.full&&this.internalBusy()}registerWorkerMessageListener(e,t){e.on("message",t)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,this.workerListener()),this.sendToWorker(e,{ready:!1,workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id}),this.setWorkerStatistics(e)}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??g),e.on("error",this.opts.errorHandler??g),e.on("error",(t=>{null!=this.emitter&&this.emitter.emit(l.error,t),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(e),!0!==this.opts.restartWorkerOnError||this.starting||(this.getWorkerInfo(this.getWorkerNodeKey(e)).dynamic?this.createAndSetupDynamicWorker():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.afterWorkerSetup(e),e}redistributeQueuedTasks(e){const t=this.getWorkerNodeKey(e);for(;this.tasksQueueSize(t)>0;){let e=t,s=1/0;for(const[r,i]of this.workerNodes.entries()){if(r!==t&&0===i.usage.tasks.queued){e=r;break}r!==t&&i.usage.tasks.queued<s&&(s=i.usage.tasks.queued,e=r)}this.enqueueTask(e,this.dequeueTask(t))}}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=S.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)}));const t=this.getWorkerInfo(this.getWorkerNodeKey(e));return t.dynamic=!0,this.sendToWorker(e,{checkAlive:!0,workerId:t.id}),e}workerListener(){return e=>{this.checkMessageWorkerId(e),null!=e.ready&&null!=e.workerId?this.handleWorkerReadyMessage(e):null!=e.id&&this.handleTaskExecutionResponse(e)}}handleWorkerReadyMessage(e){const t=this.getWorkerById(e.workerId);this.getWorkerInfo(this.getWorkerNodeKey(t)).ready=e.ready,null!=this.emitter&&this.ready&&this.emitter.emit(l.ready,this.info)}handleTaskExecutionResponse(e){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===d.dynamic&&this.full&&this.emitter.emit(l.full,this.info))}getWorkerInfo(e){return this.workerNodes[e].info}pushWorkerNode(e){return this.workerNodes.push(new P(e,this.worker))}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].enqueueTask(t)}dequeueTask(e){return this.workerNodes[e].dequeueTask()}tasksQueueSize(e){return this.workerNodes[e].tasksQueueSize()}flushTasksQueue(e){for(;this.tasksQueueSize(e)>0;)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].clearTasksQueue()}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},workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id})}}class U extends D{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:!0,workerId:e.id}),e.on("disconnect",(()=>{e.kill()})),e.disconnect()}sendToWorker(e,t){e.send(t)}createWorker(){return t.fork(this.opts.env)}get type(){return d.fixed}get worker(){return Q.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class _ extends U{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return d.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}class V extends D{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return a}async destroyWorker(e){this.sendToWorker(e,{kill:!0,workerId:e.threadId}),await e.terminate()}sendToWorker(e,t){e.postMessage(t)}createWorker(){return new n(this.filePath,{env:h,...this.opts.workerOptions})}get type(){return d.fixed}get worker(){return Q.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class B extends V{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return d.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}const j="default",L=6e4,F=S.SOFT;class H extends c{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;aliveInterval;constructor(e,t,s,r,i={killBehavior:F,maxInactiveTime:L}){super(e),this.isMain=t,this.mainWorker=r,this.opts=i,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||this.mainWorker?.on("message",this.messageListener.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??F,this.opts.maxInactiveTime=e.maxInactiveTime??L,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(j,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(j,r.bind(this)),t=!1)}if(t)throw new Error("taskFunctions parameter object is empty")}}}messageListener(e){if(null!=e.ready&&e.workerId===this.id)this.workerReady();else if(null!=e.statistics&&e.workerId===this.id)this.statistics=e.statistics;else if(null!=e.checkAlive&&e.workerId===this.id)e.checkAlive?this.startCheckAlive():this.stopCheckAlive();else if(null!=e.id&&null!=e.data&&e.workerId===this.id){const t=this.getTaskFunction(e.name);(e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name)(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}else!0===e.kill&&e.workerId===this.id&&(this.stopCheckAlive(),this.emitDestroy())}workerReady(){!this.isMain&&this.sendToMainWorker({ready:!0,workerId:this.id})}startCheckAlive(){this.lastTaskTimestamp=r.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??L)/2),this.checkAlive.bind(this)()}stopCheckAlive(){null!=this.aliveInterval&&clearInterval(this.aliveInterval)}checkAlive(){r.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??L)&&this.sendToMainWorker({kill:this.opts.killBehavior,workerId:this.id})}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}handleError(e){return e instanceof Error?e.message:e}runSync(e,t){try{let s=this.beginTaskPerformance();const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,workerId:this.id,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},workerId:this.id,id:t.id})}finally{this.isMain||null==this.aliveInterval||(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,workerId:this.id,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},workerId:this.id,id:t.id})})).finally((()=>{this.isMain||null==this.aliveInterval||(this.lastTaskTimestamp=r.now())})).catch(g)}getTaskFunction(e){e=e??j;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 K extends H{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,s)}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}}class $ extends H{constructor(e,t={}){super("worker-thread-pool:poolifier",a,e,u,t)}get id(){return k}sendToMainWorker(e){this.getMainWorker().postMessage(e)}handleError(e){return e}}export{K as ClusterWorker,_ as DynamicClusterPool,B as DynamicThreadPool,U as FixedClusterPool,V as FixedThreadPool,S as KillBehaviors,N as Measurements,l as PoolEvents,d as PoolTypes,$ as ThreadWorker,x as WorkerChoiceStrategies,Q as WorkerTypes,T as availableParallelism};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MessageValue, PromiseResponseWrapper } from '../utility-types';
|
|
2
|
-
import { type IPool, PoolEmitter, type PoolInfo, type PoolOptions, type PoolType, type TasksQueueOptions
|
|
3
|
-
import type { IWorker, Task,
|
|
2
|
+
import { type IPool, PoolEmitter, type PoolInfo, type PoolOptions, type PoolType, type TasksQueueOptions } from './pool';
|
|
3
|
+
import type { IWorker, IWorkerNode, Task, WorkerType } from './worker';
|
|
4
4
|
import { type WorkerChoiceStrategy, type WorkerChoiceStrategyOptions } from './selection-strategies/selection-strategies-types';
|
|
5
5
|
import { WorkerChoiceStrategyContext } from './selection-strategies/worker-choice-strategy-context';
|
|
6
6
|
/**
|
|
@@ -15,7 +15,7 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
|
|
|
15
15
|
protected readonly filePath: string;
|
|
16
16
|
protected readonly opts: PoolOptions<Worker>;
|
|
17
17
|
/** @inheritDoc */
|
|
18
|
-
readonly workerNodes: Array<
|
|
18
|
+
readonly workerNodes: Array<IWorkerNode<Worker, Data>>;
|
|
19
19
|
/** @inheritDoc */
|
|
20
20
|
readonly emitter?: PoolEmitter;
|
|
21
21
|
/**
|
|
@@ -45,12 +45,15 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
|
|
|
45
45
|
constructor(numberOfWorkers: number, filePath: string, opts: PoolOptions<Worker>);
|
|
46
46
|
private checkFilePath;
|
|
47
47
|
private checkNumberOfWorkers;
|
|
48
|
+
protected checkDynamicPoolSize(min: number, max: number): void;
|
|
48
49
|
private checkPoolOptions;
|
|
49
50
|
private checkValidWorkerChoiceStrategy;
|
|
50
51
|
private checkValidWorkerChoiceStrategyOptions;
|
|
51
52
|
private checkValidTasksQueueOptions;
|
|
52
53
|
/** @inheritDoc */
|
|
53
54
|
get info(): PoolInfo;
|
|
55
|
+
private get starting();
|
|
56
|
+
private get ready();
|
|
54
57
|
/**
|
|
55
58
|
* Gets the approximate pool utilization.
|
|
56
59
|
*
|
|
@@ -82,6 +85,7 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
|
|
|
82
85
|
* @returns The worker if found in the pool worker nodes, `undefined` otherwise.
|
|
83
86
|
*/
|
|
84
87
|
private getWorkerById;
|
|
88
|
+
private checkMessageWorkerId;
|
|
85
89
|
/**
|
|
86
90
|
* Gets the given worker its worker node key.
|
|
87
91
|
*
|
|
@@ -217,16 +221,9 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
|
|
|
217
221
|
* @returns The listener function to execute when a message is received from a worker.
|
|
218
222
|
*/
|
|
219
223
|
protected workerListener(): (message: MessageValue<Response>) => void;
|
|
220
|
-
private
|
|
224
|
+
private handleWorkerReadyMessage;
|
|
221
225
|
private handleTaskExecutionResponse;
|
|
222
226
|
private checkAndEmitEvents;
|
|
223
|
-
/**
|
|
224
|
-
* Sets the given worker node its tasks usage in the pool.
|
|
225
|
-
*
|
|
226
|
-
* @param workerNode - The worker node.
|
|
227
|
-
* @param workerUsage - The worker usage.
|
|
228
|
-
*/
|
|
229
|
-
private setWorkerNodeTasksUsage;
|
|
230
227
|
/**
|
|
231
228
|
* Gets the worker information.
|
|
232
229
|
*
|
|
@@ -240,13 +237,6 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
|
|
|
240
237
|
* @returns The worker nodes length.
|
|
241
238
|
*/
|
|
242
239
|
private pushWorkerNode;
|
|
243
|
-
/**
|
|
244
|
-
* Gets the worker id.
|
|
245
|
-
*
|
|
246
|
-
* @param worker - The worker.
|
|
247
|
-
* @returns The worker id.
|
|
248
|
-
*/
|
|
249
|
-
private getWorkerId;
|
|
250
240
|
/**
|
|
251
241
|
* Removes the given worker from the pool worker nodes.
|
|
252
242
|
*
|
|
@@ -257,10 +247,7 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
|
|
|
257
247
|
private enqueueTask;
|
|
258
248
|
private dequeueTask;
|
|
259
249
|
private tasksQueueSize;
|
|
260
|
-
private tasksMaxQueueSize;
|
|
261
250
|
private flushTasksQueue;
|
|
262
251
|
private flushTasksQueues;
|
|
263
252
|
private setWorkerStatistics;
|
|
264
|
-
private getInitialWorkerUsage;
|
|
265
|
-
private getInitialWorkerInfo;
|
|
266
253
|
}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { type ClusterSettings, type Worker } from 'node:cluster';
|
|
3
3
|
import type { MessageValue } from '../../utility-types';
|
|
4
4
|
import { AbstractPool } from '../abstract-pool';
|
|
5
|
-
import { type PoolOptions, type PoolType
|
|
5
|
+
import { type PoolOptions, type PoolType } from '../pool';
|
|
6
|
+
import { type WorkerType } from '../worker';
|
|
6
7
|
/**
|
|
7
8
|
* Options for a poolifier cluster pool.
|
|
8
9
|
*/
|
package/lib/pools/pool.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { EventEmitter } from 'node:events';
|
|
3
|
-
import type { ErrorHandler, ExitHandler, IWorker, MessageHandler, OnlineHandler,
|
|
3
|
+
import type { ErrorHandler, ExitHandler, IWorker, IWorkerNode, MessageHandler, OnlineHandler, WorkerType } from './worker';
|
|
4
4
|
import type { WorkerChoiceStrategy, WorkerChoiceStrategyOptions } from './selection-strategies/selection-strategies-types';
|
|
5
5
|
/**
|
|
6
6
|
* Enumeration of pool types.
|
|
@@ -19,17 +19,6 @@ export declare const PoolTypes: Readonly<{
|
|
|
19
19
|
* Pool type.
|
|
20
20
|
*/
|
|
21
21
|
export type PoolType = keyof typeof PoolTypes;
|
|
22
|
-
/**
|
|
23
|
-
* Enumeration of worker types.
|
|
24
|
-
*/
|
|
25
|
-
export declare const WorkerTypes: Readonly<{
|
|
26
|
-
readonly cluster: "cluster";
|
|
27
|
-
readonly thread: "thread";
|
|
28
|
-
}>;
|
|
29
|
-
/**
|
|
30
|
-
* Worker type.
|
|
31
|
-
*/
|
|
32
|
-
export type WorkerType = keyof typeof WorkerTypes;
|
|
33
22
|
/**
|
|
34
23
|
* Pool events emitter.
|
|
35
24
|
*/
|
|
@@ -40,6 +29,7 @@ export declare class PoolEmitter extends EventEmitter {
|
|
|
40
29
|
*/
|
|
41
30
|
export declare const PoolEvents: Readonly<{
|
|
42
31
|
readonly full: "full";
|
|
32
|
+
readonly ready: "ready";
|
|
43
33
|
readonly busy: "busy";
|
|
44
34
|
readonly error: "error";
|
|
45
35
|
readonly taskError: "taskError";
|
|
@@ -52,35 +42,37 @@ export type PoolEvent = keyof typeof PoolEvents;
|
|
|
52
42
|
* Pool information.
|
|
53
43
|
*/
|
|
54
44
|
export interface PoolInfo {
|
|
55
|
-
version: string;
|
|
56
|
-
type: PoolType;
|
|
57
|
-
worker: WorkerType;
|
|
58
|
-
|
|
59
|
-
|
|
45
|
+
readonly version: string;
|
|
46
|
+
readonly type: PoolType;
|
|
47
|
+
readonly worker: WorkerType;
|
|
48
|
+
readonly ready: boolean;
|
|
49
|
+
readonly strategy: WorkerChoiceStrategy;
|
|
50
|
+
readonly minSize: number;
|
|
51
|
+
readonly maxSize: number;
|
|
60
52
|
/** Pool utilization ratio. */
|
|
61
|
-
utilization?: number;
|
|
53
|
+
readonly utilization?: number;
|
|
62
54
|
/** Pool total worker nodes */
|
|
63
|
-
workerNodes: number;
|
|
55
|
+
readonly workerNodes: number;
|
|
64
56
|
/** Pool idle worker nodes */
|
|
65
|
-
idleWorkerNodes: number;
|
|
57
|
+
readonly idleWorkerNodes: number;
|
|
66
58
|
/** Pool busy worker nodes */
|
|
67
|
-
busyWorkerNodes: number;
|
|
68
|
-
executedTasks: number;
|
|
69
|
-
executingTasks: number;
|
|
70
|
-
queuedTasks: number;
|
|
71
|
-
maxQueuedTasks: number;
|
|
72
|
-
failedTasks: number;
|
|
73
|
-
runTime?: {
|
|
74
|
-
minimum: number;
|
|
75
|
-
maximum: number;
|
|
76
|
-
average: number;
|
|
77
|
-
median?: number;
|
|
59
|
+
readonly busyWorkerNodes: number;
|
|
60
|
+
readonly executedTasks: number;
|
|
61
|
+
readonly executingTasks: number;
|
|
62
|
+
readonly queuedTasks: number;
|
|
63
|
+
readonly maxQueuedTasks: number;
|
|
64
|
+
readonly failedTasks: number;
|
|
65
|
+
readonly runTime?: {
|
|
66
|
+
readonly minimum: number;
|
|
67
|
+
readonly maximum: number;
|
|
68
|
+
readonly average: number;
|
|
69
|
+
readonly median?: number;
|
|
78
70
|
};
|
|
79
|
-
waitTime?: {
|
|
80
|
-
minimum: number;
|
|
81
|
-
maximum: number;
|
|
82
|
-
average: number;
|
|
83
|
-
median?: number;
|
|
71
|
+
readonly waitTime?: {
|
|
72
|
+
readonly minimum: number;
|
|
73
|
+
readonly maximum: number;
|
|
74
|
+
readonly average: number;
|
|
75
|
+
readonly median?: number;
|
|
84
76
|
};
|
|
85
77
|
}
|
|
86
78
|
/**
|
|
@@ -162,14 +154,15 @@ export interface IPool<Worker extends IWorker, Data = unknown, Response = unknow
|
|
|
162
154
|
/**
|
|
163
155
|
* Pool worker nodes.
|
|
164
156
|
*/
|
|
165
|
-
readonly workerNodes: Array<
|
|
157
|
+
readonly workerNodes: Array<IWorkerNode<Worker, Data>>;
|
|
166
158
|
/**
|
|
167
159
|
* Emitter on which events can be listened to.
|
|
168
160
|
*
|
|
169
161
|
* Events that can currently be listened to:
|
|
170
162
|
*
|
|
171
|
-
* - `'full'`: Emitted when the pool is dynamic and
|
|
172
|
-
* - `'
|
|
163
|
+
* - `'full'`: Emitted when the pool is dynamic and the number of workers created has reached the maximum size expected.
|
|
164
|
+
* - `'ready'`: Emitted when the number of workers created in the pool has reached the maximum size expected and are ready.
|
|
165
|
+
* - `'busy'`: Emitted when the number of workers created in the pool has reached the maximum size expected and are executing at least one task.
|
|
173
166
|
* - `'error'`: Emitted when an uncaught error occurs.
|
|
174
167
|
* - `'taskError'`: Emitted when an error occurs while executing a task.
|
|
175
168
|
*/
|
|
@@ -181,35 +174,35 @@ export interface IPool<Worker extends IWorker, Data = unknown, Response = unknow
|
|
|
181
174
|
* @param name - The name of the worker function to execute. If not specified, the default worker function will be executed.
|
|
182
175
|
* @returns Promise that will be fulfilled when the task is completed.
|
|
183
176
|
*/
|
|
184
|
-
execute: (data?: Data, name?: string) => Promise<Response>;
|
|
177
|
+
readonly execute: (data?: Data, name?: string) => Promise<Response>;
|
|
185
178
|
/**
|
|
186
179
|
* Terminates every current worker in this pool.
|
|
187
180
|
*/
|
|
188
|
-
destroy: () => Promise<void>;
|
|
181
|
+
readonly destroy: () => Promise<void>;
|
|
189
182
|
/**
|
|
190
183
|
* Sets the worker choice strategy in this pool.
|
|
191
184
|
*
|
|
192
185
|
* @param workerChoiceStrategy - The worker choice strategy.
|
|
193
186
|
* @param workerChoiceStrategyOptions - The worker choice strategy options.
|
|
194
187
|
*/
|
|
195
|
-
setWorkerChoiceStrategy: (workerChoiceStrategy: WorkerChoiceStrategy, workerChoiceStrategyOptions?: WorkerChoiceStrategyOptions) => void;
|
|
188
|
+
readonly setWorkerChoiceStrategy: (workerChoiceStrategy: WorkerChoiceStrategy, workerChoiceStrategyOptions?: WorkerChoiceStrategyOptions) => void;
|
|
196
189
|
/**
|
|
197
190
|
* Sets the worker choice strategy options in this pool.
|
|
198
191
|
*
|
|
199
192
|
* @param workerChoiceStrategyOptions - The worker choice strategy options.
|
|
200
193
|
*/
|
|
201
|
-
setWorkerChoiceStrategyOptions: (workerChoiceStrategyOptions: WorkerChoiceStrategyOptions) => void;
|
|
194
|
+
readonly setWorkerChoiceStrategyOptions: (workerChoiceStrategyOptions: WorkerChoiceStrategyOptions) => void;
|
|
202
195
|
/**
|
|
203
196
|
* Enables/disables the worker tasks queue in this pool.
|
|
204
197
|
*
|
|
205
198
|
* @param enable - Whether to enable or disable the worker tasks queue.
|
|
206
199
|
* @param tasksQueueOptions - The worker tasks queue options.
|
|
207
200
|
*/
|
|
208
|
-
enableTasksQueue: (enable: boolean, tasksQueueOptions?: TasksQueueOptions) => void;
|
|
201
|
+
readonly enableTasksQueue: (enable: boolean, tasksQueueOptions?: TasksQueueOptions) => void;
|
|
209
202
|
/**
|
|
210
203
|
* Sets the worker tasks queue options in this pool.
|
|
211
204
|
*
|
|
212
205
|
* @param tasksQueueOptions - The worker tasks queue options.
|
|
213
206
|
*/
|
|
214
|
-
setTasksQueueOptions: (tasksQueueOptions: TasksQueueOptions) => void;
|
|
207
|
+
readonly setTasksQueueOptions: (tasksQueueOptions: TasksQueueOptions) => void;
|
|
215
208
|
}
|
|
@@ -160,30 +160,30 @@ export interface IWorkerChoiceStrategy {
|
|
|
160
160
|
*
|
|
161
161
|
* @returns `true` if the reset is successful, `false` otherwise.
|
|
162
162
|
*/
|
|
163
|
-
reset: () => boolean;
|
|
163
|
+
readonly reset: () => boolean;
|
|
164
164
|
/**
|
|
165
165
|
* Updates the worker node key strategy internals.
|
|
166
166
|
*
|
|
167
167
|
* @returns `true` if the update is successful, `false` otherwise.
|
|
168
168
|
*/
|
|
169
|
-
update: (workerNodeKey: number) => boolean;
|
|
169
|
+
readonly update: (workerNodeKey: number) => boolean;
|
|
170
170
|
/**
|
|
171
171
|
* Chooses a worker node in the pool and returns its key.
|
|
172
172
|
*
|
|
173
173
|
* @returns The worker node key.
|
|
174
174
|
*/
|
|
175
|
-
choose: () => number;
|
|
175
|
+
readonly choose: () => number;
|
|
176
176
|
/**
|
|
177
177
|
* Removes the worker node key from strategy internals.
|
|
178
178
|
*
|
|
179
179
|
* @param workerNodeKey - The worker node key.
|
|
180
180
|
* @returns `true` if the worker node key is removed, `false` otherwise.
|
|
181
181
|
*/
|
|
182
|
-
remove: (workerNodeKey: number) => boolean;
|
|
182
|
+
readonly remove: (workerNodeKey: number) => boolean;
|
|
183
183
|
/**
|
|
184
184
|
* Sets the worker choice strategy options.
|
|
185
185
|
*
|
|
186
186
|
* @param opts - The worker choice strategy options.
|
|
187
187
|
*/
|
|
188
|
-
setOptions: (opts: WorkerChoiceStrategyOptions) => void;
|
|
188
|
+
readonly setOptions: (opts: WorkerChoiceStrategyOptions) => void;
|
|
189
189
|
}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { Worker, type WorkerOptions } from 'node:worker_threads';
|
|
3
3
|
import type { MessageValue } from '../../utility-types';
|
|
4
4
|
import { AbstractPool } from '../abstract-pool';
|
|
5
|
-
import { type PoolOptions, type PoolType
|
|
5
|
+
import { type PoolOptions, type PoolType } from '../pool';
|
|
6
|
+
import { type WorkerType } from '../worker';
|
|
6
7
|
/**
|
|
7
8
|
* Options for a poolifier thread pool.
|
|
8
9
|
*/
|
package/lib/pools/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "2.6.
|
|
1
|
+
export declare const version = "2.6.10";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type IWorker, type IWorkerNode, type Task, type WorkerInfo, type WorkerType, type WorkerUsage } from './worker';
|
|
2
|
+
/**
|
|
3
|
+
* Worker node.
|
|
4
|
+
*
|
|
5
|
+
* @typeParam Worker - Type of worker.
|
|
6
|
+
* @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
|
|
7
|
+
*/
|
|
8
|
+
export declare class WorkerNode<Worker extends IWorker, Data = unknown> implements IWorkerNode<Worker, Data> {
|
|
9
|
+
readonly worker: Worker;
|
|
10
|
+
readonly info: WorkerInfo;
|
|
11
|
+
usage: WorkerUsage;
|
|
12
|
+
private readonly tasksQueue;
|
|
13
|
+
/**
|
|
14
|
+
* Constructs a new worker node.
|
|
15
|
+
*
|
|
16
|
+
* @param worker - The worker.
|
|
17
|
+
* @param workerType - The worker type.
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
constructor(worker: Worker, workerType: WorkerType);
|
|
21
|
+
/** @inheritdoc */
|
|
22
|
+
tasksQueueSize(): number;
|
|
23
|
+
/**
|
|
24
|
+
* Worker node tasks queue maximum size.
|
|
25
|
+
*
|
|
26
|
+
* @returns The tasks queue maximum size.
|
|
27
|
+
*/
|
|
28
|
+
private tasksQueueMaxSize;
|
|
29
|
+
/** @inheritdoc */
|
|
30
|
+
enqueueTask(task: Task<Data>): number;
|
|
31
|
+
/** @inheritdoc */
|
|
32
|
+
dequeueTask(): Task<Data> | undefined;
|
|
33
|
+
/** @inheritdoc */
|
|
34
|
+
clearTasksQueue(): void;
|
|
35
|
+
resetUsage(): void;
|
|
36
|
+
private initWorkerInfo;
|
|
37
|
+
private initWorkerUsage;
|
|
38
|
+
/**
|
|
39
|
+
* Gets the worker id.
|
|
40
|
+
*
|
|
41
|
+
* @param worker - The worker.
|
|
42
|
+
* @param workerType - The worker type.
|
|
43
|
+
* @returns The worker id.
|
|
44
|
+
*/
|
|
45
|
+
private getWorkerId;
|
|
46
|
+
}
|
package/lib/pools/worker.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { CircularArray } from '../circular-array';
|
|
2
|
-
import type { Queue } from '../queue';
|
|
3
2
|
/**
|
|
4
3
|
* Callback invoked if the worker has received a message.
|
|
5
4
|
*/
|
|
@@ -23,6 +22,10 @@ export type ExitHandler<Worker extends IWorker> = (this: Worker, exitCode: numbe
|
|
|
23
22
|
* @internal
|
|
24
23
|
*/
|
|
25
24
|
export interface Task<Data = unknown> {
|
|
25
|
+
/**
|
|
26
|
+
* Worker id.
|
|
27
|
+
*/
|
|
28
|
+
readonly workerId: number;
|
|
26
29
|
/**
|
|
27
30
|
* Task name.
|
|
28
31
|
*/
|
|
@@ -108,6 +111,17 @@ export interface TaskStatistics {
|
|
|
108
111
|
*/
|
|
109
112
|
failed: number;
|
|
110
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Enumeration of worker types.
|
|
116
|
+
*/
|
|
117
|
+
export declare const WorkerTypes: Readonly<{
|
|
118
|
+
readonly cluster: "cluster";
|
|
119
|
+
readonly thread: "thread";
|
|
120
|
+
}>;
|
|
121
|
+
/**
|
|
122
|
+
* Worker type.
|
|
123
|
+
*/
|
|
124
|
+
export type WorkerType = keyof typeof WorkerTypes;
|
|
111
125
|
/**
|
|
112
126
|
* Worker information.
|
|
113
127
|
*
|
|
@@ -118,14 +132,18 @@ export interface WorkerInfo {
|
|
|
118
132
|
* Worker id.
|
|
119
133
|
*/
|
|
120
134
|
readonly id: number | undefined;
|
|
135
|
+
/**
|
|
136
|
+
* Worker type.
|
|
137
|
+
*/
|
|
138
|
+
type: WorkerType;
|
|
121
139
|
/**
|
|
122
140
|
* Dynamic flag.
|
|
123
141
|
*/
|
|
124
142
|
dynamic: boolean;
|
|
125
143
|
/**
|
|
126
|
-
*
|
|
144
|
+
* Ready flag.
|
|
127
145
|
*/
|
|
128
|
-
|
|
146
|
+
ready: boolean;
|
|
129
147
|
}
|
|
130
148
|
/**
|
|
131
149
|
* Worker usage statistics.
|
|
@@ -165,14 +183,14 @@ export interface IWorker {
|
|
|
165
183
|
* @param event - The event.
|
|
166
184
|
* @param handler - The event handler.
|
|
167
185
|
*/
|
|
168
|
-
on: ((event: 'message', handler: MessageHandler<this>) => void) & ((event: 'error', handler: ErrorHandler<this>) => void) & ((event: 'online', handler: OnlineHandler<this>) => void) & ((event: 'exit', handler: ExitHandler<this>) => void);
|
|
186
|
+
readonly on: ((event: 'message', handler: MessageHandler<this>) => void) & ((event: 'error', handler: ErrorHandler<this>) => void) & ((event: 'online', handler: OnlineHandler<this>) => void) & ((event: 'exit', handler: ExitHandler<this>) => void);
|
|
169
187
|
/**
|
|
170
188
|
* Registers a listener to the exit event that will only be performed once.
|
|
171
189
|
*
|
|
172
190
|
* @param event - `'exit'`.
|
|
173
191
|
* @param handler - The exit handler.
|
|
174
192
|
*/
|
|
175
|
-
once: (event: 'exit', handler: ExitHandler<this>) => void;
|
|
193
|
+
readonly once: (event: 'exit', handler: ExitHandler<this>) => void;
|
|
176
194
|
}
|
|
177
195
|
/**
|
|
178
196
|
* Worker node interface.
|
|
@@ -181,7 +199,7 @@ export interface IWorker {
|
|
|
181
199
|
* @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
|
|
182
200
|
* @internal
|
|
183
201
|
*/
|
|
184
|
-
export interface
|
|
202
|
+
export interface IWorkerNode<Worker extends IWorker, Data = unknown> {
|
|
185
203
|
/**
|
|
186
204
|
* Worker node worker.
|
|
187
205
|
*/
|
|
@@ -195,7 +213,30 @@ export interface WorkerNode<Worker extends IWorker, Data = unknown> {
|
|
|
195
213
|
*/
|
|
196
214
|
usage: WorkerUsage;
|
|
197
215
|
/**
|
|
198
|
-
* Worker node tasks queue.
|
|
216
|
+
* Worker node tasks queue size.
|
|
217
|
+
*
|
|
218
|
+
* @returns The tasks queue size.
|
|
219
|
+
*/
|
|
220
|
+
readonly tasksQueueSize: () => number;
|
|
221
|
+
/**
|
|
222
|
+
* Worker node enqueue task.
|
|
223
|
+
*
|
|
224
|
+
* @param task - The task to queue.
|
|
225
|
+
* @returns The task queue size.
|
|
226
|
+
*/
|
|
227
|
+
readonly enqueueTask: (task: Task<Data>) => number;
|
|
228
|
+
/**
|
|
229
|
+
* Worker node dequeue task.
|
|
230
|
+
*
|
|
231
|
+
* @returns The dequeued task.
|
|
232
|
+
*/
|
|
233
|
+
readonly dequeueTask: () => Task<Data> | undefined;
|
|
234
|
+
/**
|
|
235
|
+
* Worker node clear tasks queue.
|
|
236
|
+
*/
|
|
237
|
+
readonly clearTasksQueue: () => void;
|
|
238
|
+
/**
|
|
239
|
+
* Worker node reset usage statistics .
|
|
199
240
|
*/
|
|
200
|
-
readonly
|
|
241
|
+
readonly resetUsage: () => void;
|
|
201
242
|
}
|
package/lib/utility-types.d.ts
CHANGED
|
@@ -8,10 +8,6 @@ import type { IWorker, Task } from './pools/worker';
|
|
|
8
8
|
* @typeParam Data - Type of data sent to the worker triggering an error. This can only be structured-cloneable data.
|
|
9
9
|
*/
|
|
10
10
|
export interface TaskError<Data = unknown> {
|
|
11
|
-
/**
|
|
12
|
-
* Worker id.
|
|
13
|
-
*/
|
|
14
|
-
readonly workerId: number;
|
|
15
11
|
/**
|
|
16
12
|
* Error message.
|
|
17
13
|
*/
|
|
@@ -57,14 +53,10 @@ export interface WorkerStatistics {
|
|
|
57
53
|
* @internal
|
|
58
54
|
*/
|
|
59
55
|
export interface MessageValue<Data = unknown, ErrorData = unknown> extends Task<Data> {
|
|
60
|
-
/**
|
|
61
|
-
* Worker id.
|
|
62
|
-
*/
|
|
63
|
-
readonly workerId?: number;
|
|
64
56
|
/**
|
|
65
57
|
* Kill code.
|
|
66
58
|
*/
|
|
67
|
-
readonly kill?: KillBehavior |
|
|
59
|
+
readonly kill?: KillBehavior | true;
|
|
68
60
|
/**
|
|
69
61
|
* Task error.
|
|
70
62
|
*/
|
|
@@ -78,13 +70,13 @@ export interface MessageValue<Data = unknown, ErrorData = unknown> extends Task<
|
|
|
78
70
|
*/
|
|
79
71
|
readonly statistics?: WorkerStatistics;
|
|
80
72
|
/**
|
|
81
|
-
* Whether the worker
|
|
73
|
+
* Whether the worker is ready or not.
|
|
82
74
|
*/
|
|
83
|
-
readonly
|
|
75
|
+
readonly ready?: boolean;
|
|
84
76
|
/**
|
|
85
|
-
* Whether the worker
|
|
77
|
+
* Whether the worker starts or stops its aliveness check.
|
|
86
78
|
*/
|
|
87
|
-
readonly
|
|
79
|
+
readonly checkAlive?: boolean;
|
|
88
80
|
}
|
|
89
81
|
/**
|
|
90
82
|
* An object holding the execution response promise resolve/reject callbacks.
|
|
@@ -62,7 +62,22 @@ export declare abstract class AbstractWorker<MainWorker extends Worker | Message
|
|
|
62
62
|
* @param message - Message received.
|
|
63
63
|
*/
|
|
64
64
|
protected messageListener(message: MessageValue<Data, Data>): void;
|
|
65
|
+
/**
|
|
66
|
+
* Notifies the main worker that this worker is ready to process tasks.
|
|
67
|
+
*/
|
|
68
|
+
protected workerReady(): void;
|
|
69
|
+
/**
|
|
70
|
+
* Starts the worker alive check interval.
|
|
71
|
+
*/
|
|
65
72
|
private startCheckAlive;
|
|
73
|
+
/**
|
|
74
|
+
* Stops the worker alive check interval.
|
|
75
|
+
*/
|
|
76
|
+
private stopCheckAlive;
|
|
77
|
+
/**
|
|
78
|
+
* Checks if the worker should be terminated, because its living too long.
|
|
79
|
+
*/
|
|
80
|
+
private checkAlive;
|
|
66
81
|
/**
|
|
67
82
|
* Returns the main worker.
|
|
68
83
|
*
|
|
@@ -75,10 +90,6 @@ export declare abstract class AbstractWorker<MainWorker extends Worker | Message
|
|
|
75
90
|
* @param message - The response message.
|
|
76
91
|
*/
|
|
77
92
|
protected abstract sendToMainWorker(message: MessageValue<Response, Data>): void;
|
|
78
|
-
/**
|
|
79
|
-
* Checks if the worker should be terminated, because its living too long.
|
|
80
|
-
*/
|
|
81
|
-
protected checkAlive(): void;
|
|
82
93
|
/**
|
|
83
94
|
* Handles an error and convert it to a string so it can be sent back to the main worker.
|
|
84
95
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poolifier",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.10",
|
|
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",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"@release-it/keep-a-changelog": "^3.1.0",
|
|
85
85
|
"@rollup/plugin-terser": "^0.4.3",
|
|
86
86
|
"@rollup/plugin-typescript": "^11.1.2",
|
|
87
|
-
"@types/node": "^20.4.
|
|
87
|
+
"@types/node": "^20.4.1",
|
|
88
88
|
"@typescript-eslint/eslint-plugin": "^5.61.0",
|
|
89
89
|
"@typescript-eslint/parser": "^5.61.0",
|
|
90
90
|
"benny": "^3.7.1",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"mocha": "^10.2.0",
|
|
108
108
|
"mochawesome": "^7.1.3",
|
|
109
109
|
"prettier": "^2.8.8",
|
|
110
|
-
"release-it": "^16.
|
|
110
|
+
"release-it": "^16.1.0",
|
|
111
111
|
"rollup": "^3.26.2",
|
|
112
112
|
"rollup-plugin-analyzer": "^4.0.0",
|
|
113
113
|
"rollup-plugin-command": "^1.1.3",
|