poolifier 2.6.8 → 2.6.10

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