poolifier 2.6.28 → 2.6.29
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 +1 -1
- package/lib/index.js +1 -1
- package/lib/index.mjs +1 -1
- package/lib/pools/abstract-pool.d.ts +10 -4
- package/lib/pools/pool.d.ts +12 -0
- package/lib/pools/selection-strategies/abstract-worker-choice-strategy.d.ts +18 -1
- package/lib/pools/selection-strategies/selection-strategies-types.d.ts +7 -1
- package/lib/pools/selection-strategies/worker-choice-strategy-context.d.ts +8 -3
- package/lib/pools/version.d.ts +1 -1
- package/lib/pools/worker-node.d.ts +15 -4
- package/lib/pools/worker.d.ts +23 -10
- package/lib/worker/abstract-worker.d.ts +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -128,7 +128,7 @@ pool.emitter.on(PoolEvents.ready, () => console.info('Pool is ready'))
|
|
|
128
128
|
pool.emitter.on(PoolEvents.busy, () => console.info('Pool is busy'))
|
|
129
129
|
|
|
130
130
|
// the execute method signature is the same for both implementations,
|
|
131
|
-
// so you can
|
|
131
|
+
// so you can easily switch from one to another
|
|
132
132
|
pool
|
|
133
133
|
.execute()
|
|
134
134
|
.then((res) => {
|
package/lib/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("node:events"),t=require("node:worker_threads"),s=require("node:cluster"),r=require("node:crypto"),i=require("node:perf_hooks"),o=require("node:fs"),n=require("node:os"),a=require("node:async_hooks");function h(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(s){if("default"!==s){var r=Object.getOwnPropertyDescriptor(e,s);Object.defineProperty(t,s,r.get?r:{enumerable:!0,get:function(){return e[s]}})}})),t.default=e,Object.freeze(t)}var u=h(n);const k=Object.freeze({fixed:"fixed",dynamic:"dynamic"});class c extends e.EventEmitter{}const d=Object.freeze({ready:"ready",busy:"busy",full:"full",destroy:"destroy",error:"error",taskError:"taskError"}),l="default",m=Object.freeze((()=>{})),g={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},p={aggregate:!1,average:!1,median:!1},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},f=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),T=(e,t)=>t===e,W=e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name,N=(e,t,s,r)=>{t.aggregate&&(e.aggregate=(e.aggregate??0)+s,e.minimum=Math.min(s,e.minimum??1/0),e.maximum=Math.max(s,e.maximum??-1/0),t.average&&0!==r&&(e.average=e.aggregate/r),t.median&&null!=s&&(e.history.push(s),e.median=w(e.history)))},x=Object.freeze({SOFT:"SOFT",HARD:"HARD"}),S=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 I{pool;opts;nextWorkerNodeKey=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:p,waitTime:p,elu:p};constructor(e,t=g){this.pool=e,this.opts=t,this.choose=this.choose.bind(this)}setTaskStatisticsRequirements(e){this.toggleMedianMeasurementStatisticsRequirements(this.taskStatisticsRequirements.runTime,e.runTime?.median),this.toggleMedianMeasurementStatisticsRequirements(this.taskStatisticsRequirements.waitTime,e.waitTime?.median),this.toggleMedianMeasurementStatisticsRequirements(this.taskStatisticsRequirements.elu,e.elu?.median)}toggleMedianMeasurementStatisticsRequirements(e,t){e.average&&t&&(e.average=!1,e.median=t),e.median&&!t&&(e.average=!0,e.median=t)}setOptions(e){this.opts=e??g,this.setTaskStatisticsRequirements(this.opts)}isWorkerNodeReady(e){return this.pool.workerNodes[e].info.ready}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 n.cpus()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/n.cpus().length)}}class b extends I{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:p,elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=g){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.workersVirtualTaskEndTimestamp=[],!0}update(e){return this.computeWorkerVirtualTaskEndTimestamp(e),!0}choose(){return this.fairShareNextWorkerNodeKey()}remove(e){return this.workersVirtualTaskEndTimestamp.splice(e,1),!0}fairShareNextWorkerNodeKey(){let e=1/0;for(const[t]of this.pool.workerNodes.entries()){null==this.workersVirtualTaskEndTimestamp[t]&&this.computeWorkerVirtualTaskEndTimestamp(t);const s=this.workersVirtualTaskEndTimestamp[t];this.isWorkerNodeReady(t)&&s<e&&(e=s,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}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 I{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=g){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight(),this.roundWeights=this.getRoundWeights()}reset(){return this.nextWorkerNodeKey=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.nextWorkerNodeKey;r<this.pool.workerNodes.length;r++){const i=this.opts.weights?.[r]??this.defaultWorkerWeight;if(this.isWorkerNodeReady(r)&&i>=this.roundWeights[s]){e=s,t=r;break}}this.roundId=e??0,this.nextWorkerNodeKey=t??0;const s=this.nextWorkerNodeKey;return this.nextWorkerNodeKey===this.pool.workerNodes.length-1?(this.nextWorkerNodeKey=0,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1):this.nextWorkerNodeKey=this.nextWorkerNodeKey+1,s}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=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=g){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastBusyNextWorkerNodeKey()}remove(){return!0}leastBusyNextWorkerNodeKey(){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(this.isWorkerNodeReady(t)&&0===r){this.nextWorkerNodeKey=t;break}this.isWorkerNodeReady(t)&&r<e&&(e=r,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class v extends I{constructor(e,t=g){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastUsedNextWorkerNodeKey()}remove(){return!0}leastUsedNextWorkerNodeKey(){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(this.isWorkerNodeReady(t)&&0===i){this.nextWorkerNodeKey=t;break}this.isWorkerNodeReady(t)&&i<e&&(e=i,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class O extends I{taskStatisticsRequirements={runTime:p,waitTime:p,elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=g){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastEluNextWorkerNodeKey()}remove(){return!0}leastEluNextWorkerNodeKey(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.usage,i=r.elu?.active?.aggregate??0;if(this.isWorkerNodeReady(t)&&0===i){this.nextWorkerNodeKey=t;break}this.isWorkerNodeReady(t)&&i<e&&(e=i,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class M extends I{strategyPolicy={useDynamicWorker:!0};constructor(e,t=g){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.nextWorkerNodeKey=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeKey;do{this.roundRobinNextWorkerNodeKey()}while(!this.isWorkerNodeReady(this.nextWorkerNodeKey));return e}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=this.pool.workerNodes.length-1)),!0}roundRobinNextWorkerNodeKey(){return this.nextWorkerNodeKey=this.nextWorkerNodeKey===this.pool.workerNodes.length-1?0:this.nextWorkerNodeKey+1,this.nextWorkerNodeKey}}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=g){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight()}reset(){return this.nextWorkerNodeKey=0,this.workerVirtualTaskRunTime=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeKey;do{this.weightedRoundRobinNextWorkerNodeKey()}while(!this.isWorkerNodeReady(this.nextWorkerNodeKey));return e}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=this.pool.workerNodes.length-1),this.workerVirtualTaskRunTime=0),!0}weightedRoundRobinNextWorkerNodeKey(){const e=this.workerVirtualTaskRunTime;return e<(this.opts.weights?.[this.nextWorkerNodeKey]??this.defaultWorkerWeight)?this.workerVirtualTaskRunTime=e+this.getWorkerTaskRunTime(this.nextWorkerNodeKey):(this.nextWorkerNodeKey=this.nextWorkerNodeKey===this.pool.workerNodes.length-1?0:this.nextWorkerNodeKey+1,this.workerVirtualTaskRunTime=0),this.nextWorkerNodeKey}}class K{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=S.ROUND_ROBIN,s=g){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[S.ROUND_ROBIN,new(M.bind(this))(e,s)],[S.LEAST_USED,new(v.bind(this))(e,s)],[S.LEAST_BUSY,new(R.bind(this))(e,s)],[S.LEAST_ELU,new(O.bind(this))(e,s)],[S.FAIR_SHARE,new(b.bind(this))(e,s)],[S.WEIGHTED_ROUND_ROBIN,new(z.bind(this))(e,s)],[S.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 TypeError("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 q 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=[];if(arguments.length>=3&&null!=t){if(r=super.splice(e,t,...s),this.length>this.size){const e=super.splice(0,this.length-this.size);r=new q(r.length+e.length,...r,...e)}}else r=2===arguments.length?super.splice(e,t):super.splice(e);return 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 Q{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}[Symbol.iterator](){const e=this.items;let t=this.offset;return{next:()=>{if(t>=e.length)return{value:void 0,done:!0};const s=e[t];return++t,{value:s,done:!1}}}}}const F=Object.freeze({thread:"thread",cluster:"cluster"});class A{worker;info;usage;tasksUsage;tasksQueue;constructor(e,t){this.worker=e,this.info=this.initWorkerInfo(e,t),this.usage=this.initWorkerUsage(),this.tasksUsage=new Map,this.tasksQueue=new Q}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(),this.tasksUsage.clear()}closeChannel(){null!=this.info.messageChannel&&(this.info.messageChannel?.port1.unref(),this.info.messageChannel?.port2.unref(),this.info.messageChannel?.port1.close(),this.info.messageChannel?.port2.close(),delete this.info.messageChannel)}getTaskWorkerUsage(e){return this.tasksUsage.has(e)||this.tasksUsage.set(e,this.initTaskWorkerUsage(e)),this.tasksUsage.get(e)}initWorkerInfo(e,s){return{id:this.getWorkerId(e,s),type:s,dynamic:!1,ready:!1,...s===F.thread&&{messageChannel:new t.MessageChannel}}}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 q},waitTime:{history:new q},elu:{idle:{history:new q},active:{history:new q}}}}initTaskWorkerUsage(e){const t=()=>{let t=0;for(const s of this.tasksQueue)s.name===e&&++t;return t};return{tasks:{executed:0,executing:0,get queued(){return t()},failed:0},runTime:{history:new q},waitTime:{history:new q},elu:{idle:{history:new q},active:{history:new q}}}}getWorkerId(e,t){return t===F.thread?e.threadId:t===F.cluster?e.id:void 0}}class P{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;starting;startTimestamp;taskFunctions;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 with the same type as the pool");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.dequeueTask=this.dequeueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new c),this.workerChoiceStrategyContext=new K(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook(),this.starting=!0,this.startPool(),this.starting=!1,this.startTimestamp=i.performance.now()}checkFilePath(e){if(null==e||"string"!=typeof e||"string"==typeof e&&0===e.trim().length)throw new Error("Please specify a file with a worker implementation");if(!o.existsSync(e))throw new Error(`Cannot find the worker file '${e}'`)}checkNumberOfWorkers(e){if(null==e)throw new Error("Cannot instantiate a pool without specifying the number of workers");if(!Number.isSafeInteger(e))throw new TypeError("Cannot instantiate a pool with a non safe integer number of workers");if(e<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===k.fixed&&0===e)throw new RangeError("Cannot instantiate a fixed pool with zero worker")}checkDynamicPoolSize(e,t){if(this.type===k.dynamic){if(null==t)throw new Error("Cannot instantiate a dynamic pool without specifying the maximum pool size");if(!Number.isSafeInteger(t))throw new TypeError("Cannot instantiate a dynamic pool with a non safe integer maximum pool size");if(e>t)throw new RangeError("Cannot instantiate a dynamic pool with a maximum pool size inferior to the minimum pool size");if(0===t)throw new RangeError("Cannot instantiate a dynamic pool with a maximum pool size equal to zero");if(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(!f(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??S.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??g,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(S).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}checkValidWorkerChoiceStrategyOptions(e){if(!f(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&&!f(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}'`)}startPool(){for(;this.workerNodes.reduce(((e,t)=>t.info.dynamic?e:e+1),0)<this.numberOfWorkers;)this.createAndSetupWorkerNode()}get info(){return{version:"2.6.28",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: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),...!0===this.opts.enableTasksQueue&&{queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.queued),0)},...!0===this.opts.enableTasksQueue&&{maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.maxQueued??0)),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 ready(){return this.workerNodes.reduce(((e,t)=>!t.info.dynamic&&t.info.ready?e+1:e),0)>=this.minSize}get utilization(){const e=(i.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}checkMessageWorkerId(e){if(null==e.workerId)throw new Error("Worker message received without worker id");if(null!=e.workerId&&-1===this.getWorkerNodeKeyByWorkerId(e.workerId))throw new Error(`Worker message received from unknown worker '${e.workerId}'`)}getWorkerNodeKeyByWorker(e){return this.workerNodes.findIndex((t=>t.worker===e))}getWorkerNodeKeyByWorkerId(e){return this.workerNodes.findIndex((t=>t.info.id===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const[e,t]of this.workerNodes.entries())t.resetUsage(),this.sendStatisticsMessageToWorker(e)}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!0===this.opts.enableTasksQueue?-1===this.workerNodes.findIndex((e=>e.info.ready&&e.usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency)):-1===this.workerNodes.findIndex((e=>e.info.ready&&0===e.usage.tasks.executing))}listTaskFunctions(){return null!=this.taskFunctions?this.taskFunctions:[]}async execute(e,t,s){return await new Promise(((o,n)=>{null!=t&&"string"!=typeof t&&n(new TypeError("name argument must be a string")),null!=t&&"string"==typeof t&&0===t.trim().length&&n(new TypeError("name argument must not be an empty string")),null==t||null==this.taskFunctions||this.taskFunctions.includes(t)||n(new Error(`Task function '${t}' is not registered in the pool`)),null==s||Array.isArray(s)||n(new TypeError("transferList argument must be an array"));const a=i.performance.now(),h=this.chooseWorkerNode(),u={name:t??l,data:e??{},transferList:s,timestamp:a,workerId:this.getWorkerInfo(h).id,taskId:r.randomUUID()};this.promiseResponseMap.set(u.taskId,{resolve:o,reject:n,workerNodeKey:h}),!1===this.opts.enableTasksQueue||!0===this.opts.enableTasksQueue&&this.workerNodes[h].usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency?this.executeTask(h,u):this.enqueueTask(h,u),this.checkAndEmitEvents()}))}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{await this.destroyWorkerNode(t)}))),this.emitter?.emit(d.destroy)}async sendKillMessageToWorker(e,t){await new Promise(((s,r)=>{this.registerWorkerMessageListener(e,(e=>{"success"===e.kill?s():"failure"===e.kill&&r(new Error(`Worker ${t} kill message handling failed`))})),this.sendToWorker(e,{kill:!0,workerId:t})}))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t);const r=this.workerNodes[e].getTaskWorkerUsage(t.name);++r.tasks.executing,this.updateWaitTimeWorkerUsage(r,t)}afterTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t);const r=this.workerNodes[e].getTaskWorkerUsage(t.taskPerformance?.name??l);this.updateTaskStatisticsWorkerUsage(r,t),this.updateRunTimeWorkerUsage(r,t),this.updateEluWorkerUsage(r,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,null==t.taskError?++s.executed:++s.failed}updateRunTimeWorkerUsage(e,t){N(e.runTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime,t.taskPerformance?.runTime??0,e.tasks.executed)}updateWaitTimeWorkerUsage(e,t){const s=i.performance.now(),r=s-(t.timestamp??s);N(e.waitTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime,r,e.tasks.executed)}updateEluWorkerUsage(e,t){const s=this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu;N(e.elu.active,s,t.taskPerformance?.elu?.active??0,e.tasks.executed),N(e.elu.idle,s,t.taskPerformance?.elu?.idle??0,e.tasks.executed),s.aggregate&&null!=t.taskPerformance?.elu&&(null!=e.elu.utilization?e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2:e.elu.utilization=t.taskPerformance.elu.utilization)}chooseWorkerNode(){if(this.shallCreateDynamicWorker()){const e=this.createAndSetupDynamicWorkerNode();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return e}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===k.dynamic&&!this.full&&this.internalBusy()}createAndSetupWorkerNode(){const e=this.createWorker();e.on("online",this.opts.onlineHandler??m),e.on("message",this.opts.messageHandler??m),e.on("error",this.opts.errorHandler??m),e.on("error",(t=>{const s=this.getWorkerNodeKeyByWorker(e),r=this.getWorkerInfo(s);r.ready=!1,this.workerNodes[s].closeChannel(),this.emitter?.emit(d.error,t),!0!==this.opts.restartWorkerOnError||this.starting||(r.dynamic?this.createAndSetupDynamicWorkerNode():this.createAndSetupWorkerNode()),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(s)})),e.on("exit",this.opts.exitHandler??m),e.once("exit",(()=>{this.removeWorkerNode(e)}));const t=this.addWorkerNode(e);return this.afterWorkerNodeSetup(t),t}createAndSetupDynamicWorkerNode(){const e=this.createAndSetupWorkerNode();this.registerWorkerMessageListener(e,(e=>{const t=this.getWorkerNodeKeyByWorkerId(e.workerId),s=this.workerNodes[t].usage;(T(x.HARD,e.kill)||T(x.SOFT,e.kill)&&(!1===this.opts.enableTasksQueue&&0===s.tasks.executing||!0===this.opts.enableTasksQueue&&0===s.tasks.executing&&0===this.tasksQueueSize(t)))&&this.destroyWorkerNode(t).catch((e=>{this.emitter?.emit(d.error,e)}))}));const t=this.getWorkerInfo(e);return this.sendToWorker(e,{checkActive:!0,workerId:t.id}),t.dynamic=!0,this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker&&(t.ready=!0),e}afterWorkerNodeSetup(e){this.registerWorkerMessageListener(e,this.workerListener()),this.sendStartupMessageToWorker(e),this.sendStatisticsMessageToWorker(e)}sendStatisticsMessageToWorker(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate},workerId:this.getWorkerInfo(e).id})}redistributeQueuedTasks(e){for(;this.tasksQueueSize(e)>0;){let t=e,s=1/0,r=!1;for(const[i,o]of this.workerNodes.entries()){const n=this.getWorkerInfo(i);if(i!==e&&n.ready&&0===o.usage.tasks.queued){this.workerNodes[i].usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency&&(r=!0),t=i;break}i!==e&&n.ready&&o.usage.tasks.queued<s&&(s=o.usage.tasks.queued,t=i)}r?this.executeTask(t,this.dequeueTask(e)):this.enqueueTask(t,this.dequeueTask(e))}}workerListener(){return e=>{this.checkMessageWorkerId(e),null!=e.ready?this.handleWorkerReadyResponse(e):null!=e.taskId?this.handleTaskExecutionResponse(e):null!=e.taskFunctions&&(this.taskFunctions=e.taskFunctions)}}handleWorkerReadyResponse(e){if(!1===e.ready)throw new Error(`Worker ${e.workerId} failed to initialize`);this.getWorkerInfo(this.getWorkerNodeKeyByWorkerId(e.workerId)).ready=e.ready,null!=this.emitter&&this.ready&&this.emitter.emit(d.ready,this.info)}handleTaskExecutionResponse(e){const t=this.promiseResponseMap.get(e.taskId);if(null!=t){null!=e.taskError?(this.emitter?.emit(d.taskError,e.taskError),t.reject(e.taskError.message)):t.resolve(e.data);const s=t.workerNodeKey;this.afterTaskExecutionHook(s,e),this.promiseResponseMap.delete(e.taskId),!0===this.opts.enableTasksQueue&&this.tasksQueueSize(s)>0&&this.workerNodes[s].usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency&&this.executeTask(s,this.dequeueTask(s)),this.workerChoiceStrategyContext.update(s)}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter.emit(d.busy,this.info),this.type===k.dynamic&&this.full&&this.emitter.emit(d.full,this.info))}getWorkerInfo(e){return this.workerNodes[e].info}addWorkerNode(e){const t=new A(e,this.worker);this.starting&&(t.info.ready=!0),this.workerNodes.push(t);const s=this.getWorkerNodeKeyByWorker(e);if(-1===s)throw new Error("Worker node not found");return s}removeWorkerNode(e){const t=this.getWorkerNodeKeyByWorker(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(e,t,t.transferList)}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)}}class U extends P{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}setupHook(){s.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return s.isPrimary}async destroyWorkerNode(e){this.flushTasksQueue(e);const t=this.workerNodes[e].worker,s=new Promise((e=>{t.on("exit",(()=>{e()}))}));t.on("disconnect",(()=>{t.kill()})),await this.sendKillMessageToWorker(e,t.id),t.disconnect(),await s}sendToWorker(e,t){this.workerNodes[e].worker.send(t)}sendStartupMessageToWorker(e){this.sendToWorker(e,{ready:!1,workerId:this.workerNodes[e].worker.id})}registerWorkerMessageListener(e,t){this.workerNodes[e].worker.on("message",t)}createWorker(){return s.fork(this.opts.env)}get type(){return k.fixed}get worker(){return F.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class D extends P{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return t.isMainThread}async destroyWorkerNode(e){this.flushTasksQueue(e);const t=this.workerNodes[e],s=t.worker,r=new Promise((e=>{s.on("exit",(()=>{e()}))}));await this.sendKillMessageToWorker(e,s.threadId),t.closeChannel(),await s.terminate(),await r}sendToWorker(e,t,s){this.getWorkerInfo(e).messageChannel.port1.postMessage(t,s)}sendStartupMessageToWorker(e){const t=this.workerNodes[e].worker,s=this.getWorkerInfo(e).messageChannel.port2;t.postMessage({ready:!1,workerId:t.threadId,port:s},[s])}registerWorkerMessageListener(e,t){this.getWorkerInfo(e).messageChannel.port1.on("message",t)}createWorker(){return new t.Worker(this.filePath,{env:t.SHARE_ENV,...this.opts.workerOptions})}get type(){return k.fixed}get worker(){return F.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}const L=6e4,_=x.SOFT;class B extends a.AsyncResource{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;activeInterval;constructor(e,t,s,r,i={killBehavior:_,maxInactiveTime:L,killHandler:m}){super(e),this.isMain=t,this.mainWorker=s,this.opts=i,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(r),this.isMain||this.getMainWorker()?.on("message",this.handleReadyMessage.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??_,this.opts.maxInactiveTime=e.maxInactiveTime??L,this.opts.killHandler=e.killHandler??m,delete this.opts.async}checkTaskFunctions(e){if(null==e)throw new Error("taskFunctions parameter is mandatory");if(this.taskFunctions=new Map,"function"==typeof e){const t=e.bind(this);this.taskFunctions.set(l,t),this.taskFunctions.set("string"==typeof e.name&&e.name.trim().length>0?e.name:"fn1",t)}else{if(!f(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("string"!=typeof s)throw new TypeError("A taskFunctions parameter object key is not a string");if("string"==typeof s&&0===s.trim().length)throw new TypeError("A taskFunctions parameter object key an empty string");if("function"!=typeof r)throw new TypeError("A taskFunctions parameter object value is not a function");const e=r.bind(this);t&&(this.taskFunctions.set(l,e),t=!1),this.taskFunctions.set(s,e)}if(t)throw new Error("taskFunctions parameter object is empty")}}}hasTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");return this.taskFunctions.has(e)}addTaskFunction(e,t){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");if(e===l)throw new Error("Cannot add a task function with the default reserved name");if("function"!=typeof t)throw new TypeError("fn parameter is not a function");try{const s=t.bind(this);return this.taskFunctions.get(e)===this.taskFunctions.get(l)&&this.taskFunctions.set(l,s),this.taskFunctions.set(e,s),this.sendTaskFunctionsListToMainWorker(),!0}catch{return!1}}removeTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");if(e===l)throw new Error("Cannot remove the task function with the default reserved name");if(this.taskFunctions.get(e)===this.taskFunctions.get(l))throw new Error("Cannot remove the task function used as the default task function");const t=this.taskFunctions.delete(e);return this.sendTaskFunctionsListToMainWorker(),t}listTaskFunctions(){return[...this.taskFunctions.keys()]}setDefaultTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");if(e===l)throw new Error("Cannot set the default task function reserved name as the default task function");if(!this.taskFunctions.has(e))throw new Error("Cannot set the default task function to a non-existing task function");try{return this.taskFunctions.set(l,this.taskFunctions.get(e)),!0}catch{return!1}}messageListener(e){this.checkMessageWorkerId(e),null!=e.statistics?this.statistics=e.statistics:null!=e.checkActive?e.checkActive?this.startCheckActive():this.stopCheckActive():null!=e.taskId&&null!=e.data?this.run(e):!0===e.kill&&this.handleKillMessage(e)}handleKillMessage(e){if(this.stopCheckActive(),W(this.opts.killHandler))(this.opts.killHandler?.()).then((()=>(this.sendToMainWorker({kill:"success",workerId:this.id}),null))).catch((()=>{this.sendToMainWorker({kill:"failure",workerId:this.id})})).finally((()=>{this.emitDestroy()})).catch(m);else try{this.opts.killHandler?.(),this.sendToMainWorker({kill:"success",workerId:this.id})}catch{this.sendToMainWorker({kill:"failure",workerId:this.id})}finally{this.emitDestroy()}}checkMessageWorkerId(e){if(null==e.workerId)throw new Error("Message worker id is not set");if(null!=e.workerId&&e.workerId!==this.id)throw new Error(`Message worker id ${e.workerId} does not match the worker id ${this.id}`)}startCheckActive(){this.lastTaskTimestamp=i.performance.now(),this.activeInterval=setInterval(this.checkActive.bind(this),(this.opts.maxInactiveTime??L)/2)}stopCheckActive(){null!=this.activeInterval&&(clearInterval(this.activeInterval),delete this.activeInterval)}checkActive(){i.performance.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}sendTaskFunctionsListToMainWorker(){this.sendToMainWorker({taskFunctions:this.listTaskFunctions(),workerId:this.id})}handleError(e){return e instanceof Error?e.message:e}run(e){const t=this.getTaskFunction(e.name);W(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}runSync(e,t){const{name:s,taskId:r,data:i}=t;try{let t=this.beginTaskPerformance(s);const o=e(i);t=this.endTaskPerformance(t),this.sendToMainWorker({data:o,taskPerformance:t,workerId:this.id,taskId:r})}catch(e){const t=this.handleError(e);this.sendToMainWorker({taskError:{name:s??l,message:t,data:i},workerId:this.id,taskId:r})}finally{this.updateLastTaskTimestamp()}}runAsync(e,t){const{name:s,taskId:r,data:i}=t;let o=this.beginTaskPerformance(s);e(i).then((e=>(o=this.endTaskPerformance(o),this.sendToMainWorker({data:e,taskPerformance:o,workerId:this.id,taskId:r}),null))).catch((e=>{const t=this.handleError(e);this.sendToMainWorker({taskError:{name:s??l,message:t,data:i},workerId:this.id,taskId:r})})).finally((()=>{this.updateLastTaskTimestamp()})).catch(m)}getTaskFunction(e){e=e??l;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(e){return this.checkStatistics(),{name:e??l,timestamp:i.performance.now(),...this.statistics.elu&&{elu:i.performance.eventLoopUtilization()}}}endTaskPerformance(e){return this.checkStatistics(),{...e,...this.statistics.runTime&&{runTime:i.performance.now()-e.timestamp},...this.statistics.elu&&{elu:i.performance.eventLoopUtilization(e.elu)}}}checkStatistics(){if(null==this.statistics)throw new Error("Performance statistics computation requirements not set")}updateLastTaskTimestamp(){null!=this.activeInterval&&(this.lastTaskTimestamp=i.performance.now())}}exports.ClusterWorker=class extends B{constructor(e,t={}){super("worker-cluster-pool:poolifier",s.isPrimary,s.worker,e,t)}handleReadyMessage(e){if(e.workerId===this.id&&!1===e.ready)try{this.getMainWorker()?.on("message",this.messageListener.bind(this)),this.sendTaskFunctionsListToMainWorker(),this.sendToMainWorker({ready:!0,workerId:this.id})}catch{this.sendToMainWorker({ready:!1,workerId:this.id})}}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}},exports.DynamicClusterPool=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 k.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.DynamicThreadPool=class extends D{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return k.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.FixedClusterPool=U,exports.FixedThreadPool=D,exports.KillBehaviors=x,exports.Measurements=E,exports.PoolEvents=d,exports.PoolTypes=k,exports.ThreadWorker=class extends B{port;constructor(e,s={}){super("worker-thread-pool:poolifier",t.isMainThread,t.parentPort,e,s)}handleReadyMessage(e){if(e.workerId===this.id&&!1===e.ready&&null!=e.port)try{this.port=e.port,this.port.on("message",this.messageListener.bind(this)),this.sendTaskFunctionsListToMainWorker(),this.sendToMainWorker({ready:!0,workerId:this.id})}catch{this.sendToMainWorker({ready:!1,workerId:this.id})}}handleKillMessage(e){super.handleKillMessage(e),this.port?.unref(),this.port?.close()}get id(){return t.threadId}sendToMainWorker(e){this.port.postMessage(e)}handleError(e){return e}},exports.WorkerChoiceStrategies=S,exports.WorkerTypes=F,exports.availableParallelism=()=>{let e=1;try{e=u.availableParallelism()}catch{const t=u.cpus();Array.isArray(t)&&t.length>0&&(e=t.length)}return e};
|
|
1
|
+
"use strict";var e=require("node:events"),t=require("node:worker_threads"),s=require("node:cluster"),r=require("node:crypto"),i=require("node:perf_hooks"),o=require("node:fs"),n=require("node:os"),a=require("node:async_hooks");function h(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(s){if("default"!==s){var r=Object.getOwnPropertyDescriptor(e,s);Object.defineProperty(t,s,r.get?r:{enumerable:!0,get:function(){return e[s]}})}})),t.default=e,Object.freeze(t)}var u=h(n);const k=Object.freeze({fixed:"fixed",dynamic:"dynamic"});class c extends e.EventEmitter{}const d=Object.freeze({ready:"ready",busy:"busy",full:"full",destroy:"destroy",error:"error",taskError:"taskError",backPressure:"backPressure"}),l="default",g=Object.freeze((()=>{})),m={choiceRetries:6,runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},p={aggregate:!1,average:!1,median:!1},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},f=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),T=(e,t)=>t===e,W=e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name,N=(e,t,s,r)=>{t.aggregate&&(e.aggregate=(e.aggregate??0)+s,e.minimum=Math.min(s,e.minimum??1/0),e.maximum=Math.max(s,e.maximum??-1/0),t.average&&0!==r&&(e.average=e.aggregate/r),t.median&&null!=s&&(e.history.push(s),e.median=w(e.history)))},x=Object.freeze({SOFT:"SOFT",HARD:"HARD"}),S=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 b{pool;opts;nextWorkerNodeKey=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:p,waitTime:p,elu:p};constructor(e,t=m){this.pool=e,this.opts=t,this.opts={...m,...t},this.choose=this.choose.bind(this)}setTaskStatisticsRequirements(e){this.toggleMedianMeasurementStatisticsRequirements(this.taskStatisticsRequirements.runTime,e.runTime?.median),this.toggleMedianMeasurementStatisticsRequirements(this.taskStatisticsRequirements.waitTime,e.waitTime?.median),this.toggleMedianMeasurementStatisticsRequirements(this.taskStatisticsRequirements.elu,e.elu?.median)}toggleMedianMeasurementStatisticsRequirements(e,t){e.average&&t&&(e.average=!1,e.median=t),e.median&&!t&&(e.average=!0,e.median=t)}setOptions(e){this.opts={...m,...e},this.setTaskStatisticsRequirements(this.opts)}isWorkerNodeReady(e){return this.pool.workerNodes[e].info.ready}hasWorkerNodeBackPressure(e){return this.pool.hasWorkerNodeBackPressure(e)}isWorkerNodeEligible(e){return this.isWorkerNodeReady(e)&&!this.hasWorkerNodeBackPressure(e)}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 n.cpus()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/n.cpus().length)}}class I extends b{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:p,elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.workersVirtualTaskEndTimestamp=[],!0}update(e){return this.computeWorkerVirtualTaskEndTimestamp(e),!0}choose(){return this.fairShareNextWorkerNodeKey()}remove(e){return this.workersVirtualTaskEndTimestamp.splice(e,1),!0}fairShareNextWorkerNodeKey(){let e=1/0;for(const[t]of this.pool.workerNodes.entries()){null==this.workersVirtualTaskEndTimestamp[t]&&this.computeWorkerVirtualTaskEndTimestamp(t);const s=this.workersVirtualTaskEndTimestamp[t];this.isWorkerNodeEligible(t)&&s<e&&(e=s,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}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 b{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight(),this.roundWeights=this.getRoundWeights()}reset(){return this.nextWorkerNodeKey=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.nextWorkerNodeKey;r<this.pool.workerNodes.length;r++){const i=this.opts.weights?.[r]??this.defaultWorkerWeight;if(this.isWorkerNodeEligible(r)&&i>=this.roundWeights[s]){e=s,t=r;break}}this.roundId=e??0,this.nextWorkerNodeKey=t??0;const s=this.nextWorkerNodeKey;return this.nextWorkerNodeKey===this.pool.workerNodes.length-1?(this.nextWorkerNodeKey=0,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1):this.nextWorkerNodeKey=this.nextWorkerNodeKey+1,s}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=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 b{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:p};constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastBusyNextWorkerNodeKey()}remove(){return!0}leastBusyNextWorkerNodeKey(){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(this.isWorkerNodeEligible(t)&&0===r){this.nextWorkerNodeKey=t;break}this.isWorkerNodeEligible(t)&&r<e&&(e=r,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class v extends b{constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastUsedNextWorkerNodeKey()}remove(){return!0}leastUsedNextWorkerNodeKey(){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(this.isWorkerNodeEligible(t)&&0===i){this.nextWorkerNodeKey=t;break}this.isWorkerNodeEligible(t)&&i<e&&(e=i,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class O extends b{taskStatisticsRequirements={runTime:p,waitTime:p,elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastEluNextWorkerNodeKey()}remove(){return!0}leastEluNextWorkerNodeKey(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.usage,i=r.elu?.active?.aggregate??0;if(this.isWorkerNodeEligible(t)&&0===i){this.nextWorkerNodeKey=t;break}this.isWorkerNodeEligible(t)&&i<e&&(e=i,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class z extends b{strategyPolicy={useDynamicWorker:!0};constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.nextWorkerNodeKey=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeKey;do{this.roundRobinNextWorkerNodeKey()}while(!this.isWorkerNodeEligible(this.nextWorkerNodeKey));return e}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=this.pool.workerNodes.length-1)),!0}roundRobinNextWorkerNodeKey(){return this.nextWorkerNodeKey=this.nextWorkerNodeKey===this.pool.workerNodes.length-1?0:this.nextWorkerNodeKey+1,this.nextWorkerNodeKey}}class M extends b{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:p,elu:p};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight()}reset(){return this.nextWorkerNodeKey=0,this.workerVirtualTaskRunTime=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeKey;do{this.weightedRoundRobinNextWorkerNodeKey()}while(!this.isWorkerNodeEligible(this.nextWorkerNodeKey));return e}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=this.pool.workerNodes.length-1),this.workerVirtualTaskRunTime=0),!0}weightedRoundRobinNextWorkerNodeKey(){const e=this.workerVirtualTaskRunTime;return e<(this.opts.weights?.[this.nextWorkerNodeKey]??this.defaultWorkerWeight)?this.workerVirtualTaskRunTime=e+this.getWorkerTaskRunTime(this.nextWorkerNodeKey):(this.nextWorkerNodeKey=this.nextWorkerNodeKey===this.pool.workerNodes.length-1?0:this.nextWorkerNodeKey+1,this.workerVirtualTaskRunTime=0),this.nextWorkerNodeKey}}class F{workerChoiceStrategy;opts;workerChoiceStrategies;choiceRetriesCount=0;constructor(e,t=S.ROUND_ROBIN,s=m){this.workerChoiceStrategy=t,this.opts=s,this.opts={...m,...s},this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[S.ROUND_ROBIN,new(z.bind(this))(e,s)],[S.LEAST_USED,new(v.bind(this))(e,s)],[S.LEAST_BUSY,new(R.bind(this))(e,s)],[S.LEAST_ELU,new(O.bind(this))(e,s)],[S.FAIR_SHARE,new(I.bind(this))(e,s)],[S.WEIGHTED_ROUND_ROBIN,new(M.bind(this))(e,s)],[S.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&&this.choiceRetriesCount<this.opts.choiceRetries)return this.choiceRetriesCount++,this.execute();if(null==e)throw new TypeError(`Worker node key chosen is null or undefined after ${this.choiceRetriesCount} retries`);return this.choiceRetriesCount=0,e}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).remove(e)}setOptions(e){this.opts={...m,...e};for(const t of this.workerChoiceStrategies.values())t.setOptions(e)}}class K 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=[];if(arguments.length>=3&&null!=t){if(r=super.splice(e,t,...s),this.length>this.size){const e=super.splice(0,this.length-this.size);r=new K(r.length+e.length,...r,...e)}}else r=2===arguments.length?super.splice(e,t):super.splice(e);return 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 q{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}[Symbol.iterator](){const e=this.items;let t=this.offset;return{next:()=>{if(t>=e.length)return{value:void 0,done:!0};const s=e[t];return++t,{value:s,done:!1}}}}}const P=Object.freeze({thread:"thread",cluster:"cluster"});class A{worker;info;messageChannel;usage;taskFunctionsUsage;tasksQueue;tasksQueueBackPressureSize;constructor(e,s,r){this.worker=e,this.info=this.initWorkerInfo(e,s),s===P.thread&&(this.messageChannel=new t.MessageChannel),this.usage=this.initWorkerUsage(),this.taskFunctionsUsage=new Map,this.tasksQueue=new q,this.tasksQueueBackPressureSize=Math.pow(r,2)}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()}hasBackPressure(){return this.tasksQueueSize()>=this.tasksQueueBackPressureSize}resetUsage(){this.usage=this.initWorkerUsage(),this.taskFunctionsUsage.clear()}closeChannel(){null!=this.messageChannel&&(this.messageChannel?.port1.unref(),this.messageChannel?.port2.unref(),this.messageChannel?.port1.close(),this.messageChannel?.port2.close(),delete this.messageChannel)}getTaskFunctionWorkerUsage(e){if(!Array.isArray(this.info.taskFunctions))throw new Error(`Cannot get task function worker usage for task function name '${e}' when task function names list is not yet defined`);if(Array.isArray(this.info.taskFunctions)&&this.info.taskFunctions.length<3)throw new Error(`Cannot get task function worker usage for task function name '${e}' when task function names list has less than 3 elements`);return e===l&&(e=this.info.taskFunctions[1]),this.taskFunctionsUsage.has(e)||this.taskFunctionsUsage.set(e,this.initTaskFunctionWorkerUsage(e)),this.taskFunctionsUsage.get(e)}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 K},waitTime:{history:new K},elu:{idle:{history:new K},active:{history:new K}}}}initTaskFunctionWorkerUsage(e){const t=()=>{let t=0;for(const s of this.tasksQueue)s.name===e&&++t;return t};return{tasks:{executed:0,executing:0,get queued(){return t()},failed:0},runTime:{history:new K},waitTime:{history:new K},elu:{idle:{history:new K},active:{history:new K}}}}getWorkerId(e,t){return t===P.thread?e.threadId:t===P.cluster?e.id:void 0}}class Q{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;starting;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 with the same type as the pool");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.dequeueTask=this.dequeueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new c),this.workerChoiceStrategyContext=new F(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook(),this.starting=!0,this.startPool(),this.starting=!1,this.startTimestamp=i.performance.now()}checkFilePath(e){if(null==e||"string"!=typeof e||"string"==typeof e&&0===e.trim().length)throw new Error("Please specify a file with a worker implementation");if(!o.existsSync(e))throw new Error(`Cannot find the worker file '${e}'`)}checkNumberOfWorkers(e){if(null==e)throw new Error("Cannot instantiate a pool without specifying the number of workers");if(!Number.isSafeInteger(e))throw new TypeError("Cannot instantiate a pool with a non safe integer number of workers");if(e<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===k.fixed&&0===e)throw new RangeError("Cannot instantiate a fixed pool with zero worker")}checkDynamicPoolSize(e,t){if(this.type===k.dynamic){if(null==t)throw new Error("Cannot instantiate a dynamic pool without specifying the maximum pool size");if(!Number.isSafeInteger(t))throw new TypeError("Cannot instantiate a dynamic pool with a non safe integer maximum pool size");if(e>t)throw new RangeError("Cannot instantiate a dynamic pool with a maximum pool size inferior to the minimum pool size");if(0===t)throw new RangeError("Cannot instantiate a dynamic pool with a maximum pool size equal to zero");if(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(!f(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??S.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions={...m,...e.workerChoiceStrategyOptions},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(S).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}checkValidWorkerChoiceStrategyOptions(e){if(!f(e))throw new TypeError("Invalid worker choice strategy options: must be a plain object");if(null!=e.choiceRetries&&!Number.isSafeInteger(e.choiceRetries))throw new TypeError("Invalid worker choice strategy options: choice retries must be an integer");if(null!=e.choiceRetries&&e.choiceRetries<=0)throw new RangeError(`Invalid worker choice strategy options: choice retries '${e.choiceRetries}' must be greater than zero`);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&&!f(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}' is a negative integer or zero`)}startPool(){for(;this.workerNodes.reduce(((e,t)=>t.info.dynamic?e:e+1),0)<this.numberOfWorkers;)this.createAndSetupWorkerNode()}get info(){return{version:"2.6.29",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: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),...!0===this.opts.enableTasksQueue&&{queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.queued),0)},...!0===this.opts.enableTasksQueue&&{maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.maxQueued??0)),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 ready(){return this.workerNodes.reduce(((e,t)=>!t.info.dynamic&&t.info.ready?e+1:e),0)>=this.minSize}get utilization(){const e=(i.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}checkMessageWorkerId(e){if(null==e.workerId)throw new Error("Worker message received without worker id");if(null!=e.workerId&&-1===this.getWorkerNodeKeyByWorkerId(e.workerId))throw new Error(`Worker message received from unknown worker '${e.workerId}'`)}getWorkerNodeKeyByWorker(e){return this.workerNodes.findIndex((t=>t.worker===e))}getWorkerNodeKeyByWorkerId(e){return this.workerNodes.findIndex((t=>t.info.id===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const[e,t]of this.workerNodes.entries())t.resetUsage(),this.sendStatisticsMessageToWorker(e)}setWorkerChoiceStrategyOptions(e){this.checkValidWorkerChoiceStrategyOptions(e),this.opts.workerChoiceStrategyOptions={...m,...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!0===this.opts.enableTasksQueue?-1===this.workerNodes.findIndex((e=>e.info.ready&&e.usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency)):-1===this.workerNodes.findIndex((e=>e.info.ready&&0===e.usage.tasks.executing))}listTaskFunctions(){for(const e of this.workerNodes)if(Array.isArray(e.info.taskFunctions)&&e.info.taskFunctions.length>0)return e.info.taskFunctions;return[]}async execute(e,t,s){return await new Promise(((o,n)=>{null!=t&&"string"!=typeof t&&n(new TypeError("name argument must be a string")),null!=t&&"string"==typeof t&&0===t.trim().length&&n(new TypeError("name argument must not be an empty string")),null==s||Array.isArray(s)||n(new TypeError("transferList argument must be an array"));const a=i.performance.now(),h=this.chooseWorkerNode(),u=this.getWorkerInfo(h);null!=t&&Array.isArray(u.taskFunctions)&&!u.taskFunctions.includes(t)&&n(new Error(`Task function '${t}' is not registered in the pool`));const k={name:t??l,data:e??{},transferList:s,timestamp:a,workerId:u.id,taskId:r.randomUUID()};this.promiseResponseMap.set(k.taskId,{resolve:o,reject:n,workerNodeKey:h}),!1===this.opts.enableTasksQueue||!0===this.opts.enableTasksQueue&&this.workerNodes[h].usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency?this.executeTask(h,k):this.enqueueTask(h,k),this.checkAndEmitEvents()}))}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{await this.destroyWorkerNode(t)}))),this.emitter?.emit(d.destroy)}async sendKillMessageToWorker(e,t){await new Promise(((s,r)=>{this.registerWorkerMessageListener(e,(e=>{"success"===e.kill?s():"failure"===e.kill&&r(new Error(`Worker ${t} kill message handling failed`))})),this.sendToWorker(e,{kill:!0,workerId:t})}))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;if(++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t),this.shallUpdateTaskFunctionWorkerUsage(e)){const s=this.workerNodes[e].getTaskFunctionWorkerUsage(t.name);++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t)}}afterTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;if(this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t),this.shallUpdateTaskFunctionWorkerUsage(e)){const s=this.workerNodes[e].getTaskFunctionWorkerUsage(t.taskPerformance?.name??l);this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t)}}shallUpdateTaskFunctionWorkerUsage(e){const t=this.getWorkerInfo(e);return Array.isArray(t.taskFunctions)&&t.taskFunctions.length>2}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;if(null!=s.executing&&s.executing>0)--s.executing;else if(null!=s.executing&&s.executing<0)throw new Error("Worker usage statistic for tasks executing cannot be negative");null==t.taskError?++s.executed:++s.failed}updateRunTimeWorkerUsage(e,t){N(e.runTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime,t.taskPerformance?.runTime??0,e.tasks.executed)}updateWaitTimeWorkerUsage(e,t){const s=i.performance.now(),r=s-(t.timestamp??s);N(e.waitTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime,r,e.tasks.executed)}updateEluWorkerUsage(e,t){const s=this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu;N(e.elu.active,s,t.taskPerformance?.elu?.active??0,e.tasks.executed),N(e.elu.idle,s,t.taskPerformance?.elu?.idle??0,e.tasks.executed),s.aggregate&&null!=t.taskPerformance?.elu&&(null!=e.elu.utilization?e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2:e.elu.utilization=t.taskPerformance.elu.utilization)}chooseWorkerNode(){if(this.shallCreateDynamicWorker()){const e=this.createAndSetupDynamicWorkerNode();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return e}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===k.dynamic&&!this.full&&this.internalBusy()}createAndSetupWorkerNode(){const e=this.createWorker();e.on("online",this.opts.onlineHandler??g),e.on("message",this.opts.messageHandler??g),e.on("error",this.opts.errorHandler??g),e.on("error",(t=>{const s=this.getWorkerNodeKeyByWorker(e),r=this.getWorkerInfo(s);r.ready=!1,this.workerNodes[s].closeChannel(),this.emitter?.emit(d.error,t),!0!==this.opts.restartWorkerOnError||this.starting||(r.dynamic?this.createAndSetupDynamicWorkerNode():this.createAndSetupWorkerNode()),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(s)})),e.on("exit",this.opts.exitHandler??g),e.once("exit",(()=>{this.removeWorkerNode(e)}));const t=this.addWorkerNode(e);return this.afterWorkerNodeSetup(t),t}createAndSetupDynamicWorkerNode(){const e=this.createAndSetupWorkerNode();this.registerWorkerMessageListener(e,(e=>{const t=this.getWorkerNodeKeyByWorkerId(e.workerId),s=this.workerNodes[t].usage;(T(x.HARD,e.kill)||T(x.SOFT,e.kill)&&(!1===this.opts.enableTasksQueue&&0===s.tasks.executing||!0===this.opts.enableTasksQueue&&0===s.tasks.executing&&0===this.tasksQueueSize(t)))&&this.destroyWorkerNode(t).catch((e=>{this.emitter?.emit(d.error,e)}))}));const t=this.getWorkerInfo(e);return this.sendToWorker(e,{checkActive:!0,workerId:t.id}),t.dynamic=!0,this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker&&(t.ready=!0),e}afterWorkerNodeSetup(e){this.registerWorkerMessageListener(e,this.workerListener()),this.sendStartupMessageToWorker(e),this.sendStatisticsMessageToWorker(e)}sendStatisticsMessageToWorker(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate},workerId:this.getWorkerInfo(e).id})}redistributeQueuedTasks(e){for(;this.tasksQueueSize(e)>0;){let t=e,s=1/0,r=!1;for(const[i,o]of this.workerNodes.entries()){const n=this.getWorkerInfo(i);if(i!==e&&n.ready&&0===o.usage.tasks.queued){this.workerNodes[i].usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency&&(r=!0),t=i;break}i!==e&&n.ready&&o.usage.tasks.queued<s&&(s=o.usage.tasks.queued,t=i)}r?this.executeTask(t,this.dequeueTask(e)):this.enqueueTask(t,this.dequeueTask(e))}}workerListener(){return e=>{this.checkMessageWorkerId(e),null!=e.ready&&null!=e.taskFunctions?this.handleWorkerReadyResponse(e):null!=e.taskId?this.handleTaskExecutionResponse(e):null!=e.taskFunctions&&(this.getWorkerInfo(this.getWorkerNodeKeyByWorkerId(e.workerId)).taskFunctions=e.taskFunctions)}}handleWorkerReadyResponse(e){if(!1===e.ready)throw new Error(`Worker ${e.workerId} failed to initialize`);const t=this.getWorkerInfo(this.getWorkerNodeKeyByWorkerId(e.workerId));t.ready=e.ready,t.taskFunctions=e.taskFunctions,null!=this.emitter&&this.ready&&this.emitter.emit(d.ready,this.info)}handleTaskExecutionResponse(e){const{taskId:t,taskError:s,data:r}=e,i=this.promiseResponseMap.get(t);if(null!=i){null!=s?(this.emitter?.emit(d.taskError,s),i.reject(s.message)):i.resolve(r);const o=i.workerNodeKey;this.afterTaskExecutionHook(o,e),this.promiseResponseMap.delete(t),!0===this.opts.enableTasksQueue&&this.tasksQueueSize(o)>0&&this.workerNodes[o].usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency&&this.executeTask(o,this.dequeueTask(o)),this.workerChoiceStrategyContext.update(o)}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter.emit(d.busy,this.info),this.type===k.dynamic&&this.full&&this.emitter.emit(d.full,this.info))}getWorkerInfo(e){return this.workerNodes[e].info}addWorkerNode(e){const t=new A(e,this.worker,this.maxSize);this.starting&&(t.info.ready=!0),this.workerNodes.push(t);const s=this.getWorkerNodeKeyByWorker(e);if(-1===s)throw new Error("Worker node added not found");return s}removeWorkerNode(e){const t=this.getWorkerNodeKeyByWorker(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}hasWorkerNodeBackPressure(e){return!0===this.opts.enableTasksQueue&&this.workerNodes[e].hasBackPressure()}hasBackPressure(){return!0===this.opts.enableTasksQueue&&-1!==this.workerNodes.findIndex((e=>!e.hasBackPressure()))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(e,t,t.transferList)}enqueueTask(e,t){const s=this.workerNodes[e].enqueueTask(t);return this.hasBackPressure()&&this.emitter?.emit(d.backPressure,this.info),s}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)}}class U extends Q{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}setupHook(){s.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return s.isPrimary}async destroyWorkerNode(e){this.flushTasksQueue(e);const t=this.workerNodes[e].worker,s=new Promise((e=>{t.on("exit",(()=>{e()}))}));t.on("disconnect",(()=>{t.kill()})),await this.sendKillMessageToWorker(e,t.id),t.disconnect(),await s}sendToWorker(e,t){this.workerNodes[e].worker.send(t)}sendStartupMessageToWorker(e){this.sendToWorker(e,{ready:!1,workerId:this.workerNodes[e].worker.id})}registerWorkerMessageListener(e,t){this.workerNodes[e].worker.on("message",t)}createWorker(){return s.fork(this.opts.env)}get type(){return k.fixed}get worker(){return P.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class D extends Q{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return t.isMainThread}async destroyWorkerNode(e){this.flushTasksQueue(e);const t=this.workerNodes[e],s=t.worker,r=new Promise((e=>{s.on("exit",(()=>{e()}))}));await this.sendKillMessageToWorker(e,s.threadId),t.closeChannel(),await s.terminate(),await r}sendToWorker(e,t,s){this.workerNodes[e].messageChannel.port1.postMessage(t,s)}sendStartupMessageToWorker(e){const t=this.workerNodes[e].worker,s=this.workerNodes[e].messageChannel.port2;t.postMessage({ready:!1,workerId:t.threadId,port:s},[s])}registerWorkerMessageListener(e,t){this.workerNodes[e].messageChannel.port1.on("message",t)}createWorker(){return new t.Worker(this.filePath,{env:t.SHARE_ENV,...this.opts.workerOptions})}get type(){return k.fixed}get worker(){return P.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}const B=6e4,L={killBehavior:x.SOFT,maxInactiveTime:B,killHandler:g};class _ extends a.AsyncResource{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;activeInterval;constructor(e,t,s,r,i=L){super(e),this.isMain=t,this.mainWorker=s,this.opts=i,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(r),this.isMain||this.getMainWorker()?.on("message",this.handleReadyMessage.bind(this))}checkWorkerOptions(e){this.opts={...L,...e},delete this.opts.async}checkTaskFunctions(e){if(null==e)throw new Error("taskFunctions parameter is mandatory");if(this.taskFunctions=new Map,"function"==typeof e){const t=e.bind(this);this.taskFunctions.set(l,t),this.taskFunctions.set("string"==typeof e.name&&e.name.trim().length>0?e.name:"fn1",t)}else{if(!f(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("string"!=typeof s)throw new TypeError("A taskFunctions parameter object key is not a string");if("string"==typeof s&&0===s.trim().length)throw new TypeError("A taskFunctions parameter object key an empty string");if("function"!=typeof r)throw new TypeError("A taskFunctions parameter object value is not a function");const e=r.bind(this);t&&(this.taskFunctions.set(l,e),t=!1),this.taskFunctions.set(s,e)}if(t)throw new Error("taskFunctions parameter object is empty")}}}hasTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");return this.taskFunctions.has(e)}addTaskFunction(e,t){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");if(e===l)throw new Error("Cannot add a task function with the default reserved name");if("function"!=typeof t)throw new TypeError("fn parameter is not a function");try{const s=t.bind(this);return this.taskFunctions.get(e)===this.taskFunctions.get(l)&&this.taskFunctions.set(l,s),this.taskFunctions.set(e,s),this.sendTaskFunctionsListToMainWorker(),!0}catch{return!1}}removeTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");if(e===l)throw new Error("Cannot remove the task function with the default reserved name");if(this.taskFunctions.get(e)===this.taskFunctions.get(l))throw new Error("Cannot remove the task function used as the default task function");const t=this.taskFunctions.delete(e);return this.sendTaskFunctionsListToMainWorker(),t}listTaskFunctions(){const e=[...this.taskFunctions.keys()];let t=l;for(const[e,s]of this.taskFunctions)if(e!==l&&s===this.taskFunctions.get(l)){t=e;break}return[e[e.indexOf(l)],t,...e.filter((e=>e!==l&&e!==t))]}setDefaultTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");if(e===l)throw new Error("Cannot set the default task function reserved name as the default task function");if(!this.taskFunctions.has(e))throw new Error("Cannot set the default task function to a non-existing task function");try{return this.taskFunctions.set(l,this.taskFunctions.get(e)),!0}catch{return!1}}messageListener(e){this.checkMessageWorkerId(e),null!=e.statistics?this.statistics=e.statistics:null!=e.checkActive?e.checkActive?this.startCheckActive():this.stopCheckActive():null!=e.taskId&&null!=e.data?this.run(e):!0===e.kill&&this.handleKillMessage(e)}handleKillMessage(e){if(this.stopCheckActive(),W(this.opts.killHandler))(this.opts.killHandler?.()).then((()=>(this.sendToMainWorker({kill:"success",workerId:this.id}),null))).catch((()=>{this.sendToMainWorker({kill:"failure",workerId:this.id})})).finally((()=>{this.emitDestroy()})).catch(g);else try{this.opts.killHandler?.(),this.sendToMainWorker({kill:"success",workerId:this.id})}catch{this.sendToMainWorker({kill:"failure",workerId:this.id})}finally{this.emitDestroy()}}checkMessageWorkerId(e){if(null==e.workerId)throw new Error("Message worker id is not set");if(null!=e.workerId&&e.workerId!==this.id)throw new Error(`Message worker id ${e.workerId} does not match the worker id ${this.id}`)}startCheckActive(){this.lastTaskTimestamp=i.performance.now(),this.activeInterval=setInterval(this.checkActive.bind(this),(this.opts.maxInactiveTime??B)/2)}stopCheckActive(){null!=this.activeInterval&&(clearInterval(this.activeInterval),delete this.activeInterval)}checkActive(){i.performance.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??B)&&this.sendToMainWorker({kill:this.opts.killBehavior,workerId:this.id})}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}sendTaskFunctionsListToMainWorker(){this.sendToMainWorker({taskFunctions:this.listTaskFunctions(),workerId:this.id})}handleError(e){return e instanceof Error?e.message:e}run(e){const t=this.getTaskFunction(e.name);W(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}runSync(e,t){const{name:s,taskId:r,data:i}=t;try{let t=this.beginTaskPerformance(s);const o=e(i);t=this.endTaskPerformance(t),this.sendToMainWorker({data:o,taskPerformance:t,workerId:this.id,taskId:r})}catch(e){const t=this.handleError(e);this.sendToMainWorker({taskError:{name:s??l,message:t,data:i},workerId:this.id,taskId:r})}finally{this.updateLastTaskTimestamp()}}runAsync(e,t){const{name:s,taskId:r,data:i}=t;let o=this.beginTaskPerformance(s);e(i).then((e=>(o=this.endTaskPerformance(o),this.sendToMainWorker({data:e,taskPerformance:o,workerId:this.id,taskId:r}),null))).catch((e=>{const t=this.handleError(e);this.sendToMainWorker({taskError:{name:s??l,message:t,data:i},workerId:this.id,taskId:r})})).finally((()=>{this.updateLastTaskTimestamp()})).catch(g)}getTaskFunction(e){e=e??l;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(e){return this.checkStatistics(),{name:e??l,timestamp:i.performance.now(),...this.statistics.elu&&{elu:i.performance.eventLoopUtilization()}}}endTaskPerformance(e){return this.checkStatistics(),{...e,...this.statistics.runTime&&{runTime:i.performance.now()-e.timestamp},...this.statistics.elu&&{elu:i.performance.eventLoopUtilization(e.elu)}}}checkStatistics(){if(null==this.statistics)throw new Error("Performance statistics computation requirements not set")}updateLastTaskTimestamp(){null!=this.activeInterval&&(this.lastTaskTimestamp=i.performance.now())}}exports.ClusterWorker=class extends _{constructor(e,t={}){super("worker-cluster-pool:poolifier",s.isPrimary,s.worker,e,t)}handleReadyMessage(e){if(e.workerId===this.id&&!1===e.ready)try{this.getMainWorker()?.on("message",this.messageListener.bind(this)),this.sendToMainWorker({ready:!0,taskFunctions:this.listTaskFunctions(),workerId:this.id})}catch{this.sendToMainWorker({ready:!1,taskFunctions:this.listTaskFunctions(),workerId:this.id})}}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}},exports.DynamicClusterPool=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 k.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.DynamicThreadPool=class extends D{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return k.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.FixedClusterPool=U,exports.FixedThreadPool=D,exports.KillBehaviors=x,exports.Measurements=E,exports.PoolEvents=d,exports.PoolTypes=k,exports.ThreadWorker=class extends _{port;constructor(e,s={}){super("worker-thread-pool:poolifier",t.isMainThread,t.parentPort,e,s)}handleReadyMessage(e){if(e.workerId===this.id&&!1===e.ready&&null!=e.port)try{this.port=e.port,this.port.on("message",this.messageListener.bind(this)),this.sendToMainWorker({ready:!0,taskFunctions:this.listTaskFunctions(),workerId:this.id})}catch{this.sendToMainWorker({ready:!1,taskFunctions:this.listTaskFunctions(),workerId:this.id})}}handleKillMessage(e){super.handleKillMessage(e),this.port?.unref(),this.port?.close()}get id(){return t.threadId}sendToMainWorker(e){this.port.postMessage(e)}handleError(e){return e}},exports.WorkerChoiceStrategies=S,exports.WorkerTypes=P,exports.availableParallelism=()=>{let e=1;try{e=u.availableParallelism()}catch{const t=u.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{MessageChannel as t,isMainThread as s,Worker as r,SHARE_ENV as i,parentPort as o,threadId as n}from"node:worker_threads";import a from"node:cluster";import{randomUUID as h}from"node:crypto";import{performance as u}from"node:perf_hooks";import{existsSync as k}from"node:fs";import*as c from"node:os";import{cpus as d}from"node:os";import{AsyncResource as l}from"node:async_hooks";const m=Object.freeze({fixed:"fixed",dynamic:"dynamic"});class g extends e{}const p=Object.freeze({ready:"ready",busy:"busy",full:"full",destroy:"destroy",error:"error",taskError:"taskError"}),w="default",y=Object.freeze((()=>{})),f={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},T={aggregate:!1,average:!1,median:!1},W=()=>{let e=1;try{e=c.availableParallelism()}catch{const t=c.cpus();Array.isArray(t)&&t.length>0&&(e=t.length)}return e},N=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},x=(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),E=(e,t)=>t===e,I=e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name,b=(e,t,s,r)=>{t.aggregate&&(e.aggregate=(e.aggregate??0)+s,e.minimum=Math.min(s,e.minimum??1/0),e.maximum=Math.max(s,e.maximum??-1/0),t.average&&0!==r&&(e.average=e.aggregate/r),t.median&&null!=s&&(e.history.push(s),e.median=N(e.history)))},R=Object.freeze({SOFT:"SOFT",HARD:"HARD"}),C=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LEAST_USED:"LEAST_USED",LEAST_BUSY:"LEAST_BUSY",LEAST_ELU:"LEAST_ELU",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN",INTERLEAVED_WEIGHTED_ROUND_ROBIN:"INTERLEAVED_WEIGHTED_ROUND_ROBIN"}),v=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class O{pool;opts;nextWorkerNodeKey=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:T,waitTime:T,elu:T};constructor(e,t=f){this.pool=e,this.opts=t,this.choose=this.choose.bind(this)}setTaskStatisticsRequirements(e){this.toggleMedianMeasurementStatisticsRequirements(this.taskStatisticsRequirements.runTime,e.runTime?.median),this.toggleMedianMeasurementStatisticsRequirements(this.taskStatisticsRequirements.waitTime,e.waitTime?.median),this.toggleMedianMeasurementStatisticsRequirements(this.taskStatisticsRequirements.elu,e.elu?.median)}toggleMedianMeasurementStatisticsRequirements(e,t){e.average&&t&&(e.average=!1,e.median=t),e.median&&!t&&(e.average=!0,e.median=t)}setOptions(e){this.opts=e??f,this.setTaskStatisticsRequirements(this.opts)}isWorkerNodeReady(e){return this.pool.workerNodes[e].info.ready}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 d()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/d().length)}}class M extends O{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:T,elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=f){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.workersVirtualTaskEndTimestamp=[],!0}update(e){return this.computeWorkerVirtualTaskEndTimestamp(e),!0}choose(){return this.fairShareNextWorkerNodeKey()}remove(e){return this.workersVirtualTaskEndTimestamp.splice(e,1),!0}fairShareNextWorkerNodeKey(){let e=1/0;for(const[t]of this.pool.workerNodes.entries()){null==this.workersVirtualTaskEndTimestamp[t]&&this.computeWorkerVirtualTaskEndTimestamp(t);const s=this.workersVirtualTaskEndTimestamp[t];this.isWorkerNodeReady(t)&&s<e&&(e=s,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}computeWorkerVirtualTaskEndTimestamp(e){this.workersVirtualTaskEndTimestamp[e]=this.getWorkerVirtualTaskEndTimestamp(e,this.getWorkerVirtualTaskStartTimestamp(e))}getWorkerVirtualTaskEndTimestamp(e,t){return t+(this.opts.measurement===v.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class z extends O{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=f){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight(),this.roundWeights=this.getRoundWeights()}reset(){return this.nextWorkerNodeKey=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.nextWorkerNodeKey;r<this.pool.workerNodes.length;r++){const i=this.opts.weights?.[r]??this.defaultWorkerWeight;if(this.isWorkerNodeReady(r)&&i>=this.roundWeights[s]){e=s,t=r;break}}this.roundId=e??0,this.nextWorkerNodeKey=t??0;const s=this.nextWorkerNodeKey;return this.nextWorkerNodeKey===this.pool.workerNodes.length-1?(this.nextWorkerNodeKey=0,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1):this.nextWorkerNodeKey=this.nextWorkerNodeKey+1,s}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=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 K extends O{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:T};constructor(e,t=f){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastBusyNextWorkerNodeKey()}remove(){return!0}leastBusyNextWorkerNodeKey(){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(this.isWorkerNodeReady(t)&&0===r){this.nextWorkerNodeKey=t;break}this.isWorkerNodeReady(t)&&r<e&&(e=r,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class q extends O{constructor(e,t=f){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastUsedNextWorkerNodeKey()}remove(){return!0}leastUsedNextWorkerNodeKey(){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(this.isWorkerNodeReady(t)&&0===i){this.nextWorkerNodeKey=t;break}this.isWorkerNodeReady(t)&&i<e&&(e=i,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class Q extends O{taskStatisticsRequirements={runTime:T,waitTime:T,elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=f){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastEluNextWorkerNodeKey()}remove(){return!0}leastEluNextWorkerNodeKey(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.usage,i=r.elu?.active?.aggregate??0;if(this.isWorkerNodeReady(t)&&0===i){this.nextWorkerNodeKey=t;break}this.isWorkerNodeReady(t)&&i<e&&(e=i,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class F extends O{strategyPolicy={useDynamicWorker:!0};constructor(e,t=f){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.nextWorkerNodeKey=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeKey;do{this.roundRobinNextWorkerNodeKey()}while(!this.isWorkerNodeReady(this.nextWorkerNodeKey));return e}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=this.pool.workerNodes.length-1)),!0}roundRobinNextWorkerNodeKey(){return this.nextWorkerNodeKey=this.nextWorkerNodeKey===this.pool.workerNodes.length-1?0:this.nextWorkerNodeKey+1,this.nextWorkerNodeKey}}class A extends O{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:T,elu:T};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=f){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight()}reset(){return this.nextWorkerNodeKey=0,this.workerVirtualTaskRunTime=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeKey;do{this.weightedRoundRobinNextWorkerNodeKey()}while(!this.isWorkerNodeReady(this.nextWorkerNodeKey));return e}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=this.pool.workerNodes.length-1),this.workerVirtualTaskRunTime=0),!0}weightedRoundRobinNextWorkerNodeKey(){const e=this.workerVirtualTaskRunTime;return e<(this.opts.weights?.[this.nextWorkerNodeKey]??this.defaultWorkerWeight)?this.workerVirtualTaskRunTime=e+this.getWorkerTaskRunTime(this.nextWorkerNodeKey):(this.nextWorkerNodeKey=this.nextWorkerNodeKey===this.pool.workerNodes.length-1?0:this.nextWorkerNodeKey+1,this.workerVirtualTaskRunTime=0),this.nextWorkerNodeKey}}class U{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=C.ROUND_ROBIN,s=f){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[C.ROUND_ROBIN,new(F.bind(this))(e,s)],[C.LEAST_USED,new(q.bind(this))(e,s)],[C.LEAST_BUSY,new(K.bind(this))(e,s)],[C.LEAST_ELU,new(Q.bind(this))(e,s)],[C.FAIR_SHARE,new(M.bind(this))(e,s)],[C.WEIGHTED_ROUND_ROBIN,new(A.bind(this))(e,s)],[C.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(z.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 TypeError("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 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=[];if(arguments.length>=3&&null!=t){if(r=super.splice(e,t,...s),this.length>this.size){const e=super.splice(0,this.length-this.size);r=new P(r.length+e.length,...r,...e)}}else r=2===arguments.length?super.splice(e,t):super.splice(e);return 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 D{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}[Symbol.iterator](){const e=this.items;let t=this.offset;return{next:()=>{if(t>=e.length)return{value:void 0,done:!0};const s=e[t];return++t,{value:s,done:!1}}}}}const L=Object.freeze({thread:"thread",cluster:"cluster"});class _{worker;info;usage;tasksUsage;tasksQueue;constructor(e,t){this.worker=e,this.info=this.initWorkerInfo(e,t),this.usage=this.initWorkerUsage(),this.tasksUsage=new Map,this.tasksQueue=new D}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(),this.tasksUsage.clear()}closeChannel(){null!=this.info.messageChannel&&(this.info.messageChannel?.port1.unref(),this.info.messageChannel?.port2.unref(),this.info.messageChannel?.port1.close(),this.info.messageChannel?.port2.close(),delete this.info.messageChannel)}getTaskWorkerUsage(e){return this.tasksUsage.has(e)||this.tasksUsage.set(e,this.initTaskWorkerUsage(e)),this.tasksUsage.get(e)}initWorkerInfo(e,s){return{id:this.getWorkerId(e,s),type:s,dynamic:!1,ready:!1,...s===L.thread&&{messageChannel:new t}}}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 P},waitTime:{history:new P},elu:{idle:{history:new P},active:{history:new P}}}}initTaskWorkerUsage(e){const t=()=>{let t=0;for(const s of this.tasksQueue)s.name===e&&++t;return t};return{tasks:{executed:0,executing:0,get queued(){return t()},failed:0},runTime:{history:new P},waitTime:{history:new P},elu:{idle:{history:new P},active:{history:new P}}}}getWorkerId(e,t){return t===L.thread?e.threadId:t===L.cluster?e.id:void 0}}class B{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;starting;startTimestamp;taskFunctions;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 with the same type as the pool");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.dequeueTask=this.dequeueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new g),this.workerChoiceStrategyContext=new U(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook(),this.starting=!0,this.startPool(),this.starting=!1,this.startTimestamp=u.now()}checkFilePath(e){if(null==e||"string"!=typeof e||"string"==typeof e&&0===e.trim().length)throw new Error("Please specify a file with a worker implementation");if(!k(e))throw new Error(`Cannot find the worker file '${e}'`)}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===m.fixed&&0===e)throw new RangeError("Cannot instantiate a fixed pool with zero worker")}checkDynamicPoolSize(e,t){if(this.type===m.dynamic){if(null==t)throw new Error("Cannot instantiate a dynamic pool without specifying the maximum pool size");if(!Number.isSafeInteger(t))throw new TypeError("Cannot instantiate a dynamic pool with a non safe integer maximum pool size");if(e>t)throw new RangeError("Cannot instantiate a dynamic pool with a maximum pool size inferior to the minimum pool size");if(0===t)throw new RangeError("Cannot instantiate a dynamic pool with a maximum pool size equal to zero");if(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(!S(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??C.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??f,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(C).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(v).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}'`)}startPool(){for(;this.workerNodes.reduce(((e,t)=>t.info.dynamic?e:e+1),0)<this.numberOfWorkers;)this.createAndSetupWorkerNode()}get info(){return{version:"2.6.28",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:x(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),...!0===this.opts.enableTasksQueue&&{queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.queued),0)},...!0===this.opts.enableTasksQueue&&{maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.maxQueued??0)),0)},failedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.failed),0),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&{runTime:{minimum:x(Math.min(...this.workerNodes.map((e=>e.usage.runTime?.minimum??1/0)))),maximum:x(Math.max(...this.workerNodes.map((e=>e.usage.runTime?.maximum??-1/0)))),average:x(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:x(N(this.workerNodes.map((e=>e.usage.runTime?.median??0))))}}},...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{waitTime:{minimum:x(Math.min(...this.workerNodes.map((e=>e.usage.waitTime?.minimum??1/0)))),maximum:x(Math.max(...this.workerNodes.map((e=>e.usage.waitTime?.maximum??-1/0)))),average:x(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:x(N(this.workerNodes.map((e=>e.usage.waitTime?.median??0))))}}}}}get ready(){return this.workerNodes.reduce(((e,t)=>!t.info.dynamic&&t.info.ready?e+1:e),0)>=this.minSize}get utilization(){const e=(u.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}checkMessageWorkerId(e){if(null==e.workerId)throw new Error("Worker message received without worker id");if(null!=e.workerId&&-1===this.getWorkerNodeKeyByWorkerId(e.workerId))throw new Error(`Worker message received from unknown worker '${e.workerId}'`)}getWorkerNodeKeyByWorker(e){return this.workerNodes.findIndex((t=>t.worker===e))}getWorkerNodeKeyByWorkerId(e){return this.workerNodes.findIndex((t=>t.info.id===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const[e,t]of this.workerNodes.entries())t.resetUsage(),this.sendStatisticsMessageToWorker(e)}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!0===this.opts.enableTasksQueue?-1===this.workerNodes.findIndex((e=>e.info.ready&&e.usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency)):-1===this.workerNodes.findIndex((e=>e.info.ready&&0===e.usage.tasks.executing))}listTaskFunctions(){return null!=this.taskFunctions?this.taskFunctions:[]}async execute(e,t,s){return await new Promise(((r,i)=>{null!=t&&"string"!=typeof t&&i(new TypeError("name argument must be a string")),null!=t&&"string"==typeof t&&0===t.trim().length&&i(new TypeError("name argument must not be an empty string")),null==t||null==this.taskFunctions||this.taskFunctions.includes(t)||i(new Error(`Task function '${t}' is not registered in the pool`)),null==s||Array.isArray(s)||i(new TypeError("transferList argument must be an array"));const o=u.now(),n=this.chooseWorkerNode(),a={name:t??w,data:e??{},transferList:s,timestamp:o,workerId:this.getWorkerInfo(n).id,taskId:h()};this.promiseResponseMap.set(a.taskId,{resolve:r,reject:i,workerNodeKey:n}),!1===this.opts.enableTasksQueue||!0===this.opts.enableTasksQueue&&this.workerNodes[n].usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency?this.executeTask(n,a):this.enqueueTask(n,a),this.checkAndEmitEvents()}))}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{await this.destroyWorkerNode(t)}))),this.emitter?.emit(p.destroy)}async sendKillMessageToWorker(e,t){await new Promise(((s,r)=>{this.registerWorkerMessageListener(e,(e=>{"success"===e.kill?s():"failure"===e.kill&&r(new Error(`Worker ${t} kill message handling failed`))})),this.sendToWorker(e,{kill:!0,workerId:t})}))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t);const r=this.workerNodes[e].getTaskWorkerUsage(t.name);++r.tasks.executing,this.updateWaitTimeWorkerUsage(r,t)}afterTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t);const r=this.workerNodes[e].getTaskWorkerUsage(t.taskPerformance?.name??w);this.updateTaskStatisticsWorkerUsage(r,t),this.updateRunTimeWorkerUsage(r,t),this.updateEluWorkerUsage(r,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,null==t.taskError?++s.executed:++s.failed}updateRunTimeWorkerUsage(e,t){b(e.runTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime,t.taskPerformance?.runTime??0,e.tasks.executed)}updateWaitTimeWorkerUsage(e,t){const s=u.now(),r=s-(t.timestamp??s);b(e.waitTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime,r,e.tasks.executed)}updateEluWorkerUsage(e,t){const s=this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu;b(e.elu.active,s,t.taskPerformance?.elu?.active??0,e.tasks.executed),b(e.elu.idle,s,t.taskPerformance?.elu?.idle??0,e.tasks.executed),s.aggregate&&null!=t.taskPerformance?.elu&&(null!=e.elu.utilization?e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2:e.elu.utilization=t.taskPerformance.elu.utilization)}chooseWorkerNode(){if(this.shallCreateDynamicWorker()){const e=this.createAndSetupDynamicWorkerNode();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return e}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===m.dynamic&&!this.full&&this.internalBusy()}createAndSetupWorkerNode(){const e=this.createWorker();e.on("online",this.opts.onlineHandler??y),e.on("message",this.opts.messageHandler??y),e.on("error",this.opts.errorHandler??y),e.on("error",(t=>{const s=this.getWorkerNodeKeyByWorker(e),r=this.getWorkerInfo(s);r.ready=!1,this.workerNodes[s].closeChannel(),this.emitter?.emit(p.error,t),!0!==this.opts.restartWorkerOnError||this.starting||(r.dynamic?this.createAndSetupDynamicWorkerNode():this.createAndSetupWorkerNode()),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(s)})),e.on("exit",this.opts.exitHandler??y),e.once("exit",(()=>{this.removeWorkerNode(e)}));const t=this.addWorkerNode(e);return this.afterWorkerNodeSetup(t),t}createAndSetupDynamicWorkerNode(){const e=this.createAndSetupWorkerNode();this.registerWorkerMessageListener(e,(e=>{const t=this.getWorkerNodeKeyByWorkerId(e.workerId),s=this.workerNodes[t].usage;(E(R.HARD,e.kill)||E(R.SOFT,e.kill)&&(!1===this.opts.enableTasksQueue&&0===s.tasks.executing||!0===this.opts.enableTasksQueue&&0===s.tasks.executing&&0===this.tasksQueueSize(t)))&&this.destroyWorkerNode(t).catch((e=>{this.emitter?.emit(p.error,e)}))}));const t=this.getWorkerInfo(e);return this.sendToWorker(e,{checkActive:!0,workerId:t.id}),t.dynamic=!0,this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker&&(t.ready=!0),e}afterWorkerNodeSetup(e){this.registerWorkerMessageListener(e,this.workerListener()),this.sendStartupMessageToWorker(e),this.sendStatisticsMessageToWorker(e)}sendStatisticsMessageToWorker(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate},workerId:this.getWorkerInfo(e).id})}redistributeQueuedTasks(e){for(;this.tasksQueueSize(e)>0;){let t=e,s=1/0,r=!1;for(const[i,o]of this.workerNodes.entries()){const n=this.getWorkerInfo(i);if(i!==e&&n.ready&&0===o.usage.tasks.queued){this.workerNodes[i].usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency&&(r=!0),t=i;break}i!==e&&n.ready&&o.usage.tasks.queued<s&&(s=o.usage.tasks.queued,t=i)}r?this.executeTask(t,this.dequeueTask(e)):this.enqueueTask(t,this.dequeueTask(e))}}workerListener(){return e=>{this.checkMessageWorkerId(e),null!=e.ready?this.handleWorkerReadyResponse(e):null!=e.taskId?this.handleTaskExecutionResponse(e):null!=e.taskFunctions&&(this.taskFunctions=e.taskFunctions)}}handleWorkerReadyResponse(e){if(!1===e.ready)throw new Error(`Worker ${e.workerId} failed to initialize`);this.getWorkerInfo(this.getWorkerNodeKeyByWorkerId(e.workerId)).ready=e.ready,null!=this.emitter&&this.ready&&this.emitter.emit(p.ready,this.info)}handleTaskExecutionResponse(e){const t=this.promiseResponseMap.get(e.taskId);if(null!=t){null!=e.taskError?(this.emitter?.emit(p.taskError,e.taskError),t.reject(e.taskError.message)):t.resolve(e.data);const s=t.workerNodeKey;this.afterTaskExecutionHook(s,e),this.promiseResponseMap.delete(e.taskId),!0===this.opts.enableTasksQueue&&this.tasksQueueSize(s)>0&&this.workerNodes[s].usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency&&this.executeTask(s,this.dequeueTask(s)),this.workerChoiceStrategyContext.update(s)}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter.emit(p.busy,this.info),this.type===m.dynamic&&this.full&&this.emitter.emit(p.full,this.info))}getWorkerInfo(e){return this.workerNodes[e].info}addWorkerNode(e){const t=new _(e,this.worker);this.starting&&(t.info.ready=!0),this.workerNodes.push(t);const s=this.getWorkerNodeKeyByWorker(e);if(-1===s)throw new Error("Worker node not found");return s}removeWorkerNode(e){const t=this.getWorkerNodeKeyByWorker(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(e,t,t.transferList)}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)}}class V extends B{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}setupHook(){a.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return a.isPrimary}async destroyWorkerNode(e){this.flushTasksQueue(e);const t=this.workerNodes[e].worker,s=new Promise((e=>{t.on("exit",(()=>{e()}))}));t.on("disconnect",(()=>{t.kill()})),await this.sendKillMessageToWorker(e,t.id),t.disconnect(),await s}sendToWorker(e,t){this.workerNodes[e].worker.send(t)}sendStartupMessageToWorker(e){this.sendToWorker(e,{ready:!1,workerId:this.workerNodes[e].worker.id})}registerWorkerMessageListener(e,t){this.workerNodes[e].worker.on("message",t)}createWorker(){return a.fork(this.opts.env)}get type(){return m.fixed}get worker(){return L.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class H extends V{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return m.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}class j extends B{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return s}async destroyWorkerNode(e){this.flushTasksQueue(e);const t=this.workerNodes[e],s=t.worker,r=new Promise((e=>{s.on("exit",(()=>{e()}))}));await this.sendKillMessageToWorker(e,s.threadId),t.closeChannel(),await s.terminate(),await r}sendToWorker(e,t,s){this.getWorkerInfo(e).messageChannel.port1.postMessage(t,s)}sendStartupMessageToWorker(e){const t=this.workerNodes[e].worker,s=this.getWorkerInfo(e).messageChannel.port2;t.postMessage({ready:!1,workerId:t.threadId,port:s},[s])}registerWorkerMessageListener(e,t){this.getWorkerInfo(e).messageChannel.port1.on("message",t)}createWorker(){return new r(this.filePath,{env:i,...this.opts.workerOptions})}get type(){return m.fixed}get worker(){return L.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class $ extends j{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return m.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}const G=6e4,Y=R.SOFT;class J extends l{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;activeInterval;constructor(e,t,s,r,i={killBehavior:Y,maxInactiveTime:G,killHandler:y}){super(e),this.isMain=t,this.mainWorker=s,this.opts=i,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(r),this.isMain||this.getMainWorker()?.on("message",this.handleReadyMessage.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??Y,this.opts.maxInactiveTime=e.maxInactiveTime??G,this.opts.killHandler=e.killHandler??y,delete this.opts.async}checkTaskFunctions(e){if(null==e)throw new Error("taskFunctions parameter is mandatory");if(this.taskFunctions=new Map,"function"==typeof e){const t=e.bind(this);this.taskFunctions.set(w,t),this.taskFunctions.set("string"==typeof e.name&&e.name.trim().length>0?e.name:"fn1",t)}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("string"!=typeof s)throw new TypeError("A taskFunctions parameter object key is not a string");if("string"==typeof s&&0===s.trim().length)throw new TypeError("A taskFunctions parameter object key an empty string");if("function"!=typeof r)throw new TypeError("A taskFunctions parameter object value is not a function");const e=r.bind(this);t&&(this.taskFunctions.set(w,e),t=!1),this.taskFunctions.set(s,e)}if(t)throw new Error("taskFunctions parameter object is empty")}}}hasTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");return this.taskFunctions.has(e)}addTaskFunction(e,t){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");if(e===w)throw new Error("Cannot add a task function with the default reserved name");if("function"!=typeof t)throw new TypeError("fn parameter is not a function");try{const s=t.bind(this);return this.taskFunctions.get(e)===this.taskFunctions.get(w)&&this.taskFunctions.set(w,s),this.taskFunctions.set(e,s),this.sendTaskFunctionsListToMainWorker(),!0}catch{return!1}}removeTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");if(e===w)throw new Error("Cannot remove the task function with the default reserved name");if(this.taskFunctions.get(e)===this.taskFunctions.get(w))throw new Error("Cannot remove the task function used as the default task function");const t=this.taskFunctions.delete(e);return this.sendTaskFunctionsListToMainWorker(),t}listTaskFunctions(){return[...this.taskFunctions.keys()]}setDefaultTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");if(e===w)throw new Error("Cannot set the default task function reserved name as the default task function");if(!this.taskFunctions.has(e))throw new Error("Cannot set the default task function to a non-existing task function");try{return this.taskFunctions.set(w,this.taskFunctions.get(e)),!0}catch{return!1}}messageListener(e){this.checkMessageWorkerId(e),null!=e.statistics?this.statistics=e.statistics:null!=e.checkActive?e.checkActive?this.startCheckActive():this.stopCheckActive():null!=e.taskId&&null!=e.data?this.run(e):!0===e.kill&&this.handleKillMessage(e)}handleKillMessage(e){if(this.stopCheckActive(),I(this.opts.killHandler))(this.opts.killHandler?.()).then((()=>(this.sendToMainWorker({kill:"success",workerId:this.id}),null))).catch((()=>{this.sendToMainWorker({kill:"failure",workerId:this.id})})).finally((()=>{this.emitDestroy()})).catch(y);else try{this.opts.killHandler?.(),this.sendToMainWorker({kill:"success",workerId:this.id})}catch{this.sendToMainWorker({kill:"failure",workerId:this.id})}finally{this.emitDestroy()}}checkMessageWorkerId(e){if(null==e.workerId)throw new Error("Message worker id is not set");if(null!=e.workerId&&e.workerId!==this.id)throw new Error(`Message worker id ${e.workerId} does not match the worker id ${this.id}`)}startCheckActive(){this.lastTaskTimestamp=u.now(),this.activeInterval=setInterval(this.checkActive.bind(this),(this.opts.maxInactiveTime??G)/2)}stopCheckActive(){null!=this.activeInterval&&(clearInterval(this.activeInterval),delete this.activeInterval)}checkActive(){u.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??G)&&this.sendToMainWorker({kill:this.opts.killBehavior,workerId:this.id})}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}sendTaskFunctionsListToMainWorker(){this.sendToMainWorker({taskFunctions:this.listTaskFunctions(),workerId:this.id})}handleError(e){return e instanceof Error?e.message:e}run(e){const t=this.getTaskFunction(e.name);I(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}runSync(e,t){const{name:s,taskId:r,data:i}=t;try{let t=this.beginTaskPerformance(s);const o=e(i);t=this.endTaskPerformance(t),this.sendToMainWorker({data:o,taskPerformance:t,workerId:this.id,taskId:r})}catch(e){const t=this.handleError(e);this.sendToMainWorker({taskError:{name:s??w,message:t,data:i},workerId:this.id,taskId:r})}finally{this.updateLastTaskTimestamp()}}runAsync(e,t){const{name:s,taskId:r,data:i}=t;let o=this.beginTaskPerformance(s);e(i).then((e=>(o=this.endTaskPerformance(o),this.sendToMainWorker({data:e,taskPerformance:o,workerId:this.id,taskId:r}),null))).catch((e=>{const t=this.handleError(e);this.sendToMainWorker({taskError:{name:s??w,message:t,data:i},workerId:this.id,taskId:r})})).finally((()=>{this.updateLastTaskTimestamp()})).catch(y)}getTaskFunction(e){e=e??w;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(e){return this.checkStatistics(),{name:e??w,timestamp:u.now(),...this.statistics.elu&&{elu:u.eventLoopUtilization()}}}endTaskPerformance(e){return this.checkStatistics(),{...e,...this.statistics.runTime&&{runTime:u.now()-e.timestamp},...this.statistics.elu&&{elu:u.eventLoopUtilization(e.elu)}}}checkStatistics(){if(null==this.statistics)throw new Error("Performance statistics computation requirements not set")}updateLastTaskTimestamp(){null!=this.activeInterval&&(this.lastTaskTimestamp=u.now())}}class X extends J{constructor(e,t={}){super("worker-cluster-pool:poolifier",a.isPrimary,a.worker,e,t)}handleReadyMessage(e){if(e.workerId===this.id&&!1===e.ready)try{this.getMainWorker()?.on("message",this.messageListener.bind(this)),this.sendTaskFunctionsListToMainWorker(),this.sendToMainWorker({ready:!0,workerId:this.id})}catch{this.sendToMainWorker({ready:!1,workerId:this.id})}}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}}class Z extends J{port;constructor(e,t={}){super("worker-thread-pool:poolifier",s,o,e,t)}handleReadyMessage(e){if(e.workerId===this.id&&!1===e.ready&&null!=e.port)try{this.port=e.port,this.port.on("message",this.messageListener.bind(this)),this.sendTaskFunctionsListToMainWorker(),this.sendToMainWorker({ready:!0,workerId:this.id})}catch{this.sendToMainWorker({ready:!1,workerId:this.id})}}handleKillMessage(e){super.handleKillMessage(e),this.port?.unref(),this.port?.close()}get id(){return n}sendToMainWorker(e){this.port.postMessage(e)}handleError(e){return e}}export{X as ClusterWorker,H as DynamicClusterPool,$ as DynamicThreadPool,V as FixedClusterPool,j as FixedThreadPool,R as KillBehaviors,v as Measurements,p as PoolEvents,m as PoolTypes,Z as ThreadWorker,C as WorkerChoiceStrategies,L as WorkerTypes,W as availableParallelism};
|
|
1
|
+
import{EventEmitter as e}from"node:events";import{MessageChannel as t,isMainThread as s,Worker as r,SHARE_ENV as i,parentPort as o,threadId as n}from"node:worker_threads";import a from"node:cluster";import{randomUUID as h}from"node:crypto";import{performance as u}from"node:perf_hooks";import{existsSync as k}from"node:fs";import*as c from"node:os";import{cpus as d}from"node:os";import{AsyncResource as l}from"node:async_hooks";const g=Object.freeze({fixed:"fixed",dynamic:"dynamic"});class m extends e{}const p=Object.freeze({ready:"ready",busy:"busy",full:"full",destroy:"destroy",error:"error",taskError:"taskError",backPressure:"backPressure"}),w="default",y=Object.freeze((()=>{})),f={choiceRetries:6,runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},T={aggregate:!1,average:!1,median:!1},W=()=>{let e=1;try{e=c.availableParallelism()}catch{const t=c.cpus();Array.isArray(t)&&t.length>0&&(e=t.length)}return e},N=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},x=(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),E=(e,t)=>t===e,b=e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name,I=(e,t,s,r)=>{t.aggregate&&(e.aggregate=(e.aggregate??0)+s,e.minimum=Math.min(s,e.minimum??1/0),e.maximum=Math.max(s,e.maximum??-1/0),t.average&&0!==r&&(e.average=e.aggregate/r),t.median&&null!=s&&(e.history.push(s),e.median=N(e.history)))},C=Object.freeze({SOFT:"SOFT",HARD:"HARD"}),R=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LEAST_USED:"LEAST_USED",LEAST_BUSY:"LEAST_BUSY",LEAST_ELU:"LEAST_ELU",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN",INTERLEAVED_WEIGHTED_ROUND_ROBIN:"INTERLEAVED_WEIGHTED_ROUND_ROBIN"}),v=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class z{pool;opts;nextWorkerNodeKey=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:T,waitTime:T,elu:T};constructor(e,t=f){this.pool=e,this.opts=t,this.opts={...f,...t},this.choose=this.choose.bind(this)}setTaskStatisticsRequirements(e){this.toggleMedianMeasurementStatisticsRequirements(this.taskStatisticsRequirements.runTime,e.runTime?.median),this.toggleMedianMeasurementStatisticsRequirements(this.taskStatisticsRequirements.waitTime,e.waitTime?.median),this.toggleMedianMeasurementStatisticsRequirements(this.taskStatisticsRequirements.elu,e.elu?.median)}toggleMedianMeasurementStatisticsRequirements(e,t){e.average&&t&&(e.average=!1,e.median=t),e.median&&!t&&(e.average=!0,e.median=t)}setOptions(e){this.opts={...f,...e},this.setTaskStatisticsRequirements(this.opts)}isWorkerNodeReady(e){return this.pool.workerNodes[e].info.ready}hasWorkerNodeBackPressure(e){return this.pool.hasWorkerNodeBackPressure(e)}isWorkerNodeEligible(e){return this.isWorkerNodeReady(e)&&!this.hasWorkerNodeBackPressure(e)}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 d()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/d().length)}}class O extends z{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:T,elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=f){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.workersVirtualTaskEndTimestamp=[],!0}update(e){return this.computeWorkerVirtualTaskEndTimestamp(e),!0}choose(){return this.fairShareNextWorkerNodeKey()}remove(e){return this.workersVirtualTaskEndTimestamp.splice(e,1),!0}fairShareNextWorkerNodeKey(){let e=1/0;for(const[t]of this.pool.workerNodes.entries()){null==this.workersVirtualTaskEndTimestamp[t]&&this.computeWorkerVirtualTaskEndTimestamp(t);const s=this.workersVirtualTaskEndTimestamp[t];this.isWorkerNodeEligible(t)&&s<e&&(e=s,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}computeWorkerVirtualTaskEndTimestamp(e){this.workersVirtualTaskEndTimestamp[e]=this.getWorkerVirtualTaskEndTimestamp(e,this.getWorkerVirtualTaskStartTimestamp(e))}getWorkerVirtualTaskEndTimestamp(e,t){return t+(this.opts.measurement===v.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class M extends z{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=f){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight(),this.roundWeights=this.getRoundWeights()}reset(){return this.nextWorkerNodeKey=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.nextWorkerNodeKey;r<this.pool.workerNodes.length;r++){const i=this.opts.weights?.[r]??this.defaultWorkerWeight;if(this.isWorkerNodeEligible(r)&&i>=this.roundWeights[s]){e=s,t=r;break}}this.roundId=e??0,this.nextWorkerNodeKey=t??0;const s=this.nextWorkerNodeKey;return this.nextWorkerNodeKey===this.pool.workerNodes.length-1?(this.nextWorkerNodeKey=0,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1):this.nextWorkerNodeKey=this.nextWorkerNodeKey+1,s}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=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 F extends z{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:T};constructor(e,t=f){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastBusyNextWorkerNodeKey()}remove(){return!0}leastBusyNextWorkerNodeKey(){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(this.isWorkerNodeEligible(t)&&0===r){this.nextWorkerNodeKey=t;break}this.isWorkerNodeEligible(t)&&r<e&&(e=r,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class K extends z{constructor(e,t=f){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastUsedNextWorkerNodeKey()}remove(){return!0}leastUsedNextWorkerNodeKey(){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(this.isWorkerNodeEligible(t)&&0===i){this.nextWorkerNodeKey=t;break}this.isWorkerNodeEligible(t)&&i<e&&(e=i,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class Q extends z{taskStatisticsRequirements={runTime:T,waitTime:T,elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=f){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastEluNextWorkerNodeKey()}remove(){return!0}leastEluNextWorkerNodeKey(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.usage,i=r.elu?.active?.aggregate??0;if(this.isWorkerNodeEligible(t)&&0===i){this.nextWorkerNodeKey=t;break}this.isWorkerNodeEligible(t)&&i<e&&(e=i,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class q extends z{strategyPolicy={useDynamicWorker:!0};constructor(e,t=f){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.nextWorkerNodeKey=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeKey;do{this.roundRobinNextWorkerNodeKey()}while(!this.isWorkerNodeEligible(this.nextWorkerNodeKey));return e}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=this.pool.workerNodes.length-1)),!0}roundRobinNextWorkerNodeKey(){return this.nextWorkerNodeKey=this.nextWorkerNodeKey===this.pool.workerNodes.length-1?0:this.nextWorkerNodeKey+1,this.nextWorkerNodeKey}}class A extends z{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:T,elu:T};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=f){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight()}reset(){return this.nextWorkerNodeKey=0,this.workerVirtualTaskRunTime=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeKey;do{this.weightedRoundRobinNextWorkerNodeKey()}while(!this.isWorkerNodeEligible(this.nextWorkerNodeKey));return e}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=this.pool.workerNodes.length-1),this.workerVirtualTaskRunTime=0),!0}weightedRoundRobinNextWorkerNodeKey(){const e=this.workerVirtualTaskRunTime;return e<(this.opts.weights?.[this.nextWorkerNodeKey]??this.defaultWorkerWeight)?this.workerVirtualTaskRunTime=e+this.getWorkerTaskRunTime(this.nextWorkerNodeKey):(this.nextWorkerNodeKey=this.nextWorkerNodeKey===this.pool.workerNodes.length-1?0:this.nextWorkerNodeKey+1,this.workerVirtualTaskRunTime=0),this.nextWorkerNodeKey}}class P{workerChoiceStrategy;opts;workerChoiceStrategies;choiceRetriesCount=0;constructor(e,t=R.ROUND_ROBIN,s=f){this.workerChoiceStrategy=t,this.opts=s,this.opts={...f,...s},this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[R.ROUND_ROBIN,new(q.bind(this))(e,s)],[R.LEAST_USED,new(K.bind(this))(e,s)],[R.LEAST_BUSY,new(F.bind(this))(e,s)],[R.LEAST_ELU,new(Q.bind(this))(e,s)],[R.FAIR_SHARE,new(O.bind(this))(e,s)],[R.WEIGHTED_ROUND_ROBIN,new(A.bind(this))(e,s)],[R.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(M.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&&this.choiceRetriesCount<this.opts.choiceRetries)return this.choiceRetriesCount++,this.execute();if(null==e)throw new TypeError(`Worker node key chosen is null or undefined after ${this.choiceRetriesCount} retries`);return this.choiceRetriesCount=0,e}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).remove(e)}setOptions(e){this.opts={...f,...e};for(const t of this.workerChoiceStrategies.values())t.setOptions(e)}}class U 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=[];if(arguments.length>=3&&null!=t){if(r=super.splice(e,t,...s),this.length>this.size){const e=super.splice(0,this.length-this.size);r=new U(r.length+e.length,...r,...e)}}else r=2===arguments.length?super.splice(e,t):super.splice(e);return 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 D{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}[Symbol.iterator](){const e=this.items;let t=this.offset;return{next:()=>{if(t>=e.length)return{value:void 0,done:!0};const s=e[t];return++t,{value:s,done:!1}}}}}const B=Object.freeze({thread:"thread",cluster:"cluster"});class L{worker;info;messageChannel;usage;taskFunctionsUsage;tasksQueue;tasksQueueBackPressureSize;constructor(e,s,r){this.worker=e,this.info=this.initWorkerInfo(e,s),s===B.thread&&(this.messageChannel=new t),this.usage=this.initWorkerUsage(),this.taskFunctionsUsage=new Map,this.tasksQueue=new D,this.tasksQueueBackPressureSize=Math.pow(r,2)}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()}hasBackPressure(){return this.tasksQueueSize()>=this.tasksQueueBackPressureSize}resetUsage(){this.usage=this.initWorkerUsage(),this.taskFunctionsUsage.clear()}closeChannel(){null!=this.messageChannel&&(this.messageChannel?.port1.unref(),this.messageChannel?.port2.unref(),this.messageChannel?.port1.close(),this.messageChannel?.port2.close(),delete this.messageChannel)}getTaskFunctionWorkerUsage(e){if(!Array.isArray(this.info.taskFunctions))throw new Error(`Cannot get task function worker usage for task function name '${e}' when task function names list is not yet defined`);if(Array.isArray(this.info.taskFunctions)&&this.info.taskFunctions.length<3)throw new Error(`Cannot get task function worker usage for task function name '${e}' when task function names list has less than 3 elements`);return e===w&&(e=this.info.taskFunctions[1]),this.taskFunctionsUsage.has(e)||this.taskFunctionsUsage.set(e,this.initTaskFunctionWorkerUsage(e)),this.taskFunctionsUsage.get(e)}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 U},waitTime:{history:new U},elu:{idle:{history:new U},active:{history:new U}}}}initTaskFunctionWorkerUsage(e){const t=()=>{let t=0;for(const s of this.tasksQueue)s.name===e&&++t;return t};return{tasks:{executed:0,executing:0,get queued(){return t()},failed:0},runTime:{history:new U},waitTime:{history:new U},elu:{idle:{history:new U},active:{history:new U}}}}getWorkerId(e,t){return t===B.thread?e.threadId:t===B.cluster?e.id:void 0}}class _{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;starting;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 with the same type as the pool");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.dequeueTask=this.dequeueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new m),this.workerChoiceStrategyContext=new P(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook(),this.starting=!0,this.startPool(),this.starting=!1,this.startTimestamp=u.now()}checkFilePath(e){if(null==e||"string"!=typeof e||"string"==typeof e&&0===e.trim().length)throw new Error("Please specify a file with a worker implementation");if(!k(e))throw new Error(`Cannot find the worker file '${e}'`)}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===g.fixed&&0===e)throw new RangeError("Cannot instantiate a fixed pool with zero worker")}checkDynamicPoolSize(e,t){if(this.type===g.dynamic){if(null==t)throw new Error("Cannot instantiate a dynamic pool without specifying the maximum pool size");if(!Number.isSafeInteger(t))throw new TypeError("Cannot instantiate a dynamic pool with a non safe integer maximum pool size");if(e>t)throw new RangeError("Cannot instantiate a dynamic pool with a maximum pool size inferior to the minimum pool size");if(0===t)throw new RangeError("Cannot instantiate a dynamic pool with a maximum pool size equal to zero");if(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(!S(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??R.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions={...f,...e.workerChoiceStrategyOptions},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(R).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.choiceRetries&&!Number.isSafeInteger(e.choiceRetries))throw new TypeError("Invalid worker choice strategy options: choice retries must be an integer");if(null!=e.choiceRetries&&e.choiceRetries<=0)throw new RangeError(`Invalid worker choice strategy options: choice retries '${e.choiceRetries}' must be greater than zero`);if(null!=e.weights&&Object.keys(e.weights).length!==this.maxSize)throw new Error("Invalid worker choice strategy options: must have a weight for each worker node");if(null!=e.measurement&&!Object.values(v).includes(e.measurement))throw new Error(`Invalid worker choice strategy options: invalid measurement '${e.measurement}'`)}checkValidTasksQueueOptions(e){if(null!=e&&!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}' is a negative integer or zero`)}startPool(){for(;this.workerNodes.reduce(((e,t)=>t.info.dynamic?e:e+1),0)<this.numberOfWorkers;)this.createAndSetupWorkerNode()}get info(){return{version:"2.6.29",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:x(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),...!0===this.opts.enableTasksQueue&&{queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.queued),0)},...!0===this.opts.enableTasksQueue&&{maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.maxQueued??0)),0)},failedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.failed),0),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&{runTime:{minimum:x(Math.min(...this.workerNodes.map((e=>e.usage.runTime?.minimum??1/0)))),maximum:x(Math.max(...this.workerNodes.map((e=>e.usage.runTime?.maximum??-1/0)))),average:x(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:x(N(this.workerNodes.map((e=>e.usage.runTime?.median??0))))}}},...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{waitTime:{minimum:x(Math.min(...this.workerNodes.map((e=>e.usage.waitTime?.minimum??1/0)))),maximum:x(Math.max(...this.workerNodes.map((e=>e.usage.waitTime?.maximum??-1/0)))),average:x(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:x(N(this.workerNodes.map((e=>e.usage.waitTime?.median??0))))}}}}}get ready(){return this.workerNodes.reduce(((e,t)=>!t.info.dynamic&&t.info.ready?e+1:e),0)>=this.minSize}get utilization(){const e=(u.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}checkMessageWorkerId(e){if(null==e.workerId)throw new Error("Worker message received without worker id");if(null!=e.workerId&&-1===this.getWorkerNodeKeyByWorkerId(e.workerId))throw new Error(`Worker message received from unknown worker '${e.workerId}'`)}getWorkerNodeKeyByWorker(e){return this.workerNodes.findIndex((t=>t.worker===e))}getWorkerNodeKeyByWorkerId(e){return this.workerNodes.findIndex((t=>t.info.id===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const[e,t]of this.workerNodes.entries())t.resetUsage(),this.sendStatisticsMessageToWorker(e)}setWorkerChoiceStrategyOptions(e){this.checkValidWorkerChoiceStrategyOptions(e),this.opts.workerChoiceStrategyOptions={...f,...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!0===this.opts.enableTasksQueue?-1===this.workerNodes.findIndex((e=>e.info.ready&&e.usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency)):-1===this.workerNodes.findIndex((e=>e.info.ready&&0===e.usage.tasks.executing))}listTaskFunctions(){for(const e of this.workerNodes)if(Array.isArray(e.info.taskFunctions)&&e.info.taskFunctions.length>0)return e.info.taskFunctions;return[]}async execute(e,t,s){return await new Promise(((r,i)=>{null!=t&&"string"!=typeof t&&i(new TypeError("name argument must be a string")),null!=t&&"string"==typeof t&&0===t.trim().length&&i(new TypeError("name argument must not be an empty string")),null==s||Array.isArray(s)||i(new TypeError("transferList argument must be an array"));const o=u.now(),n=this.chooseWorkerNode(),a=this.getWorkerInfo(n);null!=t&&Array.isArray(a.taskFunctions)&&!a.taskFunctions.includes(t)&&i(new Error(`Task function '${t}' is not registered in the pool`));const k={name:t??w,data:e??{},transferList:s,timestamp:o,workerId:a.id,taskId:h()};this.promiseResponseMap.set(k.taskId,{resolve:r,reject:i,workerNodeKey:n}),!1===this.opts.enableTasksQueue||!0===this.opts.enableTasksQueue&&this.workerNodes[n].usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency?this.executeTask(n,k):this.enqueueTask(n,k),this.checkAndEmitEvents()}))}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{await this.destroyWorkerNode(t)}))),this.emitter?.emit(p.destroy)}async sendKillMessageToWorker(e,t){await new Promise(((s,r)=>{this.registerWorkerMessageListener(e,(e=>{"success"===e.kill?s():"failure"===e.kill&&r(new Error(`Worker ${t} kill message handling failed`))})),this.sendToWorker(e,{kill:!0,workerId:t})}))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;if(++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t),this.shallUpdateTaskFunctionWorkerUsage(e)){const s=this.workerNodes[e].getTaskFunctionWorkerUsage(t.name);++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t)}}afterTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;if(this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t),this.shallUpdateTaskFunctionWorkerUsage(e)){const s=this.workerNodes[e].getTaskFunctionWorkerUsage(t.taskPerformance?.name??w);this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t)}}shallUpdateTaskFunctionWorkerUsage(e){const t=this.getWorkerInfo(e);return Array.isArray(t.taskFunctions)&&t.taskFunctions.length>2}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;if(null!=s.executing&&s.executing>0)--s.executing;else if(null!=s.executing&&s.executing<0)throw new Error("Worker usage statistic for tasks executing cannot be negative");null==t.taskError?++s.executed:++s.failed}updateRunTimeWorkerUsage(e,t){I(e.runTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime,t.taskPerformance?.runTime??0,e.tasks.executed)}updateWaitTimeWorkerUsage(e,t){const s=u.now(),r=s-(t.timestamp??s);I(e.waitTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime,r,e.tasks.executed)}updateEluWorkerUsage(e,t){const s=this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu;I(e.elu.active,s,t.taskPerformance?.elu?.active??0,e.tasks.executed),I(e.elu.idle,s,t.taskPerformance?.elu?.idle??0,e.tasks.executed),s.aggregate&&null!=t.taskPerformance?.elu&&(null!=e.elu.utilization?e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2:e.elu.utilization=t.taskPerformance.elu.utilization)}chooseWorkerNode(){if(this.shallCreateDynamicWorker()){const e=this.createAndSetupDynamicWorkerNode();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return e}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===g.dynamic&&!this.full&&this.internalBusy()}createAndSetupWorkerNode(){const e=this.createWorker();e.on("online",this.opts.onlineHandler??y),e.on("message",this.opts.messageHandler??y),e.on("error",this.opts.errorHandler??y),e.on("error",(t=>{const s=this.getWorkerNodeKeyByWorker(e),r=this.getWorkerInfo(s);r.ready=!1,this.workerNodes[s].closeChannel(),this.emitter?.emit(p.error,t),!0!==this.opts.restartWorkerOnError||this.starting||(r.dynamic?this.createAndSetupDynamicWorkerNode():this.createAndSetupWorkerNode()),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(s)})),e.on("exit",this.opts.exitHandler??y),e.once("exit",(()=>{this.removeWorkerNode(e)}));const t=this.addWorkerNode(e);return this.afterWorkerNodeSetup(t),t}createAndSetupDynamicWorkerNode(){const e=this.createAndSetupWorkerNode();this.registerWorkerMessageListener(e,(e=>{const t=this.getWorkerNodeKeyByWorkerId(e.workerId),s=this.workerNodes[t].usage;(E(C.HARD,e.kill)||E(C.SOFT,e.kill)&&(!1===this.opts.enableTasksQueue&&0===s.tasks.executing||!0===this.opts.enableTasksQueue&&0===s.tasks.executing&&0===this.tasksQueueSize(t)))&&this.destroyWorkerNode(t).catch((e=>{this.emitter?.emit(p.error,e)}))}));const t=this.getWorkerInfo(e);return this.sendToWorker(e,{checkActive:!0,workerId:t.id}),t.dynamic=!0,this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker&&(t.ready=!0),e}afterWorkerNodeSetup(e){this.registerWorkerMessageListener(e,this.workerListener()),this.sendStartupMessageToWorker(e),this.sendStatisticsMessageToWorker(e)}sendStatisticsMessageToWorker(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate},workerId:this.getWorkerInfo(e).id})}redistributeQueuedTasks(e){for(;this.tasksQueueSize(e)>0;){let t=e,s=1/0,r=!1;for(const[i,o]of this.workerNodes.entries()){const n=this.getWorkerInfo(i);if(i!==e&&n.ready&&0===o.usage.tasks.queued){this.workerNodes[i].usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency&&(r=!0),t=i;break}i!==e&&n.ready&&o.usage.tasks.queued<s&&(s=o.usage.tasks.queued,t=i)}r?this.executeTask(t,this.dequeueTask(e)):this.enqueueTask(t,this.dequeueTask(e))}}workerListener(){return e=>{this.checkMessageWorkerId(e),null!=e.ready&&null!=e.taskFunctions?this.handleWorkerReadyResponse(e):null!=e.taskId?this.handleTaskExecutionResponse(e):null!=e.taskFunctions&&(this.getWorkerInfo(this.getWorkerNodeKeyByWorkerId(e.workerId)).taskFunctions=e.taskFunctions)}}handleWorkerReadyResponse(e){if(!1===e.ready)throw new Error(`Worker ${e.workerId} failed to initialize`);const t=this.getWorkerInfo(this.getWorkerNodeKeyByWorkerId(e.workerId));t.ready=e.ready,t.taskFunctions=e.taskFunctions,null!=this.emitter&&this.ready&&this.emitter.emit(p.ready,this.info)}handleTaskExecutionResponse(e){const{taskId:t,taskError:s,data:r}=e,i=this.promiseResponseMap.get(t);if(null!=i){null!=s?(this.emitter?.emit(p.taskError,s),i.reject(s.message)):i.resolve(r);const o=i.workerNodeKey;this.afterTaskExecutionHook(o,e),this.promiseResponseMap.delete(t),!0===this.opts.enableTasksQueue&&this.tasksQueueSize(o)>0&&this.workerNodes[o].usage.tasks.executing<this.opts.tasksQueueOptions?.concurrency&&this.executeTask(o,this.dequeueTask(o)),this.workerChoiceStrategyContext.update(o)}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter.emit(p.busy,this.info),this.type===g.dynamic&&this.full&&this.emitter.emit(p.full,this.info))}getWorkerInfo(e){return this.workerNodes[e].info}addWorkerNode(e){const t=new L(e,this.worker,this.maxSize);this.starting&&(t.info.ready=!0),this.workerNodes.push(t);const s=this.getWorkerNodeKeyByWorker(e);if(-1===s)throw new Error("Worker node added not found");return s}removeWorkerNode(e){const t=this.getWorkerNodeKeyByWorker(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}hasWorkerNodeBackPressure(e){return!0===this.opts.enableTasksQueue&&this.workerNodes[e].hasBackPressure()}hasBackPressure(){return!0===this.opts.enableTasksQueue&&-1!==this.workerNodes.findIndex((e=>!e.hasBackPressure()))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(e,t,t.transferList)}enqueueTask(e,t){const s=this.workerNodes[e].enqueueTask(t);return this.hasBackPressure()&&this.emitter?.emit(p.backPressure,this.info),s}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)}}class V extends _{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}setupHook(){a.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return a.isPrimary}async destroyWorkerNode(e){this.flushTasksQueue(e);const t=this.workerNodes[e].worker,s=new Promise((e=>{t.on("exit",(()=>{e()}))}));t.on("disconnect",(()=>{t.kill()})),await this.sendKillMessageToWorker(e,t.id),t.disconnect(),await s}sendToWorker(e,t){this.workerNodes[e].worker.send(t)}sendStartupMessageToWorker(e){this.sendToWorker(e,{ready:!1,workerId:this.workerNodes[e].worker.id})}registerWorkerMessageListener(e,t){this.workerNodes[e].worker.on("message",t)}createWorker(){return a.fork(this.opts.env)}get type(){return g.fixed}get worker(){return B.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class j extends V{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return g.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}class H extends _{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return s}async destroyWorkerNode(e){this.flushTasksQueue(e);const t=this.workerNodes[e],s=t.worker,r=new Promise((e=>{s.on("exit",(()=>{e()}))}));await this.sendKillMessageToWorker(e,s.threadId),t.closeChannel(),await s.terminate(),await r}sendToWorker(e,t,s){this.workerNodes[e].messageChannel.port1.postMessage(t,s)}sendStartupMessageToWorker(e){const t=this.workerNodes[e].worker,s=this.workerNodes[e].messageChannel.port2;t.postMessage({ready:!1,workerId:t.threadId,port:s},[s])}registerWorkerMessageListener(e,t){this.workerNodes[e].messageChannel.port1.on("message",t)}createWorker(){return new r(this.filePath,{env:i,...this.opts.workerOptions})}get type(){return g.fixed}get worker(){return B.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class $ extends H{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return g.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}const G=6e4,Y={killBehavior:C.SOFT,maxInactiveTime:G,killHandler:y};class J extends l{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;activeInterval;constructor(e,t,s,r,i=Y){super(e),this.isMain=t,this.mainWorker=s,this.opts=i,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(r),this.isMain||this.getMainWorker()?.on("message",this.handleReadyMessage.bind(this))}checkWorkerOptions(e){this.opts={...Y,...e},delete this.opts.async}checkTaskFunctions(e){if(null==e)throw new Error("taskFunctions parameter is mandatory");if(this.taskFunctions=new Map,"function"==typeof e){const t=e.bind(this);this.taskFunctions.set(w,t),this.taskFunctions.set("string"==typeof e.name&&e.name.trim().length>0?e.name:"fn1",t)}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("string"!=typeof s)throw new TypeError("A taskFunctions parameter object key is not a string");if("string"==typeof s&&0===s.trim().length)throw new TypeError("A taskFunctions parameter object key an empty string");if("function"!=typeof r)throw new TypeError("A taskFunctions parameter object value is not a function");const e=r.bind(this);t&&(this.taskFunctions.set(w,e),t=!1),this.taskFunctions.set(s,e)}if(t)throw new Error("taskFunctions parameter object is empty")}}}hasTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");return this.taskFunctions.has(e)}addTaskFunction(e,t){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");if(e===w)throw new Error("Cannot add a task function with the default reserved name");if("function"!=typeof t)throw new TypeError("fn parameter is not a function");try{const s=t.bind(this);return this.taskFunctions.get(e)===this.taskFunctions.get(w)&&this.taskFunctions.set(w,s),this.taskFunctions.set(e,s),this.sendTaskFunctionsListToMainWorker(),!0}catch{return!1}}removeTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");if(e===w)throw new Error("Cannot remove the task function with the default reserved name");if(this.taskFunctions.get(e)===this.taskFunctions.get(w))throw new Error("Cannot remove the task function used as the default task function");const t=this.taskFunctions.delete(e);return this.sendTaskFunctionsListToMainWorker(),t}listTaskFunctions(){const e=[...this.taskFunctions.keys()];let t=w;for(const[e,s]of this.taskFunctions)if(e!==w&&s===this.taskFunctions.get(w)){t=e;break}return[e[e.indexOf(w)],t,...e.filter((e=>e!==w&&e!==t))]}setDefaultTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if("string"==typeof e&&0===e.trim().length)throw new TypeError("name parameter is an empty string");if(e===w)throw new Error("Cannot set the default task function reserved name as the default task function");if(!this.taskFunctions.has(e))throw new Error("Cannot set the default task function to a non-existing task function");try{return this.taskFunctions.set(w,this.taskFunctions.get(e)),!0}catch{return!1}}messageListener(e){this.checkMessageWorkerId(e),null!=e.statistics?this.statistics=e.statistics:null!=e.checkActive?e.checkActive?this.startCheckActive():this.stopCheckActive():null!=e.taskId&&null!=e.data?this.run(e):!0===e.kill&&this.handleKillMessage(e)}handleKillMessage(e){if(this.stopCheckActive(),b(this.opts.killHandler))(this.opts.killHandler?.()).then((()=>(this.sendToMainWorker({kill:"success",workerId:this.id}),null))).catch((()=>{this.sendToMainWorker({kill:"failure",workerId:this.id})})).finally((()=>{this.emitDestroy()})).catch(y);else try{this.opts.killHandler?.(),this.sendToMainWorker({kill:"success",workerId:this.id})}catch{this.sendToMainWorker({kill:"failure",workerId:this.id})}finally{this.emitDestroy()}}checkMessageWorkerId(e){if(null==e.workerId)throw new Error("Message worker id is not set");if(null!=e.workerId&&e.workerId!==this.id)throw new Error(`Message worker id ${e.workerId} does not match the worker id ${this.id}`)}startCheckActive(){this.lastTaskTimestamp=u.now(),this.activeInterval=setInterval(this.checkActive.bind(this),(this.opts.maxInactiveTime??G)/2)}stopCheckActive(){null!=this.activeInterval&&(clearInterval(this.activeInterval),delete this.activeInterval)}checkActive(){u.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??G)&&this.sendToMainWorker({kill:this.opts.killBehavior,workerId:this.id})}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}sendTaskFunctionsListToMainWorker(){this.sendToMainWorker({taskFunctions:this.listTaskFunctions(),workerId:this.id})}handleError(e){return e instanceof Error?e.message:e}run(e){const t=this.getTaskFunction(e.name);b(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}runSync(e,t){const{name:s,taskId:r,data:i}=t;try{let t=this.beginTaskPerformance(s);const o=e(i);t=this.endTaskPerformance(t),this.sendToMainWorker({data:o,taskPerformance:t,workerId:this.id,taskId:r})}catch(e){const t=this.handleError(e);this.sendToMainWorker({taskError:{name:s??w,message:t,data:i},workerId:this.id,taskId:r})}finally{this.updateLastTaskTimestamp()}}runAsync(e,t){const{name:s,taskId:r,data:i}=t;let o=this.beginTaskPerformance(s);e(i).then((e=>(o=this.endTaskPerformance(o),this.sendToMainWorker({data:e,taskPerformance:o,workerId:this.id,taskId:r}),null))).catch((e=>{const t=this.handleError(e);this.sendToMainWorker({taskError:{name:s??w,message:t,data:i},workerId:this.id,taskId:r})})).finally((()=>{this.updateLastTaskTimestamp()})).catch(y)}getTaskFunction(e){e=e??w;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(e){return this.checkStatistics(),{name:e??w,timestamp:u.now(),...this.statistics.elu&&{elu:u.eventLoopUtilization()}}}endTaskPerformance(e){return this.checkStatistics(),{...e,...this.statistics.runTime&&{runTime:u.now()-e.timestamp},...this.statistics.elu&&{elu:u.eventLoopUtilization(e.elu)}}}checkStatistics(){if(null==this.statistics)throw new Error("Performance statistics computation requirements not set")}updateLastTaskTimestamp(){null!=this.activeInterval&&(this.lastTaskTimestamp=u.now())}}class X extends J{constructor(e,t={}){super("worker-cluster-pool:poolifier",a.isPrimary,a.worker,e,t)}handleReadyMessage(e){if(e.workerId===this.id&&!1===e.ready)try{this.getMainWorker()?.on("message",this.messageListener.bind(this)),this.sendToMainWorker({ready:!0,taskFunctions:this.listTaskFunctions(),workerId:this.id})}catch{this.sendToMainWorker({ready:!1,taskFunctions:this.listTaskFunctions(),workerId:this.id})}}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}}class Z extends J{port;constructor(e,t={}){super("worker-thread-pool:poolifier",s,o,e,t)}handleReadyMessage(e){if(e.workerId===this.id&&!1===e.ready&&null!=e.port)try{this.port=e.port,this.port.on("message",this.messageListener.bind(this)),this.sendToMainWorker({ready:!0,taskFunctions:this.listTaskFunctions(),workerId:this.id})}catch{this.sendToMainWorker({ready:!1,taskFunctions:this.listTaskFunctions(),workerId:this.id})}}handleKillMessage(e){super.handleKillMessage(e),this.port?.unref(),this.port?.close()}get id(){return n}sendToMainWorker(e){this.port.postMessage(e)}handleError(e){return e}}export{X as ClusterWorker,j as DynamicClusterPool,$ as DynamicThreadPool,V as FixedClusterPool,H as FixedThreadPool,C as KillBehaviors,v as Measurements,p as PoolEvents,g as PoolTypes,Z as ThreadWorker,R as WorkerChoiceStrategies,B as WorkerTypes,W as availableParallelism};
|
|
@@ -41,10 +41,6 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
|
|
|
41
41
|
* The start timestamp of the pool.
|
|
42
42
|
*/
|
|
43
43
|
private readonly startTimestamp;
|
|
44
|
-
/**
|
|
45
|
-
* The task function names.
|
|
46
|
-
*/
|
|
47
|
-
private taskFunctions;
|
|
48
44
|
/**
|
|
49
45
|
* Constructs a new poolifier pool.
|
|
50
46
|
*
|
|
@@ -179,6 +175,13 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
|
|
|
179
175
|
* @param message - The received message.
|
|
180
176
|
*/
|
|
181
177
|
protected afterTaskExecutionHook(workerNodeKey: number, message: MessageValue<Response>): void;
|
|
178
|
+
/**
|
|
179
|
+
* Whether the worker node shall update its task function worker usage or not.
|
|
180
|
+
*
|
|
181
|
+
* @param workerNodeKey - The worker node key.
|
|
182
|
+
* @returns `true` if the worker node shall update its task function worker usage, `false` otherwise.
|
|
183
|
+
*/
|
|
184
|
+
private shallUpdateTaskFunctionWorkerUsage;
|
|
182
185
|
private updateTaskStatisticsWorkerUsage;
|
|
183
186
|
private updateRunTimeWorkerUsage;
|
|
184
187
|
private updateWaitTimeWorkerUsage;
|
|
@@ -280,6 +283,9 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
|
|
|
280
283
|
* @param worker - The worker.
|
|
281
284
|
*/
|
|
282
285
|
private removeWorkerNode;
|
|
286
|
+
/** @inheritDoc */
|
|
287
|
+
hasWorkerNodeBackPressure(workerNodeKey: number): boolean;
|
|
288
|
+
private hasBackPressure;
|
|
283
289
|
/**
|
|
284
290
|
* Executes the given task on the worker given its worker node key.
|
|
285
291
|
*
|
package/lib/pools/pool.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ export declare const PoolEvents: Readonly<{
|
|
|
36
36
|
readonly destroy: "destroy";
|
|
37
37
|
readonly error: "error";
|
|
38
38
|
readonly taskError: "taskError";
|
|
39
|
+
readonly backPressure: "backPressure";
|
|
39
40
|
}>;
|
|
40
41
|
/**
|
|
41
42
|
* Pool event.
|
|
@@ -156,8 +157,18 @@ export interface IPool<Worker extends IWorker, Data = unknown, Response = unknow
|
|
|
156
157
|
readonly info: PoolInfo;
|
|
157
158
|
/**
|
|
158
159
|
* Pool worker nodes.
|
|
160
|
+
*
|
|
161
|
+
* @internal
|
|
159
162
|
*/
|
|
160
163
|
readonly workerNodes: Array<IWorkerNode<Worker, Data>>;
|
|
164
|
+
/**
|
|
165
|
+
* Whether the worker node has back pressure (i.e. its tasks queue is full).
|
|
166
|
+
*
|
|
167
|
+
* @param workerNodeKey - The worker node key.
|
|
168
|
+
* @returns `true` if the worker node has back pressure, `false` otherwise.
|
|
169
|
+
* @internal
|
|
170
|
+
*/
|
|
171
|
+
readonly hasWorkerNodeBackPressure: (workerNodeKey: number) => boolean;
|
|
161
172
|
/**
|
|
162
173
|
* Emitter on which events can be listened to.
|
|
163
174
|
*
|
|
@@ -169,6 +180,7 @@ export interface IPool<Worker extends IWorker, Data = unknown, Response = unknow
|
|
|
169
180
|
* - '`destroy`': Emitted when the pool is destroyed.
|
|
170
181
|
* - `'error'`: Emitted when an uncaught error occurs.
|
|
171
182
|
* - `'taskError'`: Emitted when an error occurs while executing a task.
|
|
183
|
+
* - `'backPressure'`: Emitted when all worker nodes have back pressure (i.e. their tasks queue is full: queue size \>= pool maximum size^2).
|
|
172
184
|
*/
|
|
173
185
|
readonly emitter?: PoolEmitter;
|
|
174
186
|
/**
|
|
@@ -44,7 +44,24 @@ export declare abstract class AbstractWorkerChoiceStrategy<Worker extends IWorke
|
|
|
44
44
|
* @param workerNodeKey - The worker node key.
|
|
45
45
|
* @returns Whether the worker node is ready or not.
|
|
46
46
|
*/
|
|
47
|
-
|
|
47
|
+
private isWorkerNodeReady;
|
|
48
|
+
/**
|
|
49
|
+
* Whether the worker node has back pressure or not (i.e. its tasks queue is full).
|
|
50
|
+
*
|
|
51
|
+
* @param workerNodeKey - The worker node key.
|
|
52
|
+
* @returns `true` if the worker node has back pressure, `false` otherwise.
|
|
53
|
+
*/
|
|
54
|
+
private hasWorkerNodeBackPressure;
|
|
55
|
+
/**
|
|
56
|
+
* Whether the worker node is eligible or not.
|
|
57
|
+
* A worker node is eligible if it is ready and does not have back pressure.
|
|
58
|
+
*
|
|
59
|
+
* @param workerNodeKey - The worker node key.
|
|
60
|
+
* @returns `true` if the worker node is eligible, `false` otherwise.
|
|
61
|
+
* @see {@link isWorkerNodeReady}
|
|
62
|
+
* @see {@link hasWorkerNodeBackPressure}
|
|
63
|
+
*/
|
|
64
|
+
protected isWorkerNodeEligible(workerNodeKey: number): boolean;
|
|
48
65
|
/**
|
|
49
66
|
* Gets the worker task runtime.
|
|
50
67
|
* If the task statistics require the average runtime, the average runtime is returned.
|
|
@@ -65,7 +65,13 @@ export interface MeasurementOptions {
|
|
|
65
65
|
*/
|
|
66
66
|
export interface WorkerChoiceStrategyOptions {
|
|
67
67
|
/**
|
|
68
|
-
*
|
|
68
|
+
* Number of worker choice retries to perform if no worker is eligible.
|
|
69
|
+
*
|
|
70
|
+
* @defaultValue 6
|
|
71
|
+
*/
|
|
72
|
+
readonly choiceRetries?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Measurement to use in worker choice strategy supporting it.
|
|
69
75
|
*/
|
|
70
76
|
readonly measurement?: Measurement;
|
|
71
77
|
/**
|
|
@@ -10,7 +10,12 @@ import type { StrategyPolicy, TaskStatisticsRequirements, WorkerChoiceStrategy,
|
|
|
10
10
|
*/
|
|
11
11
|
export declare class WorkerChoiceStrategyContext<Worker extends IWorker, Data = unknown, Response = unknown> {
|
|
12
12
|
private workerChoiceStrategy;
|
|
13
|
+
private opts;
|
|
13
14
|
private readonly workerChoiceStrategies;
|
|
15
|
+
/**
|
|
16
|
+
* The number of times the worker choice strategy in the context has been retried.
|
|
17
|
+
*/
|
|
18
|
+
private choiceRetriesCount;
|
|
14
19
|
/**
|
|
15
20
|
* Worker choice strategy context constructor.
|
|
16
21
|
*
|
|
@@ -26,7 +31,7 @@ export declare class WorkerChoiceStrategyContext<Worker extends IWorker, Data =
|
|
|
26
31
|
*/
|
|
27
32
|
getStrategyPolicy(): StrategyPolicy;
|
|
28
33
|
/**
|
|
29
|
-
* Gets the worker choice strategy task statistics requirements
|
|
34
|
+
* Gets the worker choice strategy in the context task statistics requirements.
|
|
30
35
|
*
|
|
31
36
|
* @returns The task statistics requirements.
|
|
32
37
|
*/
|
|
@@ -38,13 +43,13 @@ export declare class WorkerChoiceStrategyContext<Worker extends IWorker, Data =
|
|
|
38
43
|
*/
|
|
39
44
|
setWorkerChoiceStrategy(workerChoiceStrategy: WorkerChoiceStrategy): void;
|
|
40
45
|
/**
|
|
41
|
-
* Updates the worker node key in the worker choice strategy
|
|
46
|
+
* Updates the worker node key in the worker choice strategy in the context internals.
|
|
42
47
|
*
|
|
43
48
|
* @returns `true` if the update is successful, `false` otherwise.
|
|
44
49
|
*/
|
|
45
50
|
update(workerNodeKey: number): boolean;
|
|
46
51
|
/**
|
|
47
|
-
* Executes the worker choice strategy
|
|
52
|
+
* Executes the worker choice strategy in the context algorithm.
|
|
48
53
|
*
|
|
49
54
|
* @returns The key of the worker node.
|
|
50
55
|
* @throws {@link https://nodejs.org/api/errors.html#class-error} If the worker node key is null or undefined.
|
package/lib/pools/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "2.6.
|
|
1
|
+
export declare const version = "2.6.29";
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { MessageChannel } from 'node:worker_threads';
|
|
1
3
|
import type { Task } from '../utility-types';
|
|
2
4
|
import { type IWorker, type IWorkerNode, type WorkerInfo, type WorkerType, type WorkerUsage } from './worker';
|
|
3
5
|
/**
|
|
@@ -7,18 +9,25 @@ import { type IWorker, type IWorkerNode, type WorkerInfo, type WorkerType, type
|
|
|
7
9
|
* @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
|
|
8
10
|
*/
|
|
9
11
|
export declare class WorkerNode<Worker extends IWorker, Data = unknown> implements IWorkerNode<Worker, Data> {
|
|
12
|
+
/** @inheritdoc */
|
|
10
13
|
readonly worker: Worker;
|
|
14
|
+
/** @inheritdoc */
|
|
11
15
|
readonly info: WorkerInfo;
|
|
16
|
+
/** @inheritdoc */
|
|
17
|
+
messageChannel?: MessageChannel;
|
|
18
|
+
/** @inheritdoc */
|
|
12
19
|
usage: WorkerUsage;
|
|
13
|
-
private readonly
|
|
20
|
+
private readonly taskFunctionsUsage;
|
|
14
21
|
private readonly tasksQueue;
|
|
22
|
+
private readonly tasksQueueBackPressureSize;
|
|
15
23
|
/**
|
|
16
24
|
* Constructs a new worker node.
|
|
17
25
|
*
|
|
18
26
|
* @param worker - The worker.
|
|
19
27
|
* @param workerType - The worker type.
|
|
28
|
+
* @param poolMaxSize - The pool maximum size.
|
|
20
29
|
*/
|
|
21
|
-
constructor(worker: Worker, workerType: WorkerType);
|
|
30
|
+
constructor(worker: Worker, workerType: WorkerType, poolMaxSize: number);
|
|
22
31
|
/** @inheritdoc */
|
|
23
32
|
tasksQueueSize(): number;
|
|
24
33
|
/**
|
|
@@ -34,14 +43,16 @@ export declare class WorkerNode<Worker extends IWorker, Data = unknown> implemen
|
|
|
34
43
|
/** @inheritdoc */
|
|
35
44
|
clearTasksQueue(): void;
|
|
36
45
|
/** @inheritdoc */
|
|
46
|
+
hasBackPressure(): boolean;
|
|
47
|
+
/** @inheritdoc */
|
|
37
48
|
resetUsage(): void;
|
|
38
49
|
/** @inheritdoc */
|
|
39
50
|
closeChannel(): void;
|
|
40
51
|
/** @inheritdoc */
|
|
41
|
-
|
|
52
|
+
getTaskFunctionWorkerUsage(name: string): WorkerUsage | undefined;
|
|
42
53
|
private initWorkerInfo;
|
|
43
54
|
private initWorkerUsage;
|
|
44
|
-
private
|
|
55
|
+
private initTaskFunctionWorkerUsage;
|
|
45
56
|
/**
|
|
46
57
|
* Gets the worker id.
|
|
47
58
|
*
|
package/lib/pools/worker.d.ts
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
import type { MessageChannel } from 'node:worker_threads';
|
|
3
3
|
import type { CircularArray } from '../circular-array';
|
|
4
4
|
import type { Task } from '../utility-types';
|
|
5
|
+
/**
|
|
6
|
+
* Callback invoked when the worker has started successfully.
|
|
7
|
+
*/
|
|
8
|
+
export type OnlineHandler<Worker extends IWorker> = (this: Worker) => void;
|
|
5
9
|
/**
|
|
6
10
|
* Callback invoked if the worker has received a message.
|
|
7
11
|
*/
|
|
@@ -10,10 +14,6 @@ export type MessageHandler<Worker extends IWorker> = (this: Worker, message: unk
|
|
|
10
14
|
* Callback invoked if the worker raised an error.
|
|
11
15
|
*/
|
|
12
16
|
export type ErrorHandler<Worker extends IWorker> = (this: Worker, error: Error) => void;
|
|
13
|
-
/**
|
|
14
|
-
* Callback invoked when the worker has started successfully.
|
|
15
|
-
*/
|
|
16
|
-
export type OnlineHandler<Worker extends IWorker> = (this: Worker) => void;
|
|
17
17
|
/**
|
|
18
18
|
* Callback invoked when the worker exits successfully.
|
|
19
19
|
*/
|
|
@@ -120,9 +120,9 @@ export interface WorkerInfo {
|
|
|
120
120
|
*/
|
|
121
121
|
ready: boolean;
|
|
122
122
|
/**
|
|
123
|
-
*
|
|
123
|
+
* Task function names.
|
|
124
124
|
*/
|
|
125
|
-
|
|
125
|
+
taskFunctions?: string[];
|
|
126
126
|
}
|
|
127
127
|
/**
|
|
128
128
|
* Worker usage statistics.
|
|
@@ -187,6 +187,10 @@ export interface IWorkerNode<Worker extends IWorker, Data = unknown> {
|
|
|
187
187
|
* Worker info.
|
|
188
188
|
*/
|
|
189
189
|
readonly info: WorkerInfo;
|
|
190
|
+
/**
|
|
191
|
+
* Message channel.
|
|
192
|
+
*/
|
|
193
|
+
readonly messageChannel?: MessageChannel;
|
|
190
194
|
/**
|
|
191
195
|
* Worker usage statistics.
|
|
192
196
|
*/
|
|
@@ -215,15 +219,24 @@ export interface IWorkerNode<Worker extends IWorker, Data = unknown> {
|
|
|
215
219
|
*/
|
|
216
220
|
readonly clearTasksQueue: () => void;
|
|
217
221
|
/**
|
|
218
|
-
*
|
|
222
|
+
* Whether the worker node has back pressure (i.e. its tasks queue is full).
|
|
223
|
+
*
|
|
224
|
+
* @returns `true` if the worker node has back pressure, `false` otherwise.
|
|
225
|
+
*/
|
|
226
|
+
readonly hasBackPressure: () => boolean;
|
|
227
|
+
/**
|
|
228
|
+
* Resets usage statistics.
|
|
219
229
|
*/
|
|
220
230
|
readonly resetUsage: () => void;
|
|
221
231
|
/**
|
|
222
|
-
*
|
|
232
|
+
* Closes communication channel.
|
|
223
233
|
*/
|
|
224
234
|
readonly closeChannel: () => void;
|
|
225
235
|
/**
|
|
226
|
-
* Gets task worker usage statistics.
|
|
236
|
+
* Gets task function worker usage statistics.
|
|
237
|
+
*
|
|
238
|
+
* @param name - The task function name.
|
|
239
|
+
* @returns The task function worker usage statistics if the task function worker usage statistics are initialized, `undefined` otherwise.
|
|
227
240
|
*/
|
|
228
|
-
readonly
|
|
241
|
+
readonly getTaskFunctionWorkerUsage: (name: string) => WorkerUsage | undefined;
|
|
229
242
|
}
|
|
@@ -18,7 +18,7 @@ import type { TaskAsyncFunction, TaskFunction, TaskFunctions, TaskSyncFunction }
|
|
|
18
18
|
export declare abstract class AbstractWorker<MainWorker extends Worker | MessagePort, Data = unknown, Response = unknown> extends AsyncResource {
|
|
19
19
|
protected readonly isMain: boolean;
|
|
20
20
|
private readonly mainWorker;
|
|
21
|
-
protected
|
|
21
|
+
protected opts: WorkerOptions;
|
|
22
22
|
/**
|
|
23
23
|
* Worker id.
|
|
24
24
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "poolifier",
|
|
4
|
-
"version": "2.6.
|
|
4
|
+
"version": "2.6.29",
|
|
5
5
|
"description": "Fast and small Node.js Worker_Threads and Cluster Worker Pool",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "./lib/index.js",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"@release-it/keep-a-changelog": "^4.0.0",
|
|
86
86
|
"@rollup/plugin-terser": "^0.4.3",
|
|
87
87
|
"@rollup/plugin-typescript": "^11.1.2",
|
|
88
|
-
"@types/node": "^20.5.
|
|
88
|
+
"@types/node": "^20.5.1",
|
|
89
89
|
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
|
90
90
|
"@typescript-eslint/parser": "^5.62.0",
|
|
91
91
|
"benny": "^3.7.1",
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"eslint-config-standard-with-typescript": "^37.0.0",
|
|
96
96
|
"eslint-define-config": "^1.23.0",
|
|
97
97
|
"eslint-import-resolver-typescript": "^3.6.0",
|
|
98
|
-
"eslint-plugin-import": "^2.28.
|
|
98
|
+
"eslint-plugin-import": "^2.28.1",
|
|
99
99
|
"eslint-plugin-jsdoc": "^46.4.6",
|
|
100
100
|
"eslint-plugin-n": "^16.0.1",
|
|
101
101
|
"eslint-plugin-promise": "^6.1.1",
|