poolifier 2.6.18 → 2.6.19
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/lib/index.js +1 -1
- package/lib/index.mjs +1 -1
- package/lib/pools/abstract-pool.d.ts +19 -5
- package/lib/pools/cluster/fixed.d.ts +4 -0
- package/lib/pools/thread/fixed.d.ts +4 -0
- package/lib/pools/version.d.ts +1 -1
- package/lib/pools/worker-node.d.ts +2 -0
- package/lib/pools/worker.d.ts +10 -0
- package/lib/utility-types.d.ts +6 -0
- package/lib/worker/abstract-worker.d.ts +14 -6
- package/lib/worker/cluster-worker.d.ts +2 -0
- package/lib/worker/thread-worker.d.ts +9 -0
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("node:events"),t=require("node:cluster"),r=require("node:crypto"),s=require("node:perf_hooks"),i=require("node:fs"),o=require("node:os"),a=require("node:worker_threads"),n=require("node:async_hooks");function h(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var s=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,s.get?s:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var u=h(o);const k=Object.freeze({fixed:"fixed",dynamic:"dynamic"});class c extends e.EventEmitter{}const d=Object.freeze({full:"full",ready:"ready",busy:"busy",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 r=Math.pow(10,t);return Math.round(e*r*(1+Number.EPSILON))/r},f=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),T=(e,t,r,s)=>{t.aggregate&&(e.aggregate=(e.aggregate??0)+r,e.minimum=Math.min(r,e.minimum??1/0),e.maximum=Math.max(r,e.maximum??-1/0),t.average&&0!==s&&(e.average=e.aggregate/s),t.median&&null!=r&&(e.history.push(r),e.median=w(e.history)))},W=Object.freeze({SOFT:"SOFT",HARD:"HARD"}),x=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LEAST_USED:"LEAST_USED",LEAST_BUSY:"LEAST_BUSY",LEAST_ELU:"LEAST_ELU",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN",INTERLEAVED_WEIGHTED_ROUND_ROBIN:"INTERLEAVED_WEIGHTED_ROUND_ROBIN"}),N=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class S{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 o.cpus()){const r=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,r))*Math.pow(10,r)}return Math.round(e/o.cpus().length)}}class E extends S{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 r=this.workersVirtualTaskEndTimestamp[t];this.isWorkerNodeReady(t)&&r<e&&(e=r,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===N.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class R extends S{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 r=this.roundId;r<this.roundWeights.length;r++)for(let s=this.nextWorkerNodeKey;s<this.pool.workerNodes.length;s++){const i=this.opts.weights?.[s]??this.defaultWorkerWeight;if(this.isWorkerNodeReady(s)&&i>=this.roundWeights[r]){e=r,t=s;break}}this.roundId=e??0,this.nextWorkerNodeKey=t??0;const r=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,r}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 v extends S{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,r]of this.pool.workerNodes.entries()){const s=(r.usage.runTime?.aggregate??0)+(r.usage.waitTime?.aggregate??0);if(this.isWorkerNodeReady(t)&&0===s){this.nextWorkerNodeKey=t;break}this.isWorkerNodeReady(t)&&s<e&&(e=s,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class b extends S{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,r]of this.pool.workerNodes.entries()){const s=r.usage.tasks,i=s.executed+s.executing+s.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 C extends S{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,r]of this.pool.workerNodes.entries()){const s=r.usage,i=s.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 I extends S{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 O extends S{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 z{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=x.ROUND_ROBIN,r=g){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[x.ROUND_ROBIN,new(I.bind(this))(e,r)],[x.LEAST_USED,new(b.bind(this))(e,r)],[x.LEAST_BUSY,new(v.bind(this))(e,r)],[x.LEAST_ELU,new(C.bind(this))(e,r)],[x.FAIR_SHARE,new(E.bind(this))(e,r)],[x.WEIGHTED_ROUND_ROBIN,new(O.bind(this))(e,r)],[x.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(R.bind(this))(e,r)]])}getStrategyPolicy(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).strategyPolicy}getTaskStatisticsRequirements(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).taskStatisticsRequirements}setWorkerChoiceStrategy(e){this.workerChoiceStrategy!==e&&(this.workerChoiceStrategy=e),this.workerChoiceStrategies.get(this.workerChoiceStrategy)?.reset()}update(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).update(e)}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategy).choose();if(null==e)throw new Error("Worker node key chosen is null or undefined");return e}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).remove(e)}setOptions(e){for(const t of this.workerChoiceStrategies.values())t.setOptions(e)}}class 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,...r){let s;return arguments.length>=3&&void 0!==t?(s=super.splice(e,t),this.push(...r)):s=2===arguments.length?super.splice(e,t):super.splice(e),s}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 K{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 r=e[t];return++t,{value:r,done:!1}}}}}const M=Object.freeze({cluster:"cluster",thread:"thread"});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 K}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()}getTaskWorkerUsage(e){return this.tasksUsage.has(e)||this.tasksUsage.set(e,this.initTaskWorkerUsage(e)),this.tasksUsage.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 q},waitTime:{history:new q},elu:{idle:{history:new q},active:{history:new q}}}}initTaskWorkerUsage(e){const t=()=>{let t=0;for(const r of this.tasksQueue)r.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===M.thread?e.threadId:t===M.cluster?e.id:void 0}}class P{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;starting;startTimestamp;constructor(e,t,r){if(this.numberOfWorkers=e,this.filePath=t,this.opts=r,!this.isMain())throw new Error("Cannot start a pool from a worker!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.chooseWorkerNode=this.chooseWorkerNode.bind(this),this.executeTask=this.executeTask.bind(this),this.enqueueTask=this.enqueueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new c),this.workerChoiceStrategyContext=new z(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook(),this.starting=!0,this.startPool(),this.starting=!1,this.startTimestamp=s.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(!i.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(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 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??x.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(x).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(N).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.createAndSetupWorker()}get info(){return{version:"2.6.18",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),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.queued),0),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=(s.performance.now()-this.startTimestamp)*this.maxSize;return(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)+this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0))/e}getWorkerById(e){return this.workerNodes.find((t=>t.info.id===e))?.worker}checkMessageWorkerId(e){if(null!=e.workerId&&null==this.getWorkerById(e.workerId))throw new Error(`Worker message received from unknown worker '${e.workerId}'`)}getWorkerNodeKey(e){return this.workerNodes.findIndex((t=>t.worker===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const e of this.workerNodes)e.resetUsage(),this.setWorkerStatistics(e.worker)}setWorkerChoiceStrategyOptions(e){this.checkValidWorkerChoiceStrategyOptions(e),this.opts.workerChoiceStrategyOptions=e,this.workerChoiceStrategyContext.setOptions(this.opts.workerChoiceStrategyOptions)}enableTasksQueue(e,t){!0!==this.opts.enableTasksQueue||e||this.flushTasksQueues(),this.opts.enableTasksQueue=e,this.setTasksQueueOptions(t)}setTasksQueueOptions(e){!0===this.opts.enableTasksQueue?(this.checkValidTasksQueueOptions(e),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e)):null!=this.opts.tasksQueueOptions&&delete this.opts.tasksQueueOptions}buildTasksQueueOptions(e){return{concurrency:e?.concurrency??1}}get full(){return this.workerNodes.length>=this.maxSize}internalBusy(){return-1===this.workerNodes.findIndex((e=>0===e.usage.tasks.executing))}async execute(e,t){const i=s.performance.now(),o=this.chooseWorkerNode(),a={name:t??l,data:e??{},timestamp:i,workerId:this.getWorkerInfo(o).id,id:r.randomUUID()},n=new Promise(((e,t)=>{this.promiseResponseMap.set(a.id,{resolve:e,reject:t,worker:this.workerNodes[o].worker})}));return!0===this.opts.enableTasksQueue&&(this.busy||this.workerNodes[o].usage.tasks.executing>=this.opts.tasksQueueOptions.concurrency)?this.enqueueTask(o,a):this.executeTask(o,a),this.checkAndEmitEvents(),n}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{this.flushTasksQueue(t);const r=new Promise((t=>{e.worker.on("exit",(()=>{t()}))}));await this.destroyWorker(e.worker),await r})))}setupHook(){}beforeTaskExecutionHook(e,t){const r=this.workerNodes[e].usage;++r.tasks.executing,this.updateWaitTimeWorkerUsage(r,t);const s=this.workerNodes[e].getTaskWorkerUsage(t.name);++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t)}afterTaskExecutionHook(e,t){const r=this.getWorkerNodeKey(e),s=this.workerNodes[r].usage;this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t);const i=this.workerNodes[r].getTaskWorkerUsage(t.taskPerformance?.name??l);this.updateTaskStatisticsWorkerUsage(i,t),this.updateRunTimeWorkerUsage(i,t),this.updateEluWorkerUsage(i,t)}updateTaskStatisticsWorkerUsage(e,t){const r=e.tasks;--r.executing,null==t.taskError?++r.executed:++r.failed}updateRunTimeWorkerUsage(e,t){T(e.runTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime,t.taskPerformance?.runTime??0,e.tasks.executed)}updateWaitTimeWorkerUsage(e,t){const r=s.performance.now(),i=r-(t.timestamp??r);T(e.waitTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime,i,e.tasks.executed)}updateEluWorkerUsage(e,t){const r=this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu;T(e.elu.active,r,t.taskPerformance?.elu?.active??0,e.tasks.executed),T(e.elu.idle,r,t.taskPerformance?.elu?.idle??0,e.tasks.executed),r.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.createAndSetupDynamicWorker();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return this.getWorkerNodeKey(e)}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===k.dynamic&&!this.full&&this.internalBusy()}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??m),e.on("error",this.opts.errorHandler??m),e.on("error",(t=>{const r=this.getWorkerNodeKey(e),s=this.getWorkerInfo(r);s.ready=!1,this.emitter?.emit(d.error,t),!0!==this.opts.restartWorkerOnError||this.starting||(s.dynamic?this.createAndSetupDynamicWorker():this.createAndSetupWorker()),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(r)})),e.on("online",this.opts.onlineHandler??m),e.on("exit",this.opts.exitHandler??m),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.addWorkerNode(e),this.afterWorkerSetup(e),e}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();this.registerWorkerMessageListener(e,(t=>{const r=this.getWorkerNodeKey(e);var s;s=W.HARD,(t.kill===s||null!=t.kill&&(!1===this.opts.enableTasksQueue&&0===this.workerNodes[r].usage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[r].usage.tasks.executing&&0===this.tasksQueueSize(r)))&&this.destroyWorker(e)}));const t=this.getWorkerInfo(this.getWorkerNodeKey(e));return t.dynamic=!0,this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker&&(t.ready=!0),this.sendToWorker(e,{checkActive:!0,workerId:t.id}),e}registerWorkerMessageListener(e,t){e.on("message",t)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,this.workerListener()),this.sendWorkerStartupMessage(e),this.setWorkerStatistics(e)}sendWorkerStartupMessage(e){this.sendToWorker(e,{ready:!1,workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id})}redistributeQueuedTasks(e){for(;this.tasksQueueSize(e)>0;){let t=e,r=1/0;for(const[s,i]of this.workerNodes.entries()){const o=this.getWorkerInfo(s);if(s!==e&&o.ready&&0===i.usage.tasks.queued){t=s;break}s!==e&&o.ready&&i.usage.tasks.queued<r&&(r=i.usage.tasks.queued,t=s)}this.enqueueTask(t,this.dequeueTask(e))}}workerListener(){return e=>{this.checkMessageWorkerId(e),null!=e.ready?this.handleWorkerReadyResponse(e):null!=e.id&&this.handleTaskExecutionResponse(e)}}handleWorkerReadyResponse(e){const t=this.getWorkerById(e.workerId);this.getWorkerInfo(this.getWorkerNodeKey(t)).ready=e.ready,null!=this.emitter&&this.ready&&this.emitter.emit(d.ready,this.info)}handleTaskExecutionResponse(e){const t=this.promiseResponseMap.get(e.id);if(null!=t){null!=e.taskError?(this.emitter?.emit(d.taskError,e.taskError),t.reject(e.taskError.message)):t.resolve(e.data),this.afterTaskExecutionHook(t.worker,e),this.promiseResponseMap.delete(e.id);const r=this.getWorkerNodeKey(t.worker);!0===this.opts.enableTasksQueue&&this.tasksQueueSize(r)>0&&this.executeTask(r,this.dequeueTask(r)),this.workerChoiceStrategyContext.update(r)}}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);return this.starting&&(t.info.ready=!0),this.workerNodes.push(t)}removeWorkerNode(e){const t=this.getWorkerNodeKey(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(this.workerNodes[e].worker,t)}enqueueTask(e,t){return this.workerNodes[e].enqueueTask(t)}dequeueTask(e){return this.workerNodes[e].dequeueTask()}tasksQueueSize(e){return this.workerNodes[e].tasksQueueSize()}flushTasksQueue(e){for(;this.tasksQueueSize(e)>0;)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].clearTasksQueue()}flushTasksQueues(){for(const[e]of this.workerNodes.entries())this.flushTasksQueue(e)}setWorkerStatistics(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate},workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id})}}class Q extends P{opts;constructor(e,t,r={}){super(e,t,r),this.opts=r}setupHook(){t.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return t.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:!0,workerId:e.id}),e.on("disconnect",(()=>{e.kill()})),e.disconnect()}sendToWorker(e,t){e.send(t)}createWorker(){return t.fork(this.opts.env)}get type(){return k.fixed}get worker(){return M.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class U extends P{opts;constructor(e,t,r={}){super(e,t,r),this.opts=r}isMain(){return a.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:!0,workerId:e.threadId}),await e.terminate()}sendToWorker(e,t){e.postMessage(t)}createWorker(){return new a.Worker(this.filePath,{env:a.SHARE_ENV,...this.opts.workerOptions})}get type(){return k.fixed}get worker(){return M.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}const D=6e4,F=W.SOFT;class _ extends n.AsyncResource{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;activeInterval;constructor(e,t,r,s,i={killBehavior:F,maxInactiveTime:D}){super(e),this.isMain=t,this.mainWorker=s,this.opts=i,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(r),this.isMain||this.mainWorker?.on("message",this.messageListener.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??F,this.opts.maxInactiveTime=e.maxInactiveTime??D,delete this.opts.async}checkTaskFunctions(e){if(null==e)throw new Error("taskFunctions parameter is mandatory");if(this.taskFunctions=new Map,"function"==typeof e){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[r,s]of Object.entries(e)){if("string"!=typeof r)throw new TypeError("A taskFunctions parameter object key is not a string");if("function"!=typeof s)throw new TypeError("A taskFunctions parameter object value is not a function");const e=s.bind(this);t&&(this.taskFunctions.set(l,e),t=!1),this.taskFunctions.set(r,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");return this.taskFunctions.has(e)}addTaskFunction(e,t){if("string"!=typeof e)throw new TypeError("name parameter is not a 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 r=t.bind(this);return this.taskFunctions.get(e)===this.taskFunctions.get(l)&&this.taskFunctions.set(l,r),this.taskFunctions.set(e,r),!0}catch{return!1}}removeTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a 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");return this.taskFunctions.delete(e)}listTaskFunctions(){return Array.from(this.taskFunctions.keys())}setDefaultTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a 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){e.workerId===this.id&&(null!=e.ready?this.sendReadyResponse():null!=e.statistics?this.statistics=e.statistics:null!=e.checkActive?!this.isMain&&e.checkActive?this.startCheckActive():this.stopCheckActive():null!=e.id&&null!=e.data?this.run(e):!0===e.kill&&(!this.isMain&&this.stopCheckActive(),this.emitDestroy()))}sendReadyResponse(){!this.isMain&&this.sendToMainWorker({ready:!0,workerId:this.id})}startCheckActive(){this.lastTaskTimestamp=s.performance.now(),this.activeInterval=setInterval(this.checkActive.bind(this),(this.opts.maxInactiveTime??D)/2).unref()}stopCheckActive(){null!=this.activeInterval&&(clearInterval(this.activeInterval),delete this.activeInterval)}checkActive(){s.performance.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??D)&&this.sendToMainWorker({kill:this.opts.killBehavior,workerId:this.id})}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}handleError(e){return e instanceof Error?e.message:e}run(e){if(this.isMain)throw new Error("Cannot run a task in the main worker");const t=this.getTaskFunction(e.name);(e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name)(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}runSync(e,t){try{let r=this.beginTaskPerformance(t.name);const s=e(t.data);r=this.endTaskPerformance(r),this.sendToMainWorker({data:s,taskPerformance:r,workerId:this.id,id:t.id})}catch(e){const r=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??l,message:r,data:t.data},workerId:this.id,id:t.id})}finally{this.updateLastTaskTimestamp()}}runAsync(e,t){let r=this.beginTaskPerformance(t.name);e(t.data).then((e=>(r=this.endTaskPerformance(r),this.sendToMainWorker({data:e,taskPerformance:r,workerId:this.id,id:t.id}),null))).catch((e=>{const r=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??l,message:r,data:t.data},workerId:this.id,id:t.id})})).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:s.performance.now(),...this.statistics.elu&&{elu:s.performance.eventLoopUtilization()}}}endTaskPerformance(e){return this.checkStatistics(),{...e,...this.statistics.runTime&&{runTime:s.performance.now()-e.timestamp},...this.statistics.elu&&{elu:s.performance.eventLoopUtilization(e.elu)}}}checkStatistics(){if(null==this.statistics)throw new Error("Performance statistics computation requirements not set")}updateLastTaskTimestamp(){this.isMain||null==this.activeInterval||(this.lastTaskTimestamp=s.performance.now())}}exports.ClusterWorker=class extends _{constructor(e,r={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,r)}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}},exports.DynamicClusterPool=class extends Q{max;constructor(e,t,r,s={}){super(e,r,s),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 U{max;constructor(e,t,r,s={}){super(e,r,s),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=Q,exports.FixedThreadPool=U,exports.KillBehaviors=W,exports.Measurements=N,exports.PoolEvents=d,exports.PoolTypes=k,exports.ThreadWorker=class extends _{constructor(e,t={}){super("worker-thread-pool:poolifier",a.isMainThread,e,a.parentPort,t)}get id(){return a.threadId}sendToMainWorker(e){this.getMainWorker().postMessage(e)}handleError(e){return e}},exports.WorkerChoiceStrategies=x,exports.WorkerTypes=M,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:cluster"),s=require("node:crypto"),r=require("node:perf_hooks"),i=require("node:fs"),o=require("node:os"),n=require("node:worker_threads"),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(o);const k=Object.freeze({fixed:"fixed",dynamic:"dynamic"});class c extends e.EventEmitter{}const d=Object.freeze({full:"full",ready:"ready",busy:"busy",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,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)))},W=Object.freeze({SOFT:"SOFT",HARD:"HARD"}),x=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LEAST_USED:"LEAST_USED",LEAST_BUSY:"LEAST_BUSY",LEAST_ELU:"LEAST_ELU",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN",INTERLEAVED_WEIGHTED_ROUND_ROBIN:"INTERLEAVED_WEIGHTED_ROUND_ROBIN"}),N=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class S{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 o.cpus()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/o.cpus().length)}}class E extends S{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===N.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class v extends S{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 S{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 C extends S{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 b extends S{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 I extends S{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 O extends S{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 z{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=x.ROUND_ROBIN,s=g){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[x.ROUND_ROBIN,new(I.bind(this))(e,s)],[x.LEAST_USED,new(C.bind(this))(e,s)],[x.LEAST_BUSY,new(R.bind(this))(e,s)],[x.LEAST_ELU,new(b.bind(this))(e,s)],[x.FAIR_SHARE,new(E.bind(this))(e,s)],[x.WEIGHTED_ROUND_ROBIN,new(O.bind(this))(e,s)],[x.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(v.bind(this))(e,s)]])}getStrategyPolicy(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).strategyPolicy}getTaskStatisticsRequirements(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).taskStatisticsRequirements}setWorkerChoiceStrategy(e){this.workerChoiceStrategy!==e&&(this.workerChoiceStrategy=e),this.workerChoiceStrategies.get(this.workerChoiceStrategy)?.reset()}update(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).update(e)}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategy).choose();if(null==e)throw new Error("Worker node key chosen is null or undefined");return e}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).remove(e)}setOptions(e){for(const t of this.workerChoiceStrategies.values())t.setOptions(e)}}class M extends Array{size;constructor(e=1024,...t){super(),this.checkSize(e),this.size=e,arguments.length>1&&this.push(...t)}push(...e){const t=super.push(...e);return t>this.size&&super.splice(0,t-this.size),this.length}unshift(...e){return super.unshift(...e)>this.size&&super.splice(this.size,e.length),this.length}concat(...e){const t=super.concat(e);return t.size=this.size,t.length>t.size&&t.splice(0,t.length-t.size),t}splice(e,t,...s){let r;return arguments.length>=3&&void 0!==t?(r=super.splice(e,t),this.push(...s)):r=2===arguments.length?super.splice(e,t):super.splice(e),r}resize(e){if(this.checkSize(e),0===e)this.length=0;else if(e<this.size)for(let t=e;t<this.size;t++)super.pop();this.size=e}empty(){return 0===this.length}full(){return this.length===this.size}checkSize(e){if(!Number.isSafeInteger(e))throw new TypeError(`Invalid circular array size: ${e} is not a safe integer`);if(e<0)throw new RangeError(`Invalid circular array size: ${e} < 0`)}}class 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 K=Object.freeze({cluster:"cluster",thread:"thread"});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,t){return{id:this.getWorkerId(e,t),type:t,dynamic:!1,ready:!1,...t===K.thread&&{messageChannel:new n.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 M},waitTime:{history:new M},elu:{idle:{history:new M},active:{history:new M}}}}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 M},waitTime:{history:new M},elu:{idle:{history:new M},active:{history:new M}}}}getWorkerId(e,t){return t===K.thread?e.threadId:t===K.cluster?e.id:void 0}}class P{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!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.chooseWorkerNode=this.chooseWorkerNode.bind(this),this.executeTask=this.executeTask.bind(this),this.enqueueTask=this.enqueueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new c),this.workerChoiceStrategyContext=new z(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook(),this.starting=!0,this.startPool(),this.starting=!1,this.startTimestamp=r.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(!i.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(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 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??x.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(x).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(N).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.createAndSetupWorker()}get info(){return{version:"2.6.19",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),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.queued),0),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=(r.performance.now()-this.startTimestamp)*this.maxSize;return(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)+this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0))/e}getWorkerById(e){return this.workerNodes.find((t=>t.info.id===e))?.worker}checkMessageWorkerId(e){if(null!=e.workerId&&null==this.getWorkerById(e.workerId))throw new Error(`Worker message received from unknown worker '${e.workerId}'`)}getWorkerNodeKey(e){return this.workerNodes.findIndex((t=>t.worker===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const e of this.workerNodes)e.resetUsage(),this.setWorkerStatistics(e.worker)}setWorkerChoiceStrategyOptions(e){this.checkValidWorkerChoiceStrategyOptions(e),this.opts.workerChoiceStrategyOptions=e,this.workerChoiceStrategyContext.setOptions(this.opts.workerChoiceStrategyOptions)}enableTasksQueue(e,t){!0!==this.opts.enableTasksQueue||e||this.flushTasksQueues(),this.opts.enableTasksQueue=e,this.setTasksQueueOptions(t)}setTasksQueueOptions(e){!0===this.opts.enableTasksQueue?(this.checkValidTasksQueueOptions(e),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e)):null!=this.opts.tasksQueueOptions&&delete this.opts.tasksQueueOptions}buildTasksQueueOptions(e){return{concurrency:e?.concurrency??1}}get full(){return this.workerNodes.length>=this.maxSize}internalBusy(){return-1===this.workerNodes.findIndex((e=>0===e.usage.tasks.executing))}async execute(e,t){const i=r.performance.now(),o=this.chooseWorkerNode(),n={name:t??l,data:e??{},timestamp:i,workerId:this.getWorkerInfo(o).id,id:s.randomUUID()},a=new Promise(((e,t)=>{this.promiseResponseMap.set(n.id,{resolve:e,reject:t,worker:this.workerNodes[o].worker})}));return!0===this.opts.enableTasksQueue&&(this.busy||this.workerNodes[o].usage.tasks.executing>=this.opts.tasksQueueOptions.concurrency)?this.enqueueTask(o,n):this.executeTask(o,n),this.checkAndEmitEvents(),a}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{this.flushTasksQueue(t);const s=new Promise((t=>{e.worker.on("exit",(()=>{t()}))}));await this.destroyWorker(e.worker),await s})))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t);const r=this.workerNodes[e].getTaskWorkerUsage(t.name);++r.tasks.executing,this.updateWaitTimeWorkerUsage(r,t)}afterTaskExecutionHook(e,t){const s=this.getWorkerNodeKey(e),r=this.workerNodes[s].usage;this.updateTaskStatisticsWorkerUsage(r,t),this.updateRunTimeWorkerUsage(r,t),this.updateEluWorkerUsage(r,t);const i=this.workerNodes[s].getTaskWorkerUsage(t.taskPerformance?.name??l);this.updateTaskStatisticsWorkerUsage(i,t),this.updateRunTimeWorkerUsage(i,t),this.updateEluWorkerUsage(i,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,null==t.taskError?++s.executed:++s.failed}updateRunTimeWorkerUsage(e,t){T(e.runTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime,t.taskPerformance?.runTime??0,e.tasks.executed)}updateWaitTimeWorkerUsage(e,t){const s=r.performance.now(),i=s-(t.timestamp??s);T(e.waitTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime,i,e.tasks.executed)}updateEluWorkerUsage(e,t){const s=this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu;T(e.elu.active,s,t.taskPerformance?.elu?.active??0,e.tasks.executed),T(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.createAndSetupDynamicWorker();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return this.getWorkerNodeKey(e)}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===k.dynamic&&!this.full&&this.internalBusy()}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??m),e.on("error",this.opts.errorHandler??m),e.on("error",(t=>{const s=this.getWorkerNodeKey(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.createAndSetupDynamicWorker():this.createAndSetupWorker()),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(s)})),e.on("online",this.opts.onlineHandler??m),e.on("exit",this.opts.exitHandler??m),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.addWorkerNode(e),this.afterWorkerSetup(e),e}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=W.HARD,(t.kill===r||null!=t.kill&&(!1===this.opts.enableTasksQueue&&0===this.workerNodes[s].usage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[s].usage.tasks.executing&&0===this.tasksQueueSize(s)))&&this.destroyWorker(e)}));const t=this.getWorkerInfoByWorker(e);return t.dynamic=!0,this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker&&(t.ready=!0),this.sendToWorker(e,{checkActive:!0,workerId:t.id}),e}afterWorkerSetup(e){this.registerWorkerMessageListener(e,this.workerListener()),this.sendStartupMessageToWorker(e),this.setWorkerStatistics(e)}redistributeQueuedTasks(e){for(;this.tasksQueueSize(e)>0;){let t=e,s=1/0;for(const[r,i]of this.workerNodes.entries()){const o=this.getWorkerInfo(r);if(r!==e&&o.ready&&0===i.usage.tasks.queued){t=r;break}r!==e&&o.ready&&i.usage.tasks.queued<s&&(s=i.usage.tasks.queued,t=r)}this.enqueueTask(t,this.dequeueTask(e))}}workerListener(){return e=>{this.checkMessageWorkerId(e),null!=e.ready?this.handleWorkerReadyResponse(e):null!=e.id&&this.handleTaskExecutionResponse(e)}}handleWorkerReadyResponse(e){this.getWorkerInfoByWorker(this.getWorkerById(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.id);if(null!=t){null!=e.taskError?(this.emitter?.emit(d.taskError,e.taskError),t.reject(e.taskError.message)):t.resolve(e.data),this.afterTaskExecutionHook(t.worker,e),this.promiseResponseMap.delete(e.id);const s=this.getWorkerNodeKey(t.worker);!0===this.opts.enableTasksQueue&&this.tasksQueueSize(s)>0&&this.executeTask(s,this.dequeueTask(s)),this.workerChoiceStrategyContext.update(s)}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter.emit(d.busy,this.info),this.type===k.dynamic&&this.full&&this.emitter.emit(d.full,this.info))}getWorkerInfo(e){return this.workerNodes[e].info}getWorkerInfoByWorker(e){const t=this.getWorkerNodeKey(e);if(-1===t)throw new Error("Worker not found");return this.workerNodes[t].info}addWorkerNode(e){const t=new A(e,this.worker);return this.starting&&(t.info.ready=!0),this.workerNodes.push(t)}removeWorkerNode(e){const t=this.getWorkerNodeKey(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(this.workerNodes[e].worker,t)}enqueueTask(e,t){return this.workerNodes[e].enqueueTask(t)}dequeueTask(e){return this.workerNodes[e].dequeueTask()}tasksQueueSize(e){return this.workerNodes[e].tasksQueueSize()}flushTasksQueue(e){for(;this.tasksQueueSize(e)>0;)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].clearTasksQueue()}flushTasksQueues(){for(const[e]of this.workerNodes.entries())this.flushTasksQueue(e)}setWorkerStatistics(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate},workerId:this.getWorkerInfoByWorker(e).id})}}class Q extends P{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}setupHook(){t.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return t.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:!0,workerId:e.id}),e.on("disconnect",(()=>{e.kill()})),e.disconnect()}sendToWorker(e,t){e.send(t)}sendStartupMessageToWorker(e){this.sendToWorker(e,{ready:!1,workerId:e.id})}registerWorkerMessageListener(e,t){e.on("message",t)}createWorker(){return t.fork(this.opts.env)}get type(){return k.fixed}get worker(){return K.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class U extends P{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return n.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:!0,workerId:e.threadId}),this.workerNodes[this.getWorkerNodeKey(e)].closeChannel(),await e.terminate()}sendToWorker(e,t){this.getWorkerInfoByWorker(e).messageChannel.port1.postMessage(t)}sendStartupMessageToWorker(e){const t=this.getWorkerInfoByWorker(e).messageChannel.port2;e.postMessage({ready:!1,workerId:e.threadId,port:t},[t])}registerWorkerMessageListener(e,t){this.getWorkerInfoByWorker(e).messageChannel.port1.on("message",t)}createWorker(){return new n.Worker(this.filePath,{env:n.SHARE_ENV,...this.opts.workerOptions})}get type(){return k.fixed}get worker(){return K.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}const D=6e4,F=W.SOFT;class B extends a.AsyncResource{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;activeInterval;constructor(e,t,s,r,i={killBehavior:F,maxInactiveTime:D}){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??F,this.opts.maxInactiveTime=e.maxInactiveTime??D,delete this.opts.async}checkTaskFunctions(e){if(null==e)throw new Error("taskFunctions parameter is mandatory");if(this.taskFunctions=new Map,"function"==typeof e){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("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");return this.taskFunctions.has(e)}addTaskFunction(e,t){if("string"!=typeof e)throw new TypeError("name parameter is not a 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),!0}catch{return!1}}removeTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a 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");return this.taskFunctions.delete(e)}listTaskFunctions(){return Array.from(this.taskFunctions.keys())}setDefaultTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a 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){e.workerId===this.id&&(null!=e.statistics?this.statistics=e.statistics:null!=e.checkActive?!this.isMain&&e.checkActive?this.startCheckActive():this.stopCheckActive():null!=e.id&&null!=e.data?this.run(e):!0===e.kill&&this.handleKillMessage(e))}handleKillMessage(e){!this.isMain&&this.stopCheckActive(),this.emitDestroy()}startCheckActive(){this.lastTaskTimestamp=r.performance.now(),this.activeInterval=setInterval(this.checkActive.bind(this),(this.opts.maxInactiveTime??D)/2),this.activeInterval.unref()}stopCheckActive(){null!=this.activeInterval&&(clearInterval(this.activeInterval),delete this.activeInterval)}checkActive(){r.performance.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??D)&&this.sendToMainWorker({kill:this.opts.killBehavior,workerId:this.id})}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}handleError(e){return e instanceof Error?e.message:e}run(e){if(this.isMain)throw new Error("Cannot run a task in the main worker");const t=this.getTaskFunction(e.name);(e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name)(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}runSync(e,t){try{let s=this.beginTaskPerformance(t.name);const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,workerId:this.id,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??l,message:s,data:t.data},workerId:this.id,id:t.id})}finally{this.updateLastTaskTimestamp()}}runAsync(e,t){let s=this.beginTaskPerformance(t.name);e(t.data).then((e=>(s=this.endTaskPerformance(s),this.sendToMainWorker({data:e,taskPerformance:s,workerId:this.id,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??l,message:s,data:t.data},workerId:this.id,id:t.id})})).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:r.performance.now(),...this.statistics.elu&&{elu:r.performance.eventLoopUtilization()}}}endTaskPerformance(e){return this.checkStatistics(),{...e,...this.statistics.runTime&&{runTime:r.performance.now()-e.timestamp},...this.statistics.elu&&{elu:r.performance.eventLoopUtilization(e.elu)}}}checkStatistics(){if(null==this.statistics)throw new Error("Performance statistics computation requirements not set")}updateLastTaskTimestamp(){this.isMain||null==this.activeInterval||(this.lastTaskTimestamp=r.performance.now())}}exports.ClusterWorker=class extends B{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,t.worker,e,s)}handleReadyMessage(e){this.isMain||e.workerId!==this.id||null==e.ready||(this.getMainWorker()?.on("message",this.messageListener.bind(this)),this.sendToMainWorker({ready:!0,workerId:this.id}))}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}},exports.DynamicClusterPool=class extends Q{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,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 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.FixedClusterPool=Q,exports.FixedThreadPool=U,exports.KillBehaviors=W,exports.Measurements=N,exports.PoolEvents=d,exports.PoolTypes=k,exports.ThreadWorker=class extends B{port;constructor(e,t={}){super("worker-thread-pool:poolifier",n.isMainThread,n.parentPort,e,t)}handleReadyMessage(e){this.isMain||e.workerId!==this.id||null==e.ready||null==e.port||(this.port=e.port,this.port.on("message",this.messageListener.bind(this)),this.sendToMainWorker({ready:!0,workerId:this.id}))}handleKillMessage(e){super.handleKillMessage(e),this.port?.unref(),this.port?.close()}get id(){return n.threadId}sendToMainWorker(e){this.port.postMessage(e)}handleError(e){return e}},exports.WorkerChoiceStrategies=x,exports.WorkerTypes=K,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 t from"node:cluster";import{randomUUID as s}from"node:crypto";import{performance as r}from"node:perf_hooks";import{existsSync as i}from"node:fs";import*as o from"node:os";import{cpus as n}from"node:os";import{isMainThread as a,Worker as h,SHARE_ENV as u,parentPort as k,threadId as c}from"node:worker_threads";import{AsyncResource as d}from"node:async_hooks";const l=Object.freeze({fixed:"fixed",dynamic:"dynamic"});class m extends e{}const g=Object.freeze({full:"full",ready:"ready",busy:"busy",error:"error",taskError:"taskError"}),p="default",w=Object.freeze((()=>{})),y={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},f={aggregate:!1,average:!1,median:!1},T=()=>{let e=1;try{e=o.availableParallelism()}catch{const t=o.cpus();Array.isArray(t)&&t.length>0&&(e=t.length)}return e},W=e=>{if(Array.isArray(e)&&0===e.length)return 0;if(Array.isArray(e)&&1===e.length)return e[0];const t=e.slice().sort(((e,t)=>e-t));return(t[t.length-1>>1]+t[t.length>>1])/2},N=(e,t=2)=>{const s=Math.pow(10,t);return Math.round(e*s*(1+Number.EPSILON))/s},x=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),S=(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)))},E=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 b{pool;opts;nextWorkerNodeKey=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:f,waitTime:f,elu:f};constructor(e,t=y){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??y,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()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/n().length)}}class C extends b{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:f,elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=y){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 I extends b{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=y){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 O extends b{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:f};constructor(e,t=y){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 z extends b{constructor(e,t=y){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 K extends b{taskStatisticsRequirements={runTime:f,waitTime:f,elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=y){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 q extends b{strategyPolicy={useDynamicWorker:!0};constructor(e,t=y){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 M extends b{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:f,elu:f};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=y){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 A{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=R.ROUND_ROBIN,s=y){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[R.ROUND_ROBIN,new(q.bind(this))(e,s)],[R.LEAST_USED,new(z.bind(this))(e,s)],[R.LEAST_BUSY,new(O.bind(this))(e,s)],[R.LEAST_ELU,new(K.bind(this))(e,s)],[R.FAIR_SHARE,new(C.bind(this))(e,s)],[R.WEIGHTED_ROUND_ROBIN,new(M.bind(this))(e,s)],[R.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(I.bind(this))(e,s)]])}getStrategyPolicy(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).strategyPolicy}getTaskStatisticsRequirements(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).taskStatisticsRequirements}setWorkerChoiceStrategy(e){this.workerChoiceStrategy!==e&&(this.workerChoiceStrategy=e),this.workerChoiceStrategies.get(this.workerChoiceStrategy)?.reset()}update(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).update(e)}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategy).choose();if(null==e)throw new Error("Worker node key chosen is null or undefined");return e}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).remove(e)}setOptions(e){for(const t of this.workerChoiceStrategies.values())t.setOptions(e)}}class 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;return arguments.length>=3&&void 0!==t?(r=super.splice(e,t),this.push(...s)):r=2===arguments.length?super.splice(e,t):super.splice(e),r}resize(e){if(this.checkSize(e),0===e)this.length=0;else if(e<this.size)for(let t=e;t<this.size;t++)super.pop();this.size=e}empty(){return 0===this.length}full(){return this.length===this.size}checkSize(e){if(!Number.isSafeInteger(e))throw new TypeError(`Invalid circular array size: ${e} is not a safe integer`);if(e<0)throw new RangeError(`Invalid circular array size: ${e} < 0`)}}class U{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({cluster:"cluster",thread:"thread"});class D{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 U}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()}getTaskWorkerUsage(e){return this.tasksUsage.has(e)||this.tasksUsage.set(e,this.initTaskWorkerUsage(e)),this.tasksUsage.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 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===P.thread?e.threadId:t===P.cluster?e.id:void 0}}class F{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!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.chooseWorkerNode=this.chooseWorkerNode.bind(this),this.executeTask=this.executeTask.bind(this),this.enqueueTask=this.enqueueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new m),this.workerChoiceStrategyContext=new A(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook(),this.starting=!0,this.startPool(),this.starting=!1,this.startTimestamp=r.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(!i(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===l.fixed&&0===e)throw new RangeError("Cannot instantiate a fixed pool with zero worker")}checkDynamicPoolSize(e,t){if(this.type===l.dynamic){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 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(!x(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=e.workerChoiceStrategyOptions??y,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(!x(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&&!x(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.createAndSetupWorker()}get info(){return{version:"2.6.18",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:N(this.utilization)},workerNodes:this.workerNodes.length,idleWorkerNodes:this.workerNodes.reduce(((e,t)=>0===t.usage.tasks.executing?e+1:e),0),busyWorkerNodes:this.workerNodes.reduce(((e,t)=>t.usage.tasks.executing>0?e+1:e),0),executedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.executed),0),executingTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.executing),0),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.queued),0),maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.maxQueued??0)),0),failedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.failed),0),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&{runTime:{minimum:N(Math.min(...this.workerNodes.map((e=>e.usage.runTime?.minimum??1/0)))),maximum:N(Math.max(...this.workerNodes.map((e=>e.usage.runTime?.maximum??-1/0)))),average:N(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:N(W(this.workerNodes.map((e=>e.usage.runTime?.median??0))))}}},...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{waitTime:{minimum:N(Math.min(...this.workerNodes.map((e=>e.usage.waitTime?.minimum??1/0)))),maximum:N(Math.max(...this.workerNodes.map((e=>e.usage.waitTime?.maximum??-1/0)))),average:N(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:N(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=(r.now()-this.startTimestamp)*this.maxSize;return(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)+this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0))/e}getWorkerById(e){return this.workerNodes.find((t=>t.info.id===e))?.worker}checkMessageWorkerId(e){if(null!=e.workerId&&null==this.getWorkerById(e.workerId))throw new Error(`Worker message received from unknown worker '${e.workerId}'`)}getWorkerNodeKey(e){return this.workerNodes.findIndex((t=>t.worker===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const e of this.workerNodes)e.resetUsage(),this.setWorkerStatistics(e.worker)}setWorkerChoiceStrategyOptions(e){this.checkValidWorkerChoiceStrategyOptions(e),this.opts.workerChoiceStrategyOptions=e,this.workerChoiceStrategyContext.setOptions(this.opts.workerChoiceStrategyOptions)}enableTasksQueue(e,t){!0!==this.opts.enableTasksQueue||e||this.flushTasksQueues(),this.opts.enableTasksQueue=e,this.setTasksQueueOptions(t)}setTasksQueueOptions(e){!0===this.opts.enableTasksQueue?(this.checkValidTasksQueueOptions(e),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e)):null!=this.opts.tasksQueueOptions&&delete this.opts.tasksQueueOptions}buildTasksQueueOptions(e){return{concurrency:e?.concurrency??1}}get full(){return this.workerNodes.length>=this.maxSize}internalBusy(){return-1===this.workerNodes.findIndex((e=>0===e.usage.tasks.executing))}async execute(e,t){const i=r.now(),o=this.chooseWorkerNode(),n={name:t??p,data:e??{},timestamp:i,workerId:this.getWorkerInfo(o).id,id:s()},a=new Promise(((e,t)=>{this.promiseResponseMap.set(n.id,{resolve:e,reject:t,worker:this.workerNodes[o].worker})}));return!0===this.opts.enableTasksQueue&&(this.busy||this.workerNodes[o].usage.tasks.executing>=this.opts.tasksQueueOptions.concurrency)?this.enqueueTask(o,n):this.executeTask(o,n),this.checkAndEmitEvents(),a}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{this.flushTasksQueue(t);const s=new Promise((t=>{e.worker.on("exit",(()=>{t()}))}));await this.destroyWorker(e.worker),await s})))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t);const r=this.workerNodes[e].getTaskWorkerUsage(t.name);++r.tasks.executing,this.updateWaitTimeWorkerUsage(r,t)}afterTaskExecutionHook(e,t){const s=this.getWorkerNodeKey(e),r=this.workerNodes[s].usage;this.updateTaskStatisticsWorkerUsage(r,t),this.updateRunTimeWorkerUsage(r,t),this.updateEluWorkerUsage(r,t);const i=this.workerNodes[s].getTaskWorkerUsage(t.taskPerformance?.name??p);this.updateTaskStatisticsWorkerUsage(i,t),this.updateRunTimeWorkerUsage(i,t),this.updateEluWorkerUsage(i,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,null==t.taskError?++s.executed:++s.failed}updateRunTimeWorkerUsage(e,t){S(e.runTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime,t.taskPerformance?.runTime??0,e.tasks.executed)}updateWaitTimeWorkerUsage(e,t){const s=r.now(),i=s-(t.timestamp??s);S(e.waitTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime,i,e.tasks.executed)}updateEluWorkerUsage(e,t){const s=this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu;S(e.elu.active,s,t.taskPerformance?.elu?.active??0,e.tasks.executed),S(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.createAndSetupDynamicWorker();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return this.getWorkerNodeKey(e)}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===l.dynamic&&!this.full&&this.internalBusy()}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??w),e.on("error",this.opts.errorHandler??w),e.on("error",(t=>{const s=this.getWorkerNodeKey(e),r=this.getWorkerInfo(s);r.ready=!1,this.emitter?.emit(g.error,t),!0!==this.opts.restartWorkerOnError||this.starting||(r.dynamic?this.createAndSetupDynamicWorker():this.createAndSetupWorker()),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(s)})),e.on("online",this.opts.onlineHandler??w),e.on("exit",this.opts.exitHandler??w),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.addWorkerNode(e),this.afterWorkerSetup(e),e}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=E.HARD,(t.kill===r||null!=t.kill&&(!1===this.opts.enableTasksQueue&&0===this.workerNodes[s].usage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[s].usage.tasks.executing&&0===this.tasksQueueSize(s)))&&this.destroyWorker(e)}));const t=this.getWorkerInfo(this.getWorkerNodeKey(e));return t.dynamic=!0,this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker&&(t.ready=!0),this.sendToWorker(e,{checkActive:!0,workerId:t.id}),e}registerWorkerMessageListener(e,t){e.on("message",t)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,this.workerListener()),this.sendWorkerStartupMessage(e),this.setWorkerStatistics(e)}sendWorkerStartupMessage(e){this.sendToWorker(e,{ready:!1,workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id})}redistributeQueuedTasks(e){for(;this.tasksQueueSize(e)>0;){let t=e,s=1/0;for(const[r,i]of this.workerNodes.entries()){const o=this.getWorkerInfo(r);if(r!==e&&o.ready&&0===i.usage.tasks.queued){t=r;break}r!==e&&o.ready&&i.usage.tasks.queued<s&&(s=i.usage.tasks.queued,t=r)}this.enqueueTask(t,this.dequeueTask(e))}}workerListener(){return e=>{this.checkMessageWorkerId(e),null!=e.ready?this.handleWorkerReadyResponse(e):null!=e.id&&this.handleTaskExecutionResponse(e)}}handleWorkerReadyResponse(e){const t=this.getWorkerById(e.workerId);this.getWorkerInfo(this.getWorkerNodeKey(t)).ready=e.ready,null!=this.emitter&&this.ready&&this.emitter.emit(g.ready,this.info)}handleTaskExecutionResponse(e){const t=this.promiseResponseMap.get(e.id);if(null!=t){null!=e.taskError?(this.emitter?.emit(g.taskError,e.taskError),t.reject(e.taskError.message)):t.resolve(e.data),this.afterTaskExecutionHook(t.worker,e),this.promiseResponseMap.delete(e.id);const s=this.getWorkerNodeKey(t.worker);!0===this.opts.enableTasksQueue&&this.tasksQueueSize(s)>0&&this.executeTask(s,this.dequeueTask(s)),this.workerChoiceStrategyContext.update(s)}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter.emit(g.busy,this.info),this.type===l.dynamic&&this.full&&this.emitter.emit(g.full,this.info))}getWorkerInfo(e){return this.workerNodes[e].info}addWorkerNode(e){const t=new D(e,this.worker);return this.starting&&(t.info.ready=!0),this.workerNodes.push(t)}removeWorkerNode(e){const t=this.getWorkerNodeKey(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(this.workerNodes[e].worker,t)}enqueueTask(e,t){return this.workerNodes[e].enqueueTask(t)}dequeueTask(e){return this.workerNodes[e].dequeueTask()}tasksQueueSize(e){return this.workerNodes[e].tasksQueueSize()}flushTasksQueue(e){for(;this.tasksQueueSize(e)>0;)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].clearTasksQueue()}flushTasksQueues(){for(const[e]of this.workerNodes.entries())this.flushTasksQueue(e)}setWorkerStatistics(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate},workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id})}}class _ extends F{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}setupHook(){t.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return t.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:!0,workerId:e.id}),e.on("disconnect",(()=>{e.kill()})),e.disconnect()}sendToWorker(e,t){e.send(t)}createWorker(){return t.fork(this.opts.env)}get type(){return l.fixed}get worker(){return P.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class V extends _{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return l.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}class B extends F{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return a}async destroyWorker(e){this.sendToWorker(e,{kill:!0,workerId:e.threadId}),await e.terminate()}sendToWorker(e,t){e.postMessage(t)}createWorker(){return new h(this.filePath,{env:u,...this.opts.workerOptions})}get type(){return l.fixed}get worker(){return P.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class L extends B{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return l.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}const j=6e4,H=E.SOFT;class $ extends d{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;activeInterval;constructor(e,t,s,r,i={killBehavior:H,maxInactiveTime:j}){super(e),this.isMain=t,this.mainWorker=r,this.opts=i,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||this.mainWorker?.on("message",this.messageListener.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??H,this.opts.maxInactiveTime=e.maxInactiveTime??j,delete this.opts.async}checkTaskFunctions(e){if(null==e)throw new Error("taskFunctions parameter is mandatory");if(this.taskFunctions=new Map,"function"==typeof e){const t=e.bind(this);this.taskFunctions.set(p,t),this.taskFunctions.set("string"==typeof e.name&&e.name.trim().length>0?e.name:"fn1",t)}else{if(!x(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("function"!=typeof r)throw new TypeError("A taskFunctions parameter object value is not a function");const e=r.bind(this);t&&(this.taskFunctions.set(p,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");return this.taskFunctions.has(e)}addTaskFunction(e,t){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if(e===p)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(p)&&this.taskFunctions.set(p,s),this.taskFunctions.set(e,s),!0}catch{return!1}}removeTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if(e===p)throw new Error("Cannot remove the task function with the default reserved name");if(this.taskFunctions.get(e)===this.taskFunctions.get(p))throw new Error("Cannot remove the task function used as the default task function");return this.taskFunctions.delete(e)}listTaskFunctions(){return Array.from(this.taskFunctions.keys())}setDefaultTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if(e===p)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(p,this.taskFunctions.get(e)),!0}catch{return!1}}messageListener(e){e.workerId===this.id&&(null!=e.ready?this.sendReadyResponse():null!=e.statistics?this.statistics=e.statistics:null!=e.checkActive?!this.isMain&&e.checkActive?this.startCheckActive():this.stopCheckActive():null!=e.id&&null!=e.data?this.run(e):!0===e.kill&&(!this.isMain&&this.stopCheckActive(),this.emitDestroy()))}sendReadyResponse(){!this.isMain&&this.sendToMainWorker({ready:!0,workerId:this.id})}startCheckActive(){this.lastTaskTimestamp=r.now(),this.activeInterval=setInterval(this.checkActive.bind(this),(this.opts.maxInactiveTime??j)/2).unref()}stopCheckActive(){null!=this.activeInterval&&(clearInterval(this.activeInterval),delete this.activeInterval)}checkActive(){r.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??j)&&this.sendToMainWorker({kill:this.opts.killBehavior,workerId:this.id})}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}handleError(e){return e instanceof Error?e.message:e}run(e){if(this.isMain)throw new Error("Cannot run a task in the main worker");const t=this.getTaskFunction(e.name);(e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name)(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}runSync(e,t){try{let s=this.beginTaskPerformance(t.name);const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,workerId:this.id,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??p,message:s,data:t.data},workerId:this.id,id:t.id})}finally{this.updateLastTaskTimestamp()}}runAsync(e,t){let s=this.beginTaskPerformance(t.name);e(t.data).then((e=>(s=this.endTaskPerformance(s),this.sendToMainWorker({data:e,taskPerformance:s,workerId:this.id,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??p,message:s,data:t.data},workerId:this.id,id:t.id})})).finally((()=>{this.updateLastTaskTimestamp()})).catch(w)}getTaskFunction(e){e=e??p;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(e){return this.checkStatistics(),{name:e??p,timestamp:r.now(),...this.statistics.elu&&{elu:r.eventLoopUtilization()}}}endTaskPerformance(e){return this.checkStatistics(),{...e,...this.statistics.runTime&&{runTime:r.now()-e.timestamp},...this.statistics.elu&&{elu:r.eventLoopUtilization(e.elu)}}}checkStatistics(){if(null==this.statistics)throw new Error("Performance statistics computation requirements not set")}updateLastTaskTimestamp(){this.isMain||null==this.activeInterval||(this.lastTaskTimestamp=r.now())}}class G extends ${constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,s)}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}}class Y extends ${constructor(e,t={}){super("worker-thread-pool:poolifier",a,e,k,t)}get id(){return c}sendToMainWorker(e){this.getMainWorker().postMessage(e)}handleError(e){return e}}export{G as ClusterWorker,V as DynamicClusterPool,L as DynamicThreadPool,_ as FixedClusterPool,B as FixedThreadPool,E as KillBehaviors,v as Measurements,g as PoolEvents,l as PoolTypes,Y as ThreadWorker,R as WorkerChoiceStrategies,P as WorkerTypes,T as availableParallelism};
|
|
1
|
+
import{EventEmitter as e}from"node:events";import t from"node:cluster";import{randomUUID as s}from"node:crypto";import{performance as r}from"node:perf_hooks";import{existsSync as i}from"node:fs";import*as o from"node:os";import{cpus as n}from"node:os";import{MessageChannel as a,isMainThread as h,Worker as u,SHARE_ENV as k,parentPort as c,threadId as d}from"node:worker_threads";import{AsyncResource as l}from"node:async_hooks";const m=Object.freeze({fixed:"fixed",dynamic:"dynamic"});class g extends e{}const p=Object.freeze({full:"full",ready:"ready",busy:"busy",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=o.availableParallelism()}catch{const t=o.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,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"}),v=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LEAST_USED:"LEAST_USED",LEAST_BUSY:"LEAST_BUSY",LEAST_ELU:"LEAST_ELU",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN",INTERLEAVED_WEIGHTED_ROUND_ROBIN:"INTERLEAVED_WEIGHTED_ROUND_ROBIN"}),C=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class b{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 n()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/n().length)}}class I extends b{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===C.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class O extends b{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 z extends b{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 M extends b{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 K extends b{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 q extends b{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 b{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 Q{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=v.ROUND_ROBIN,s=f){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[v.ROUND_ROBIN,new(q.bind(this))(e,s)],[v.LEAST_USED,new(M.bind(this))(e,s)],[v.LEAST_BUSY,new(z.bind(this))(e,s)],[v.LEAST_ELU,new(K.bind(this))(e,s)],[v.FAIR_SHARE,new(I.bind(this))(e,s)],[v.WEIGHTED_ROUND_ROBIN,new(A.bind(this))(e,s)],[v.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(O.bind(this))(e,s)]])}getStrategyPolicy(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).strategyPolicy}getTaskStatisticsRequirements(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).taskStatisticsRequirements}setWorkerChoiceStrategy(e){this.workerChoiceStrategy!==e&&(this.workerChoiceStrategy=e),this.workerChoiceStrategies.get(this.workerChoiceStrategy)?.reset()}update(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).update(e)}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategy).choose();if(null==e)throw new Error("Worker node key chosen is null or undefined");return e}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).remove(e)}setOptions(e){for(const t of this.workerChoiceStrategies.values())t.setOptions(e)}}class 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;return arguments.length>=3&&void 0!==t?(r=super.splice(e,t),this.push(...s)):r=2===arguments.length?super.splice(e,t):super.splice(e),r}resize(e){if(this.checkSize(e),0===e)this.length=0;else if(e<this.size)for(let t=e;t<this.size;t++)super.pop();this.size=e}empty(){return 0===this.length}full(){return this.length===this.size}checkSize(e){if(!Number.isSafeInteger(e))throw new TypeError(`Invalid circular array size: ${e} is not a safe integer`);if(e<0)throw new RangeError(`Invalid circular array size: ${e} < 0`)}}class P{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 D=Object.freeze({cluster:"cluster",thread:"thread"});class F{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 P}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,t){return{id:this.getWorkerId(e,t),type:t,dynamic:!1,ready:!1,...t===D.thread&&{messageChannel:new a}}}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}}}}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 U},waitTime:{history:new U},elu:{idle:{history:new U},active:{history:new U}}}}getWorkerId(e,t){return t===D.thread?e.threadId:t===D.cluster?e.id:void 0}}class B{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!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.chooseWorkerNode=this.chooseWorkerNode.bind(this),this.executeTask=this.executeTask.bind(this),this.enqueueTask=this.enqueueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new g),this.workerChoiceStrategyContext=new Q(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook(),this.starting=!0,this.startPool(),this.starting=!1,this.startTimestamp=r.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(!i(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(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 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??v.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(v).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}checkValidWorkerChoiceStrategyOptions(e){if(!S(e))throw new TypeError("Invalid worker choice strategy options: must be a plain object");if(null!=e.weights&&Object.keys(e.weights).length!==this.maxSize)throw new Error("Invalid worker choice strategy options: must have a weight for each worker node");if(null!=e.measurement&&!Object.values(C).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.createAndSetupWorker()}get info(){return{version:"2.6.19",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),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.queued),0),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=(r.now()-this.startTimestamp)*this.maxSize;return(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)+this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0))/e}getWorkerById(e){return this.workerNodes.find((t=>t.info.id===e))?.worker}checkMessageWorkerId(e){if(null!=e.workerId&&null==this.getWorkerById(e.workerId))throw new Error(`Worker message received from unknown worker '${e.workerId}'`)}getWorkerNodeKey(e){return this.workerNodes.findIndex((t=>t.worker===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const e of this.workerNodes)e.resetUsage(),this.setWorkerStatistics(e.worker)}setWorkerChoiceStrategyOptions(e){this.checkValidWorkerChoiceStrategyOptions(e),this.opts.workerChoiceStrategyOptions=e,this.workerChoiceStrategyContext.setOptions(this.opts.workerChoiceStrategyOptions)}enableTasksQueue(e,t){!0!==this.opts.enableTasksQueue||e||this.flushTasksQueues(),this.opts.enableTasksQueue=e,this.setTasksQueueOptions(t)}setTasksQueueOptions(e){!0===this.opts.enableTasksQueue?(this.checkValidTasksQueueOptions(e),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e)):null!=this.opts.tasksQueueOptions&&delete this.opts.tasksQueueOptions}buildTasksQueueOptions(e){return{concurrency:e?.concurrency??1}}get full(){return this.workerNodes.length>=this.maxSize}internalBusy(){return-1===this.workerNodes.findIndex((e=>0===e.usage.tasks.executing))}async execute(e,t){const i=r.now(),o=this.chooseWorkerNode(),n={name:t??w,data:e??{},timestamp:i,workerId:this.getWorkerInfo(o).id,id:s()},a=new Promise(((e,t)=>{this.promiseResponseMap.set(n.id,{resolve:e,reject:t,worker:this.workerNodes[o].worker})}));return!0===this.opts.enableTasksQueue&&(this.busy||this.workerNodes[o].usage.tasks.executing>=this.opts.tasksQueueOptions.concurrency)?this.enqueueTask(o,n):this.executeTask(o,n),this.checkAndEmitEvents(),a}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{this.flushTasksQueue(t);const s=new Promise((t=>{e.worker.on("exit",(()=>{t()}))}));await this.destroyWorker(e.worker),await s})))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t);const r=this.workerNodes[e].getTaskWorkerUsage(t.name);++r.tasks.executing,this.updateWaitTimeWorkerUsage(r,t)}afterTaskExecutionHook(e,t){const s=this.getWorkerNodeKey(e),r=this.workerNodes[s].usage;this.updateTaskStatisticsWorkerUsage(r,t),this.updateRunTimeWorkerUsage(r,t),this.updateEluWorkerUsage(r,t);const i=this.workerNodes[s].getTaskWorkerUsage(t.taskPerformance?.name??w);this.updateTaskStatisticsWorkerUsage(i,t),this.updateRunTimeWorkerUsage(i,t),this.updateEluWorkerUsage(i,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,null==t.taskError?++s.executed:++s.failed}updateRunTimeWorkerUsage(e,t){E(e.runTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime,t.taskPerformance?.runTime??0,e.tasks.executed)}updateWaitTimeWorkerUsage(e,t){const s=r.now(),i=s-(t.timestamp??s);E(e.waitTime,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime,i,e.tasks.executed)}updateEluWorkerUsage(e,t){const s=this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu;E(e.elu.active,s,t.taskPerformance?.elu?.active??0,e.tasks.executed),E(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.createAndSetupDynamicWorker();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return this.getWorkerNodeKey(e)}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===m.dynamic&&!this.full&&this.internalBusy()}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??y),e.on("error",this.opts.errorHandler??y),e.on("error",(t=>{const s=this.getWorkerNodeKey(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.createAndSetupDynamicWorker():this.createAndSetupWorker()),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(s)})),e.on("online",this.opts.onlineHandler??y),e.on("exit",this.opts.exitHandler??y),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.addWorkerNode(e),this.afterWorkerSetup(e),e}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=R.HARD,(t.kill===r||null!=t.kill&&(!1===this.opts.enableTasksQueue&&0===this.workerNodes[s].usage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[s].usage.tasks.executing&&0===this.tasksQueueSize(s)))&&this.destroyWorker(e)}));const t=this.getWorkerInfoByWorker(e);return t.dynamic=!0,this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker&&(t.ready=!0),this.sendToWorker(e,{checkActive:!0,workerId:t.id}),e}afterWorkerSetup(e){this.registerWorkerMessageListener(e,this.workerListener()),this.sendStartupMessageToWorker(e),this.setWorkerStatistics(e)}redistributeQueuedTasks(e){for(;this.tasksQueueSize(e)>0;){let t=e,s=1/0;for(const[r,i]of this.workerNodes.entries()){const o=this.getWorkerInfo(r);if(r!==e&&o.ready&&0===i.usage.tasks.queued){t=r;break}r!==e&&o.ready&&i.usage.tasks.queued<s&&(s=i.usage.tasks.queued,t=r)}this.enqueueTask(t,this.dequeueTask(e))}}workerListener(){return e=>{this.checkMessageWorkerId(e),null!=e.ready?this.handleWorkerReadyResponse(e):null!=e.id&&this.handleTaskExecutionResponse(e)}}handleWorkerReadyResponse(e){this.getWorkerInfoByWorker(this.getWorkerById(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.id);if(null!=t){null!=e.taskError?(this.emitter?.emit(p.taskError,e.taskError),t.reject(e.taskError.message)):t.resolve(e.data),this.afterTaskExecutionHook(t.worker,e),this.promiseResponseMap.delete(e.id);const s=this.getWorkerNodeKey(t.worker);!0===this.opts.enableTasksQueue&&this.tasksQueueSize(s)>0&&this.executeTask(s,this.dequeueTask(s)),this.workerChoiceStrategyContext.update(s)}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter.emit(p.busy,this.info),this.type===m.dynamic&&this.full&&this.emitter.emit(p.full,this.info))}getWorkerInfo(e){return this.workerNodes[e].info}getWorkerInfoByWorker(e){const t=this.getWorkerNodeKey(e);if(-1===t)throw new Error("Worker not found");return this.workerNodes[t].info}addWorkerNode(e){const t=new F(e,this.worker);return this.starting&&(t.info.ready=!0),this.workerNodes.push(t)}removeWorkerNode(e){const t=this.getWorkerNodeKey(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(this.workerNodes[e].worker,t)}enqueueTask(e,t){return this.workerNodes[e].enqueueTask(t)}dequeueTask(e){return this.workerNodes[e].dequeueTask()}tasksQueueSize(e){return this.workerNodes[e].tasksQueueSize()}flushTasksQueue(e){for(;this.tasksQueueSize(e)>0;)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].clearTasksQueue()}flushTasksQueues(){for(const[e]of this.workerNodes.entries())this.flushTasksQueue(e)}setWorkerStatistics(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate},workerId:this.getWorkerInfoByWorker(e).id})}}class _ extends B{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}setupHook(){t.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return t.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:!0,workerId:e.id}),e.on("disconnect",(()=>{e.kill()})),e.disconnect()}sendToWorker(e,t){e.send(t)}sendStartupMessageToWorker(e){this.sendToWorker(e,{ready:!1,workerId:e.id})}registerWorkerMessageListener(e,t){e.on("message",t)}createWorker(){return t.fork(this.opts.env)}get type(){return m.fixed}get worker(){return D.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class V extends _{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return m.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}class L extends B{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return h}async destroyWorker(e){this.sendToWorker(e,{kill:!0,workerId:e.threadId}),this.workerNodes[this.getWorkerNodeKey(e)].closeChannel(),await e.terminate()}sendToWorker(e,t){this.getWorkerInfoByWorker(e).messageChannel.port1.postMessage(t)}sendStartupMessageToWorker(e){const t=this.getWorkerInfoByWorker(e).messageChannel.port2;e.postMessage({ready:!1,workerId:e.threadId,port:t},[t])}registerWorkerMessageListener(e,t){this.getWorkerInfoByWorker(e).messageChannel.port1.on("message",t)}createWorker(){return new u(this.filePath,{env:k,...this.opts.workerOptions})}get type(){return m.fixed}get worker(){return D.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class j extends L{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 H=6e4,$=R.SOFT;class G extends l{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;activeInterval;constructor(e,t,s,r,i={killBehavior:$,maxInactiveTime:H}){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??H,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("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");return this.taskFunctions.has(e)}addTaskFunction(e,t){if("string"!=typeof e)throw new TypeError("name parameter is not a 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),!0}catch{return!1}}removeTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a 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");return this.taskFunctions.delete(e)}listTaskFunctions(){return Array.from(this.taskFunctions.keys())}setDefaultTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a 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){e.workerId===this.id&&(null!=e.statistics?this.statistics=e.statistics:null!=e.checkActive?!this.isMain&&e.checkActive?this.startCheckActive():this.stopCheckActive():null!=e.id&&null!=e.data?this.run(e):!0===e.kill&&this.handleKillMessage(e))}handleKillMessage(e){!this.isMain&&this.stopCheckActive(),this.emitDestroy()}startCheckActive(){this.lastTaskTimestamp=r.now(),this.activeInterval=setInterval(this.checkActive.bind(this),(this.opts.maxInactiveTime??H)/2),this.activeInterval.unref()}stopCheckActive(){null!=this.activeInterval&&(clearInterval(this.activeInterval),delete this.activeInterval)}checkActive(){r.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??H)&&this.sendToMainWorker({kill:this.opts.killBehavior,workerId:this.id})}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}handleError(e){return e instanceof Error?e.message:e}run(e){if(this.isMain)throw new Error("Cannot run a task in the main worker");const t=this.getTaskFunction(e.name);(e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name)(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}runSync(e,t){try{let s=this.beginTaskPerformance(t.name);const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,workerId:this.id,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??w,message:s,data:t.data},workerId:this.id,id:t.id})}finally{this.updateLastTaskTimestamp()}}runAsync(e,t){let s=this.beginTaskPerformance(t.name);e(t.data).then((e=>(s=this.endTaskPerformance(s),this.sendToMainWorker({data:e,taskPerformance:s,workerId:this.id,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??w,message:s,data:t.data},workerId:this.id,id:t.id})})).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:r.now(),...this.statistics.elu&&{elu:r.eventLoopUtilization()}}}endTaskPerformance(e){return this.checkStatistics(),{...e,...this.statistics.runTime&&{runTime:r.now()-e.timestamp},...this.statistics.elu&&{elu:r.eventLoopUtilization(e.elu)}}}checkStatistics(){if(null==this.statistics)throw new Error("Performance statistics computation requirements not set")}updateLastTaskTimestamp(){this.isMain||null==this.activeInterval||(this.lastTaskTimestamp=r.now())}}class Y extends G{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,t.worker,e,s)}handleReadyMessage(e){this.isMain||e.workerId!==this.id||null==e.ready||(this.getMainWorker()?.on("message",this.messageListener.bind(this)),this.sendToMainWorker({ready:!0,workerId:this.id}))}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}}class J extends G{port;constructor(e,t={}){super("worker-thread-pool:poolifier",h,c,e,t)}handleReadyMessage(e){this.isMain||e.workerId!==this.id||null==e.ready||null==e.port||(this.port=e.port,this.port.on("message",this.messageListener.bind(this)),this.sendToMainWorker({ready:!0,workerId:this.id}))}handleKillMessage(e){super.handleKillMessage(e),this.port?.unref(),this.port?.close()}get id(){return d}sendToMainWorker(e){this.port.postMessage(e)}handleError(e){return e}}export{Y as ClusterWorker,V as DynamicClusterPool,j as DynamicThreadPool,_ as FixedClusterPool,L as FixedThreadPool,R as KillBehaviors,C as Measurements,p as PoolEvents,m as PoolTypes,J as ThreadWorker,v as WorkerChoiceStrategies,D as WorkerTypes,W as availableParallelism};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MessageValue, PromiseResponseWrapper, Task } from '../utility-types';
|
|
2
2
|
import { type IPool, PoolEmitter, type PoolInfo, type PoolOptions, type PoolType, type TasksQueueOptions } from './pool';
|
|
3
|
-
import type { IWorker, IWorkerNode, WorkerType } from './worker';
|
|
3
|
+
import type { IWorker, IWorkerNode, WorkerInfo, WorkerType } from './worker';
|
|
4
4
|
import { type WorkerChoiceStrategy, type WorkerChoiceStrategyOptions } from './selection-strategies/selection-strategies-types';
|
|
5
5
|
import { WorkerChoiceStrategyContext } from './selection-strategies/worker-choice-strategy-context';
|
|
6
6
|
/**
|
|
@@ -102,7 +102,7 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
|
|
|
102
102
|
* @param worker - The worker.
|
|
103
103
|
* @returns The worker node key if found in the pool worker nodes, `-1` otherwise.
|
|
104
104
|
*/
|
|
105
|
-
|
|
105
|
+
protected getWorkerNodeKey(worker: Worker): number;
|
|
106
106
|
/** @inheritDoc */
|
|
107
107
|
setWorkerChoiceStrategy(workerChoiceStrategy: WorkerChoiceStrategy, workerChoiceStrategyOptions?: WorkerChoiceStrategyOptions): void;
|
|
108
108
|
/** @inheritDoc */
|
|
@@ -216,7 +216,7 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
|
|
|
216
216
|
* @param worker - The worker which should register a listener.
|
|
217
217
|
* @param listener - The message listener callback.
|
|
218
218
|
*/
|
|
219
|
-
|
|
219
|
+
protected abstract registerWorkerMessageListener<Message extends Data | Response>(worker: Worker, listener: (message: MessageValue<Message>) => void): void;
|
|
220
220
|
/**
|
|
221
221
|
* Function that can be hooked up when a worker has been newly created and moved to the pool worker nodes.
|
|
222
222
|
* Can be overridden.
|
|
@@ -224,7 +224,12 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
|
|
|
224
224
|
* @param worker - The newly created worker.
|
|
225
225
|
*/
|
|
226
226
|
protected afterWorkerSetup(worker: Worker): void;
|
|
227
|
-
|
|
227
|
+
/**
|
|
228
|
+
* Sends the startup message to the given worker.
|
|
229
|
+
*
|
|
230
|
+
* @param worker - The worker which should receive the startup message.
|
|
231
|
+
*/
|
|
232
|
+
protected abstract sendStartupMessageToWorker(worker: Worker): void;
|
|
228
233
|
private redistributeQueuedTasks;
|
|
229
234
|
/**
|
|
230
235
|
* This function is the listener registered for each worker message.
|
|
@@ -236,11 +241,20 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
|
|
|
236
241
|
private handleTaskExecutionResponse;
|
|
237
242
|
private checkAndEmitEvents;
|
|
238
243
|
/**
|
|
239
|
-
* Gets the worker information.
|
|
244
|
+
* Gets the worker information from the given worker node key.
|
|
240
245
|
*
|
|
241
246
|
* @param workerNodeKey - The worker node key.
|
|
247
|
+
* @returns The worker information.
|
|
242
248
|
*/
|
|
243
249
|
private getWorkerInfo;
|
|
250
|
+
/**
|
|
251
|
+
* Gets the worker information from the given worker.
|
|
252
|
+
*
|
|
253
|
+
* @param worker - The worker.
|
|
254
|
+
* @returns The worker information.
|
|
255
|
+
* @throws {@link https://nodejs.org/api/errors.html#class-error} If the worker is not found.
|
|
256
|
+
*/
|
|
257
|
+
protected getWorkerInfoByWorker(worker: Worker): WorkerInfo;
|
|
244
258
|
/**
|
|
245
259
|
* Adds the given worker in the pool worker nodes.
|
|
246
260
|
*
|
|
@@ -48,6 +48,10 @@ export declare class FixedClusterPool<Data = unknown, Response = unknown> extend
|
|
|
48
48
|
/** @inheritDoc */
|
|
49
49
|
protected sendToWorker(worker: Worker, message: MessageValue<Data>): void;
|
|
50
50
|
/** @inheritDoc */
|
|
51
|
+
protected sendStartupMessageToWorker(worker: Worker): void;
|
|
52
|
+
/** @inheritDoc */
|
|
53
|
+
protected registerWorkerMessageListener<Message extends Data | Response>(worker: Worker, listener: (message: MessageValue<Message>) => void): void;
|
|
54
|
+
/** @inheritDoc */
|
|
51
55
|
protected createWorker(): Worker;
|
|
52
56
|
/** @inheritDoc */
|
|
53
57
|
protected get type(): PoolType;
|
|
@@ -40,6 +40,10 @@ export declare class FixedThreadPool<Data = unknown, Response = unknown> extends
|
|
|
40
40
|
/** @inheritDoc */
|
|
41
41
|
protected sendToWorker(worker: Worker, message: MessageValue<Data>): void;
|
|
42
42
|
/** @inheritDoc */
|
|
43
|
+
protected sendStartupMessageToWorker(worker: Worker): void;
|
|
44
|
+
/** @inheritDoc */
|
|
45
|
+
protected registerWorkerMessageListener<Message extends Data | Response>(worker: Worker, listener: (message: MessageValue<Message>) => void): void;
|
|
46
|
+
/** @inheritDoc */
|
|
43
47
|
protected createWorker(): Worker;
|
|
44
48
|
/** @inheritDoc */
|
|
45
49
|
protected get type(): PoolType;
|
package/lib/pools/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "2.6.
|
|
1
|
+
export declare const version = "2.6.19";
|
|
@@ -36,6 +36,8 @@ export declare class WorkerNode<Worker extends IWorker, Data = unknown> implemen
|
|
|
36
36
|
/** @inheritdoc */
|
|
37
37
|
resetUsage(): void;
|
|
38
38
|
/** @inheritdoc */
|
|
39
|
+
closeChannel(): void;
|
|
40
|
+
/** @inheritdoc */
|
|
39
41
|
getTaskWorkerUsage(name: string): WorkerUsage | undefined;
|
|
40
42
|
private initWorkerInfo;
|
|
41
43
|
private initWorkerUsage;
|
package/lib/pools/worker.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { MessageChannel } from 'node:worker_threads';
|
|
1
3
|
import type { CircularArray } from '../circular-array';
|
|
2
4
|
import type { Task } from '../utility-types';
|
|
3
5
|
/**
|
|
@@ -117,6 +119,10 @@ export interface WorkerInfo {
|
|
|
117
119
|
* Ready flag.
|
|
118
120
|
*/
|
|
119
121
|
ready: boolean;
|
|
122
|
+
/**
|
|
123
|
+
* Message channel.
|
|
124
|
+
*/
|
|
125
|
+
messageChannel?: MessageChannel;
|
|
120
126
|
}
|
|
121
127
|
/**
|
|
122
128
|
* Worker usage statistics.
|
|
@@ -212,6 +218,10 @@ export interface IWorkerNode<Worker extends IWorker, Data = unknown> {
|
|
|
212
218
|
* Resets usage statistics .
|
|
213
219
|
*/
|
|
214
220
|
readonly resetUsage: () => void;
|
|
221
|
+
/**
|
|
222
|
+
* Close communication channel.
|
|
223
|
+
*/
|
|
224
|
+
readonly closeChannel: () => void;
|
|
215
225
|
/**
|
|
216
226
|
* Gets task worker usage statistics.
|
|
217
227
|
*/
|
package/lib/utility-types.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
import type { EventLoopUtilization } from 'node:perf_hooks';
|
|
4
|
+
import type { MessagePort } from 'node:worker_threads';
|
|
3
5
|
import type { KillBehavior } from './worker/worker-options';
|
|
4
6
|
import type { IWorker } from './pools/worker';
|
|
5
7
|
/**
|
|
@@ -113,6 +115,10 @@ export interface MessageValue<Data = unknown, ErrorData = unknown> extends Task<
|
|
|
113
115
|
* Whether the worker starts or stops its activity check.
|
|
114
116
|
*/
|
|
115
117
|
readonly checkActive?: boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Message port.
|
|
120
|
+
*/
|
|
121
|
+
readonly port?: MessagePort;
|
|
116
122
|
}
|
|
117
123
|
/**
|
|
118
124
|
* An object holding the execution response promise resolve/reject callbacks.
|
|
@@ -17,7 +17,7 @@ import type { TaskFunctions, WorkerAsyncFunction, WorkerFunction, WorkerSyncFunc
|
|
|
17
17
|
*/
|
|
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
21
|
protected readonly opts: WorkerOptions;
|
|
22
22
|
/**
|
|
23
23
|
* Worker id.
|
|
@@ -44,11 +44,11 @@ export declare abstract class AbstractWorker<MainWorker extends Worker | Message
|
|
|
44
44
|
*
|
|
45
45
|
* @param type - The type of async event.
|
|
46
46
|
* @param isMain - Whether this is the main worker or not.
|
|
47
|
-
* @param taskFunctions - Task function(s) processed by the worker when the pool's `execution` function is invoked. The first function is the default function.
|
|
48
47
|
* @param mainWorker - Reference to main worker.
|
|
48
|
+
* @param taskFunctions - Task function(s) processed by the worker when the pool's `execution` function is invoked. The first function is the default function.
|
|
49
49
|
* @param opts - Options for the worker.
|
|
50
50
|
*/
|
|
51
|
-
constructor(type: string, isMain: boolean, taskFunctions: WorkerFunction<Data, Response> | TaskFunctions<Data, Response>,
|
|
51
|
+
constructor(type: string, isMain: boolean, mainWorker: MainWorker, taskFunctions: WorkerFunction<Data, Response> | TaskFunctions<Data, Response>, opts?: WorkerOptions);
|
|
52
52
|
private checkWorkerOptions;
|
|
53
53
|
/**
|
|
54
54
|
* Checks if the `taskFunctions` parameter is passed to the constructor.
|
|
@@ -102,16 +102,24 @@ export declare abstract class AbstractWorker<MainWorker extends Worker | Message
|
|
|
102
102
|
* @throws {@link https://nodejs.org/api/errors.html#class-error} If the `name` parameter is a non-existing task function.
|
|
103
103
|
*/
|
|
104
104
|
setDefaultTaskFunction(name: string): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Handles the ready message sent by the main worker.
|
|
107
|
+
*
|
|
108
|
+
* @param message - The ready message.
|
|
109
|
+
*/
|
|
110
|
+
protected abstract handleReadyMessage(message: MessageValue<Data>): void;
|
|
105
111
|
/**
|
|
106
112
|
* Worker message listener.
|
|
107
113
|
*
|
|
108
114
|
* @param message - The received message.
|
|
109
115
|
*/
|
|
110
|
-
protected messageListener(message: MessageValue<Data
|
|
116
|
+
protected messageListener(message: MessageValue<Data>): void;
|
|
111
117
|
/**
|
|
112
|
-
*
|
|
118
|
+
* Handles a kill message sent by the main worker.
|
|
119
|
+
*
|
|
120
|
+
* @param message - The kill message.
|
|
113
121
|
*/
|
|
114
|
-
protected
|
|
122
|
+
protected handleKillMessage(message: MessageValue<Data>): void;
|
|
115
123
|
/**
|
|
116
124
|
* Starts the worker check active interval.
|
|
117
125
|
*/
|
|
@@ -27,6 +27,8 @@ export declare class ClusterWorker<Data = unknown, Response = unknown> extends A
|
|
|
27
27
|
*/
|
|
28
28
|
constructor(taskFunctions: WorkerFunction<Data, Response> | TaskFunctions<Data, Response>, opts?: WorkerOptions);
|
|
29
29
|
/** @inheritDoc */
|
|
30
|
+
protected handleReadyMessage(message: MessageValue<Data>): void;
|
|
31
|
+
/** @inheritDoc */
|
|
30
32
|
protected get id(): number;
|
|
31
33
|
/** @inheritDoc */
|
|
32
34
|
protected sendToMainWorker(message: MessageValue<Response>): void;
|
|
@@ -19,6 +19,10 @@ import type { TaskFunctions, WorkerFunction } from './worker-functions';
|
|
|
19
19
|
* @since 0.0.1
|
|
20
20
|
*/
|
|
21
21
|
export declare class ThreadWorker<Data = unknown, Response = unknown> extends AbstractWorker<MessagePort, Data, Response> {
|
|
22
|
+
/**
|
|
23
|
+
* Message port used to communicate with the main thread.
|
|
24
|
+
*/
|
|
25
|
+
private port;
|
|
22
26
|
/**
|
|
23
27
|
* Constructs a new poolifier thread worker.
|
|
24
28
|
*
|
|
@@ -26,6 +30,11 @@ export declare class ThreadWorker<Data = unknown, Response = unknown> extends Ab
|
|
|
26
30
|
* @param opts - Options for the worker.
|
|
27
31
|
*/
|
|
28
32
|
constructor(taskFunctions: WorkerFunction<Data, Response> | TaskFunctions<Data, Response>, opts?: WorkerOptions);
|
|
33
|
+
/** @inheritDoc */
|
|
34
|
+
protected handleReadyMessage(message: MessageValue<Data>): void;
|
|
35
|
+
/** @inheritDoc */
|
|
36
|
+
protected handleKillMessage(message: MessageValue<Data, unknown>): void;
|
|
37
|
+
/** @inheritDoc */
|
|
29
38
|
protected get id(): number;
|
|
30
39
|
/** @inheritDoc */
|
|
31
40
|
protected sendToMainWorker(message: MessageValue<Response>): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poolifier",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.19",
|
|
4
4
|
"description": "A fast, easy to use Node.js Worker Thread Pool and Cluster Pool implementation",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"mocha": "^10.2.0",
|
|
107
107
|
"mochawesome": "^7.1.3",
|
|
108
108
|
"prettier": "^3.0.0",
|
|
109
|
-
"release-it": "^16.1.
|
|
109
|
+
"release-it": "^16.1.3",
|
|
110
110
|
"rollup": "^3.26.3",
|
|
111
111
|
"rollup-plugin-analyzer": "^4.0.0",
|
|
112
112
|
"rollup-plugin-command": "^1.1.3",
|