poolifier
Advanced tools
Comparing version 2.4.4 to 2.4.5
@@ -1,1 +0,1 @@ | ||
"use strict";var e,r=require("node:cluster"),t=require("node:crypto"),s=require("node:events"),i=require("node:os"),o=require("node:worker_threads"),n=require("node:async_hooks");!function(e){e.FIXED="fixed",e.DYNAMIC="dynamic"}(e||(e={}));const a=Object.freeze((()=>{})),h=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class k extends s{}const u=Object.freeze({full:"full",busy:"busy"}),c=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LESS_USED:"LESS_USED",LESS_BUSY:"LESS_BUSY",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN"});class l{pool;isDynamicPool;requiredStatistics={runTime:!1,avgRunTime:!1};constructor(r){this.pool=r,this.isDynamicPool=this.pool.type===e.DYNAMIC,this.choose.bind(this)}}class p extends l{requiredStatistics={runTime:!0,avgRunTime:!0};workerLastVirtualTaskTimestamp=new Map;reset(){return this.workerLastVirtualTaskTimestamp.clear(),!0}choose(){let e,r=1/0;for(const[t]of this.pool.workers.entries()){this.computeWorkerLastVirtualTaskTimestamp(t);const s=this.workerLastVirtualTaskTimestamp.get(t)?.end??0;s<r&&(r=s,e=t)}return e}remove(e){const r=this.workerLastVirtualTaskTimestamp.delete(e);for(const[r,t]of this.workerLastVirtualTaskTimestamp.entries())r>e&&this.workerLastVirtualTaskTimestamp.set(r-1,t);return r}computeWorkerLastVirtualTaskTimestamp(e){const r=Math.max(Date.now(),this.workerLastVirtualTaskTimestamp.get(e)?.end??-1/0);this.workerLastVirtualTaskTimestamp.set(e,{start:r,end:r+(this.pool.workers[e].tasksUsage.avgRunTime??0)})}}class m extends l{requiredStatistics={runTime:!0,avgRunTime:!1};reset(){return!0}choose(){const e=this.pool.findFreeWorkerKey();if(-1!==e)return e;let r,t=1/0;for(const[e,s]of this.pool.workers.entries()){const i=s.tasksUsage.runTime;if(0===i)return e;i<t&&(t=i,r=e)}return r}remove(e){return!0}}class d extends l{reset(){return!0}choose(){const e=this.pool.findFreeWorkerKey();if(-1!==e)return e;let r,t=1/0;for(const[e,s]of this.pool.workers.entries()){const i=s.tasksUsage,o=i.run+i.running;if(0===o)return e;o<t&&(t=o,r=e)}return r}remove(e){return!0}}class g extends l{nextWorkerId=0;reset(){return this.nextWorkerId=0,!0}choose(){const e=this.nextWorkerId;return this.nextWorkerId=this.nextWorkerId===this.pool.workers.length-1?0:this.nextWorkerId+1,e}remove(e){return this.nextWorkerId===e&&(0===this.pool.workers.length?this.nextWorkerId=0:this.nextWorkerId=this.nextWorkerId>this.pool.workers.length-1?this.pool.workers.length-1:this.nextWorkerId),!0}}class w extends l{requiredStatistics={runTime:!0,avgRunTime:!0};currentWorkerId=0;defaultWorkerWeight;workersTaskRunTime=new Map;constructor(e){super(e),this.defaultWorkerWeight=this.computeWorkerWeight(),this.initWorkersTaskRunTime()}reset(){return this.currentWorkerId=0,this.workersTaskRunTime.clear(),this.initWorkersTaskRunTime(),!0}choose(){const e=this.currentWorkerId;this.isDynamicPool&&!this.workersTaskRunTime.has(e)&&this.initWorkerTaskRunTime(e);const r=this.workersTaskRunTime.get(e)?.runTime??0,t=this.workersTaskRunTime.get(e)?.weight??this.defaultWorkerWeight;return r<t?this.setWorkerTaskRunTime(e,t,r+(this.getWorkerVirtualTaskRunTime(e)??0)):(this.currentWorkerId=this.currentWorkerId===this.pool.workers.length-1?0:this.currentWorkerId+1,this.setWorkerTaskRunTime(this.currentWorkerId,t,0)),e}remove(e){this.currentWorkerId===e&&(0===this.pool.workers.length?this.currentWorkerId=0:this.currentWorkerId=this.currentWorkerId>this.pool.workers.length-1?this.pool.workers.length-1:this.currentWorkerId);const r=this.workersTaskRunTime.delete(e);for(const[r,t]of this.workersTaskRunTime)r>e&&this.workersTaskRunTime.set(r-1,t);return r}initWorkersTaskRunTime(){for(const[e]of this.pool.workers.entries())this.initWorkerTaskRunTime(e)}initWorkerTaskRunTime(e){this.setWorkerTaskRunTime(e,this.defaultWorkerWeight,0)}setWorkerTaskRunTime(e,r,t){this.workersTaskRunTime.set(e,{weight:r,runTime:t})}getWorkerVirtualTaskRunTime(e){return this.pool.workers[e].tasksUsage.avgRunTime}computeWorkerWeight(){let e=0;for(const r of i.cpus()){const t=r.speed.toString().length-1;e+=1/(r.speed/Math.pow(10,t))*Math.pow(10,t)}return Math.round(e/i.cpus().length)}}class T{workerChoiceStrategyType;workerChoiceStrategies;constructor(e,r=c.ROUND_ROBIN){this.workerChoiceStrategyType=r,this.execute.bind(this),this.workerChoiceStrategies=new Map([[c.ROUND_ROBIN,new g(e)],[c.LESS_USED,new d(e)],[c.LESS_BUSY,new m(e)],[c.FAIR_SHARE,new p(e)],[c.WEIGHTED_ROUND_ROBIN,new w(e)]])}getRequiredStatistics(){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).requiredStatistics}setWorkerChoiceStrategy(e){this.workerChoiceStrategyType!==e&&(this.workerChoiceStrategyType=e),this.workerChoiceStrategies.get(this.workerChoiceStrategyType)?.reset()}execute(){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).choose()}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).remove(e)}}class W{numberOfWorkers;filePath;opts;workers=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;constructor(e,r,t){if(this.numberOfWorkers=e,this.filePath=r,this.opts=t,!this.isMain())throw new Error("Cannot start a pool from a worker!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.chooseWorker.bind(this),this.internalExecute.bind(this),this.checkAndEmitFull.bind(this),this.checkAndEmitBusy.bind(this),this.sendToWorker.bind(this),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker();!0===this.opts.enableEvents&&(this.emitter=new k),this.workerChoiceStrategyContext=new T(this,this.opts.workerChoiceStrategy)}checkFilePath(e){if(null==e||"string"==typeof e&&0===e.trim().length)throw new Error("Please specify a file with a worker implementation")}checkNumberOfWorkers(r){if(null==r)throw new Error("Cannot instantiate a pool without specifying the number of workers");if(!Number.isSafeInteger(r))throw new TypeError("Cannot instantiate a pool with a non integer number of workers");if(r<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===e.FIXED&&0===r)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){this.opts.workerChoiceStrategy=e.workerChoiceStrategy??c.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.enableEvents=e.enableEvents??!0}checkValidWorkerChoiceStrategy(e){if(!Object.values(c).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}get numberOfRunningTasks(){return this.promiseResponseMap.size}getWorkerKey(e){return this.workers.findIndex((r=>r.worker===e))}setWorkerChoiceStrategy(e){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e;for(const[e,r]of this.workers.entries())this.setWorker(e,r.worker,{run:0,running:0,runTime:0,avgRunTime:0,error:0});this.workerChoiceStrategyContext.setWorkerChoiceStrategy(e)}internalBusy(){return this.numberOfRunningTasks>=this.numberOfWorkers&&-1===this.findFreeWorkerKey()}findFreeWorkerKey(){return this.workers.findIndex((e=>0===e.tasksUsage.running))}async execute(e){const[r,s]=this.chooseWorker(),i=t.randomUUID(),o=this.internalExecute(r,s,i);return this.checkAndEmitFull(),this.checkAndEmitBusy(),this.sendToWorker(s,{data:e??{},id:i}),o}async destroy(){await Promise.all(this.workers.map((async e=>{await this.destroyWorker(e.worker)})))}setupHook(){}beforePromiseResponseHook(e){++this.workers[e].tasksUsage.running}afterPromiseResponseHook(e,r){const t=this.getWorkerTasksUsage(e);--t.running,++t.run,null!=r.error&&++t.error,this.workerChoiceStrategyContext.getRequiredStatistics().runTime&&(t.runTime+=r.runTime??0,this.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime&&0!==t.run&&(t.avgRunTime=t.runTime/t.run))}chooseWorker(){let r;if(this.type!==e.DYNAMIC||this.full||-1!==this.findFreeWorkerKey())r=this.workerChoiceStrategyContext.execute();else{const e=this.createAndSetupWorker();this.registerWorkerMessageListener(e,(r=>{var t;t=h.HARD,(r.kill===t||null!=r.kill&&0===this.getWorkerTasksUsage(e)?.running)&&this.destroyWorker(e)})),r=this.getWorkerKey(e)}return[r,this.workers[r].worker]}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??a),e.on("error",this.opts.errorHandler??a),e.on("online",this.opts.onlineHandler??a),e.on("exit",this.opts.exitHandler??a),e.once("exit",(()=>{this.removeWorker(e)})),this.pushWorker(e,{run:0,running:0,runTime:0,avgRunTime:0,error:0}),this.afterWorkerSetup(e),e}workerListener(){return e=>{if(null!=e.id){const r=this.promiseResponseMap.get(e.id);null!=r&&(null!=e.error?r.reject(e.error):r.resolve(e.data),this.afterPromiseResponseHook(r.worker,e),this.promiseResponseMap.delete(e.id))}}}async internalExecute(e,r,t){return this.beforePromiseResponseHook(e),await new Promise(((e,s)=>{this.promiseResponseMap.set(t,{resolve:e,reject:s,worker:r})}))}checkAndEmitBusy(){!0===this.opts.enableEvents&&this.busy&&this.emitter?.emit(u.busy)}checkAndEmitFull(){this.type===e.DYNAMIC&&!0===this.opts.enableEvents&&this.full&&this.emitter?.emit(u.full)}getWorkerTasksUsage(e){const r=this.getWorkerKey(e);if(-1!==r)return this.workers[r].tasksUsage;throw new Error("Worker could not be found in the pool")}pushWorker(e,r){this.workers.push({worker:e,tasksUsage:r})}setWorker(e,r,t){this.workers[e]={worker:r,tasksUsage:t}}removeWorker(e){const r=this.getWorkerKey(e);this.workers.splice(r,1),this.workerChoiceStrategyContext.remove(r)}}class f extends W{opts;constructor(e,r,t={}){super(e,r,t),this.opts=t}setupHook(){r.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return r.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.kill()}sendToWorker(e,r){e.send(r)}registerWorkerMessageListener(e,r){e.on("message",r)}createWorker(){return r.fork(this.opts.env)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get full(){return this.workers.length===this.numberOfWorkers}get busy(){return this.internalBusy()}}class y extends W{constructor(e,r,t={}){super(e,r,t)}isMain(){return o.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,r){e.postMessage(r)}registerWorkerMessageListener(e,r){e.port2?.on("message",r)}createWorker(){return new o.Worker(this.filePath,{env:o.SHARE_ENV})}afterWorkerSetup(e){const{port1:r,port2:t}=new o.MessageChannel;e.postMessage({parent:r},[r]),e.port1=r,e.port2=t,this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get full(){return this.workers.length===this.numberOfWorkers}get busy(){return this.internalBusy()}}const S=6e4,R=h.SOFT;class x extends n.AsyncResource{isMain;mainWorker;opts;lastTaskTimestamp;aliveInterval;constructor(e,r,t,s,i={killBehavior:R,maxInactiveTime:S}){super(e),this.isMain=r,this.mainWorker=s,this.opts=i,this.checkFunctionInput(t),this.checkWorkerOptions(this.opts),this.isMain||(this.lastTaskTimestamp=Date.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??S)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",(e=>{this.messageListener(e,t)}))}messageListener(e,r){null!=e.data&&null!=e.id?!0===this.opts.async?this.runInAsyncScope(this.runAsync.bind(this),this,r,e):this.runInAsyncScope(this.run.bind(this),this,r,e):null!=e.parent?this.mainWorker=e.parent:null!=e.kill&&(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??R,this.opts.maxInactiveTime=e.maxInactiveTime??S,this.opts.async=e.async??!1}checkFunctionInput(e){if(null==e)throw new Error("fn parameter is mandatory");if("function"!=typeof e)throw new TypeError("fn parameter is not a function")}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker was not set");return this.mainWorker}checkAlive(){Date.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??S)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}run(e,r){try{const t=Date.now(),s=e(r.data),i=Date.now()-t;this.sendToMainWorker({data:s,id:r.id,runTime:i})}catch(e){const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})}finally{!this.isMain&&(this.lastTaskTimestamp=Date.now())}}runAsync(e,r){const t=Date.now();e(r.data).then((e=>{const s=Date.now()-t;return this.sendToMainWorker({data:e,id:r.id,runTime:s}),null})).catch((e=>{const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})})).finally((()=>{!this.isMain&&(this.lastTaskTimestamp=Date.now())})).catch(a)}}exports.ClusterWorker=class extends x{constructor(e,t={}){super("worker-cluster-pool:poolifier",r.isPrimary,e,r.worker,t)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}},exports.DynamicClusterPool=class extends f{max;constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get full(){return this.workers.length===this.max}get busy(){return this.full&&-1===this.findFreeWorkerKey()}},exports.DynamicThreadPool=class extends y{max;constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get full(){return this.workers.length===this.max}get busy(){return this.full&&-1===this.findFreeWorkerKey()}},exports.FixedClusterPool=f,exports.FixedThreadPool=y,exports.KillBehaviors=h,exports.PoolEvents=u,exports.ThreadWorker=class extends x{constructor(e,r={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,r)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=c; | ||
"use strict";var e,r=require("node:cluster"),t=require("node:crypto"),s=require("node:events"),i=require("node:os"),o=require("node:worker_threads"),n=require("node:async_hooks");!function(e){e.FIXED="fixed",e.DYNAMIC="dynamic"}(e||(e={}));const a=Object.freeze((()=>{})),h=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class u extends s{}const k=Object.freeze({full:"full",busy:"busy"}),c=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LESS_USED:"LESS_USED",LESS_BUSY:"LESS_BUSY",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN"});class l{pool;opts;isDynamicPool;requiredStatistics={runTime:!1,avgRunTime:!1,medRunTime:!1};constructor(r,t={medRunTime:!1}){this.pool=r,this.opts=t,this.checkOptions(),this.isDynamicPool=this.pool.type===e.DYNAMIC,this.choose.bind(this)}checkOptions(){this.requiredStatistics.avgRunTime&&!0===this.opts.medRunTime&&(this.requiredStatistics.medRunTime=!0)}}class d extends l{requiredStatistics={runTime:!0,avgRunTime:!0,medRunTime:!1};workerLastVirtualTaskTimestamp=new Map;reset(){return this.workerLastVirtualTaskTimestamp.clear(),!0}choose(){let e,r=1/0;for(const[t]of this.pool.workerNodes.entries()){this.computeWorkerLastVirtualTaskTimestamp(t);const s=this.workerLastVirtualTaskTimestamp.get(t)?.end??0;s<r&&(r=s,e=t)}return e}remove(e){const r=this.workerLastVirtualTaskTimestamp.delete(e);for(const[r,t]of this.workerLastVirtualTaskTimestamp.entries())r>e&&this.workerLastVirtualTaskTimestamp.set(r-1,t);return r}computeWorkerLastVirtualTaskTimestamp(e){const r=Math.max(performance.now(),this.workerLastVirtualTaskTimestamp.get(e)?.end??-1/0),t=this.requiredStatistics.medRunTime?this.pool.workerNodes[e].tasksUsage.medRunTime:this.pool.workerNodes[e].tasksUsage.avgRunTime;this.workerLastVirtualTaskTimestamp.set(e,{start:r,end:r+(t??0)})}}class p extends l{requiredStatistics={runTime:!0,avgRunTime:!1,medRunTime:!1};reset(){return!0}choose(){const e=this.pool.findFreeWorkerNodeKey();if(-1!==e)return e;let r,t=1/0;for(const[e,s]of this.pool.workerNodes.entries()){const i=s.tasksUsage.runTime;if(0===i)return e;i<t&&(t=i,r=e)}return r}remove(e){return!0}}class m extends l{reset(){return!0}choose(){const e=this.pool.findFreeWorkerNodeKey();if(-1!==e)return e;let r,t=1/0;for(const[e,s]of this.pool.workerNodes.entries()){const i=s.tasksUsage,o=i.run+i.running;if(0===o)return e;o<t&&(t=o,r=e)}return r}remove(e){return!0}}class g extends l{nextWorkerNodeId=0;reset(){return this.nextWorkerNodeId=0,!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.nextWorkerNodeId>this.pool.workerNodes.length-1?this.pool.workerNodes.length-1:this.nextWorkerNodeId),!0}}class T extends l{requiredStatistics={runTime:!0,avgRunTime:!0,medRunTime:!1};currentWorkerNodeId=0;defaultWorkerWeight;workersTaskRunTime=new Map;constructor(e,r){super(e,r),this.defaultWorkerWeight=this.computeWorkerWeight(),this.initWorkersTaskRunTime()}reset(){return this.currentWorkerNodeId=0,this.workersTaskRunTime.clear(),this.initWorkersTaskRunTime(),!0}choose(){const e=this.currentWorkerNodeId;this.isDynamicPool&&!this.workersTaskRunTime.has(e)&&this.initWorkerTaskRunTime(e);const r=this.workersTaskRunTime.get(e)?.runTime??0,t=this.workersTaskRunTime.get(e)?.weight??this.defaultWorkerWeight;return r<t?this.setWorkerTaskRunTime(e,t,r+(this.getWorkerVirtualTaskRunTime(e)??0)):(this.currentWorkerNodeId=this.currentWorkerNodeId===this.pool.workerNodes.length-1?0:this.currentWorkerNodeId+1,this.setWorkerTaskRunTime(this.currentWorkerNodeId,t,0)),e}remove(e){this.currentWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.currentWorkerNodeId=0:this.currentWorkerNodeId=this.currentWorkerNodeId>this.pool.workerNodes.length-1?this.pool.workerNodes.length-1:this.currentWorkerNodeId);const r=this.workersTaskRunTime.delete(e);for(const[r,t]of this.workersTaskRunTime)r>e&&this.workersTaskRunTime.set(r-1,t);return r}initWorkersTaskRunTime(){for(const[e]of this.pool.workerNodes.entries())this.initWorkerTaskRunTime(e)}initWorkerTaskRunTime(e){this.setWorkerTaskRunTime(e,this.defaultWorkerWeight,0)}setWorkerTaskRunTime(e,r,t){this.workersTaskRunTime.set(e,{weight:r,runTime:t})}getWorkerVirtualTaskRunTime(e){return this.requiredStatistics.medRunTime?this.pool.workerNodes[e].tasksUsage.medRunTime:this.pool.workerNodes[e].tasksUsage.avgRunTime}computeWorkerWeight(){let e=0;for(const r of i.cpus()){const t=r.speed.toString().length-1;e+=1/(r.speed/Math.pow(10,t))*Math.pow(10,t)}return Math.round(e/i.cpus().length)}}class w{workerChoiceStrategyType;workerChoiceStrategies;constructor(e,r=c.ROUND_ROBIN,t={medRunTime:!1}){this.workerChoiceStrategyType=r,this.execute.bind(this),this.workerChoiceStrategies=new Map([[c.ROUND_ROBIN,new g(e,t)],[c.LESS_USED,new m(e,t)],[c.LESS_BUSY,new p(e,t)],[c.FAIR_SHARE,new d(e,t)],[c.WEIGHTED_ROUND_ROBIN,new T(e,t)]])}getRequiredStatistics(){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).requiredStatistics}setWorkerChoiceStrategy(e){this.workerChoiceStrategyType!==e&&(this.workerChoiceStrategyType=e),this.workerChoiceStrategies.get(this.workerChoiceStrategyType)?.reset()}execute(){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).choose()}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).remove(e)}}class W extends Array{size;constructor(e=1024,...r){super(),this.checkSize(e),this.size=e,arguments.length>1&&this.push(...r)}push(...e){const r=super.push(...e);return r>this.size&&super.splice(0,r-this.size),this.length}unshift(...e){return super.unshift(...e)>this.size&&super.splice(this.size,e.length),this.length}concat(...e){const r=super.concat(e);return r.size=this.size,r.length>r.size&&r.splice(0,r.length-r.size),r}splice(e,r,...t){let s;return arguments.length>=3&&void 0!==r?(s=super.splice(e,r),this.push(...t)):s=2===arguments.length?super.splice(e,r):super.splice(e),s}resize(e){if(this.checkSize(e),0===e)this.length=0;else if(e<this.size)for(let r=e;r<this.size;r++)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{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;constructor(e,r,t){if(this.numberOfWorkers=e,this.filePath=r,this.opts=t,!this.isMain())throw new Error("Cannot start a pool from a worker!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.chooseWorkerNode.bind(this),this.internalExecute.bind(this),this.checkAndEmitEvents.bind(this),this.sendToWorker.bind(this),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker();!0===this.opts.enableEvents&&(this.emitter=new u),this.workerChoiceStrategyContext=new w(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions)}checkFilePath(e){if(null==e||"string"==typeof e&&0===e.trim().length)throw new Error("Please specify a file with a worker implementation")}checkNumberOfWorkers(r){if(null==r)throw new Error("Cannot instantiate a pool without specifying the number of workers");if(!Number.isSafeInteger(r))throw new TypeError("Cannot instantiate a pool with a non integer number of workers");if(r<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===e.FIXED&&0===r)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){this.opts.workerChoiceStrategy=e.workerChoiceStrategy??c.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??{medRunTime:!1},this.opts.enableEvents=e.enableEvents??!0,this.opts.enableTasksQueue=e.enableTasksQueue??!1}checkValidWorkerChoiceStrategy(e){if(!Object.values(c).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}get numberOfRunningTasks(){return this.workerNodes.reduce(((e,r)=>e+r.tasksUsage.running),0)}get numberOfQueuedTasks(){return!1===this.opts.enableTasksQueue?0:this.workerNodes.reduce(((e,r)=>e+r.tasksQueue.length),0)}getWorkerNodeKey(e){return this.workerNodes.findIndex((r=>r.worker===e))}setWorkerChoiceStrategy(e){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e;for(const[e,r]of this.workerNodes.entries())this.setWorkerNode(e,r.worker,{run:0,running:0,runTime:0,runTimeHistory:new W,avgRunTime:0,medRunTime:0,error:0},r.tasksQueue);this.workerChoiceStrategyContext.setWorkerChoiceStrategy(e)}internalBusy(){return this.numberOfRunningTasks>=this.numberOfWorkers&&-1===this.findFreeWorkerNodeKey()}findFreeWorkerNodeKey(){return this.workerNodes.findIndex((e=>0===e.tasksUsage?.running))}async execute(e){const[r,s]=this.chooseWorkerNode(),i={data:e??{},id:t.randomUUID()},o=this.internalExecute(r,s,i);let n=i;return!0===this.opts.enableTasksQueue&&(this.busy||this.tasksQueueSize(r)>0)&&(n=this.enqueueDequeueTask(r,i)),this.sendToWorker(s.worker,n),this.checkAndEmitEvents(),o}async destroy(){await Promise.all(this.workerNodes.map((async e=>{this.flushTasksQueueByWorker(e.worker),await this.destroyWorker(e.worker)})))}setupHook(){}beforePromiseResponseHook(e){++this.workerNodes[e].tasksUsage.running}afterPromiseResponseHook(e,r){const t=this.getWorkerTasksUsage(e);--t.running,++t.run,null!=r.error&&++t.error,this.workerChoiceStrategyContext.getRequiredStatistics().runTime&&(t.runTime+=r.runTime??0,this.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime&&0!==t.run&&(t.avgRunTime=t.runTime/t.run),this.workerChoiceStrategyContext.getRequiredStatistics().medRunTime&&(t.runTimeHistory.push(r.runTime??0),t.medRunTime=(e=>{if(Array.isArray(e)&&1===e.length)return e[0];const r=e.slice().sort(((e,r)=>e-r)),t=Math.floor(r.length/2);return r.length%2==0?r[t/2]:(r[t-1]+r[t])/2})(t.runTimeHistory)))}chooseWorkerNode(){let r;if(this.type!==e.DYNAMIC||this.full||-1!==this.findFreeWorkerNodeKey())r=this.workerChoiceStrategyContext.execute();else{const e=this.createAndSetupWorker();this.registerWorkerMessageListener(e,(r=>{var t;t=h.HARD,(r.kill===t||null!=r.kill&&0===this.getWorkerTasksUsage(e)?.running)&&(this.flushTasksQueueByWorker(e),this.destroyWorker(e))})),r=this.getWorkerNodeKey(e)}return[r,this.workerNodes[r]]}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??a),e.on("error",this.opts.errorHandler??a),e.on("online",this.opts.onlineHandler??a),e.on("exit",this.opts.exitHandler??a),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.pushWorkerNode(e),this.afterWorkerSetup(e),e}workerListener(){return e=>{if(null!=e.id){const r=this.promiseResponseMap.get(e.id);if(null!=r){null!=e.error?r.reject(e.error):r.resolve(e.data),this.afterPromiseResponseHook(r.worker,e),this.promiseResponseMap.delete(e.id);const t=this.getWorkerNodeKey(r.worker);!0===this.opts.enableTasksQueue&&this.tasksQueueSize(t)>0&&this.sendToWorker(r.worker,this.dequeueTask(t))}}}}async internalExecute(e,r,t){return this.beforePromiseResponseHook(e),await new Promise(((e,s)=>{this.promiseResponseMap.set(t.id,{resolve:e,reject:s,worker:r.worker})}))}checkAndEmitEvents(){!0===this.opts.enableEvents&&(this.busy&&this.emitter?.emit(k.busy),this.type===e.DYNAMIC&&this.full&&this.emitter?.emit(k.full))}getWorkerTasksUsage(e){const r=this.getWorkerNodeKey(e);if(-1!==r)return this.workerNodes[r].tasksUsage;throw new Error("Worker could not be found in the pool worker nodes")}pushWorkerNode(e){return this.workerNodes.push({worker:e,tasksUsage:{run:0,running:0,runTime:0,runTimeHistory:new W,avgRunTime:0,medRunTime:0,error:0},tasksQueue:[]})}setWorkerNode(e,r,t,s){this.workerNodes[e]={worker:r,tasksUsage:t,tasksQueue:s}}removeWorkerNode(e){const r=this.getWorkerNodeKey(e);this.workerNodes.splice(r,1),this.workerChoiceStrategyContext.remove(r)}enqueueDequeueTask(e,r){return this.enqueueTask(e,r),this.dequeueTask(e)}enqueueTask(e,r){this.workerNodes[e].tasksQueue.push(r)}dequeueTask(e){return this.workerNodes[e].tasksQueue.shift()}tasksQueueSize(e){return this.workerNodes[e].tasksQueue.length}flushTasksQueue(e){if(this.tasksQueueSize(e)>0){for(const r of this.workerNodes[e].tasksQueue)this.sendToWorker(this.workerNodes[e].worker,r);this.workerNodes[e].tasksQueue=[]}}flushTasksQueueByWorker(e){const r=this.getWorkerNodeKey(e);this.flushTasksQueue(r)}}class y extends f{opts;constructor(e,r,t={}){super(e,r,t),this.opts=t}setupHook(){r.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return r.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.kill()}sendToWorker(e,r){e.send(r)}registerWorkerMessageListener(e,r){e.on("message",r)}createWorker(){return r.fork(this.opts.env)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get full(){return this.workerNodes.length===this.numberOfWorkers}get busy(){return this.internalBusy()}}class N extends f{constructor(e,r,t={}){super(e,r,t)}isMain(){return o.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,r){e.postMessage(r)}registerWorkerMessageListener(e,r){e.port2?.on("message",r)}createWorker(){return new o.Worker(this.filePath,{env:o.SHARE_ENV})}afterWorkerSetup(e){const{port1:r,port2:t}=new o.MessageChannel;e.postMessage({parent:r},[r]),e.port1=r,e.port2=t,this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get full(){return this.workerNodes.length===this.numberOfWorkers}get busy(){return this.internalBusy()}}const S=6e4,R=h.SOFT;class x extends n.AsyncResource{isMain;mainWorker;opts;lastTaskTimestamp;aliveInterval;constructor(e,r,t,s,i={killBehavior:R,maxInactiveTime:S}){super(e),this.isMain=r,this.mainWorker=s,this.opts=i,this.checkFunctionInput(t),this.checkWorkerOptions(this.opts),this.isMain||(this.lastTaskTimestamp=performance.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??S)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",(e=>{this.messageListener(e,t)}))}messageListener(e,r){null!=e.data&&null!=e.id?!0===this.opts.async?this.runInAsyncScope(this.runAsync.bind(this),this,r,e):this.runInAsyncScope(this.run.bind(this),this,r,e):null!=e.parent?this.mainWorker=e.parent:null!=e.kill&&(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??R,this.opts.maxInactiveTime=e.maxInactiveTime??S,this.opts.async=e.async??!1}checkFunctionInput(e){if(null==e)throw new Error("fn parameter is mandatory");if("function"!=typeof e)throw new TypeError("fn parameter is not a function")}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker was not set");return this.mainWorker}checkAlive(){performance.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??S)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}run(e,r){try{const t=performance.now(),s=e(r.data),i=performance.now()-t;this.sendToMainWorker({data:s,id:r.id,runTime:i})}catch(e){const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})}finally{!this.isMain&&(this.lastTaskTimestamp=performance.now())}}runAsync(e,r){const t=performance.now();e(r.data).then((e=>{const s=performance.now()-t;return this.sendToMainWorker({data:e,id:r.id,runTime:s}),null})).catch((e=>{const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})})).finally((()=>{!this.isMain&&(this.lastTaskTimestamp=performance.now())})).catch(a)}}exports.ClusterWorker=class extends x{constructor(e,t={}){super("worker-cluster-pool:poolifier",r.isPrimary,e,r.worker,t)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}},exports.DynamicClusterPool=class extends y{max;constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get full(){return this.workerNodes.length===this.max}get busy(){return this.full&&-1===this.findFreeWorkerNodeKey()}},exports.DynamicThreadPool=class extends N{max;constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get full(){return this.workerNodes.length===this.max}get busy(){return this.full&&-1===this.findFreeWorkerNodeKey()}},exports.FixedClusterPool=y,exports.FixedThreadPool=N,exports.KillBehaviors=h,exports.PoolEvents=k,exports.ThreadWorker=class extends x{constructor(e,r={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,r)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=c; |
@@ -6,5 +6,5 @@ export { DynamicClusterPool } from './pools/cluster/dynamic'; | ||
export type { IPool, PoolEmitter, PoolOptions, PoolEvent } from './pools/pool'; | ||
export type { ErrorHandler, ExitHandler, MessageHandler, OnlineHandler } from './pools/pool-worker'; | ||
export type { ErrorHandler, ExitHandler, MessageHandler, OnlineHandler } from './pools/worker'; | ||
export { WorkerChoiceStrategies } from './pools/selection-strategies/selection-strategies-types'; | ||
export type { WorkerChoiceStrategy } from './pools/selection-strategies/selection-strategies-types'; | ||
export type { WorkerChoiceStrategy, WorkerChoiceStrategyOptions } from './pools/selection-strategies/selection-strategies-types'; | ||
export { DynamicThreadPool } from './pools/thread/dynamic'; | ||
@@ -11,0 +11,0 @@ export { FixedThreadPool } from './pools/thread/fixed'; |
@@ -1,1 +0,1 @@ | ||
"use strict";var e,r=require("node:cluster"),t=require("node:crypto"),s=require("node:events"),i=require("node:os"),o=require("node:worker_threads"),n=require("node:async_hooks");!function(e){e.FIXED="fixed",e.DYNAMIC="dynamic"}(e||(e={}));const a=Object.freeze((()=>{})),h=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class k extends s{}const u=Object.freeze({full:"full",busy:"busy"}),c=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LESS_USED:"LESS_USED",LESS_BUSY:"LESS_BUSY",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN"});class l{pool;isDynamicPool;requiredStatistics={runTime:!1,avgRunTime:!1};constructor(r){this.pool=r,this.isDynamicPool=this.pool.type===e.DYNAMIC,this.choose.bind(this)}}class p extends l{requiredStatistics={runTime:!0,avgRunTime:!0};workerLastVirtualTaskTimestamp=new Map;reset(){return this.workerLastVirtualTaskTimestamp.clear(),!0}choose(){let e,r=1/0;for(const[t]of this.pool.workers.entries()){this.computeWorkerLastVirtualTaskTimestamp(t);const s=this.workerLastVirtualTaskTimestamp.get(t)?.end??0;s<r&&(r=s,e=t)}return e}remove(e){const r=this.workerLastVirtualTaskTimestamp.delete(e);for(const[r,t]of this.workerLastVirtualTaskTimestamp.entries())r>e&&this.workerLastVirtualTaskTimestamp.set(r-1,t);return r}computeWorkerLastVirtualTaskTimestamp(e){const r=Math.max(Date.now(),this.workerLastVirtualTaskTimestamp.get(e)?.end??-1/0);this.workerLastVirtualTaskTimestamp.set(e,{start:r,end:r+(this.pool.workers[e].tasksUsage.avgRunTime??0)})}}class m extends l{requiredStatistics={runTime:!0,avgRunTime:!1};reset(){return!0}choose(){const e=this.pool.findFreeWorkerKey();if(-1!==e)return e;let r,t=1/0;for(const[e,s]of this.pool.workers.entries()){const i=s.tasksUsage.runTime;if(0===i)return e;i<t&&(t=i,r=e)}return r}remove(e){return!0}}class d extends l{reset(){return!0}choose(){const e=this.pool.findFreeWorkerKey();if(-1!==e)return e;let r,t=1/0;for(const[e,s]of this.pool.workers.entries()){const i=s.tasksUsage,o=i.run+i.running;if(0===o)return e;o<t&&(t=o,r=e)}return r}remove(e){return!0}}class g extends l{nextWorkerId=0;reset(){return this.nextWorkerId=0,!0}choose(){const e=this.nextWorkerId;return this.nextWorkerId=this.nextWorkerId===this.pool.workers.length-1?0:this.nextWorkerId+1,e}remove(e){return this.nextWorkerId===e&&(0===this.pool.workers.length?this.nextWorkerId=0:this.nextWorkerId=this.nextWorkerId>this.pool.workers.length-1?this.pool.workers.length-1:this.nextWorkerId),!0}}class w extends l{requiredStatistics={runTime:!0,avgRunTime:!0};currentWorkerId=0;defaultWorkerWeight;workersTaskRunTime=new Map;constructor(e){super(e),this.defaultWorkerWeight=this.computeWorkerWeight(),this.initWorkersTaskRunTime()}reset(){return this.currentWorkerId=0,this.workersTaskRunTime.clear(),this.initWorkersTaskRunTime(),!0}choose(){const e=this.currentWorkerId;this.isDynamicPool&&!this.workersTaskRunTime.has(e)&&this.initWorkerTaskRunTime(e);const r=this.workersTaskRunTime.get(e)?.runTime??0,t=this.workersTaskRunTime.get(e)?.weight??this.defaultWorkerWeight;return r<t?this.setWorkerTaskRunTime(e,t,r+(this.getWorkerVirtualTaskRunTime(e)??0)):(this.currentWorkerId=this.currentWorkerId===this.pool.workers.length-1?0:this.currentWorkerId+1,this.setWorkerTaskRunTime(this.currentWorkerId,t,0)),e}remove(e){this.currentWorkerId===e&&(0===this.pool.workers.length?this.currentWorkerId=0:this.currentWorkerId=this.currentWorkerId>this.pool.workers.length-1?this.pool.workers.length-1:this.currentWorkerId);const r=this.workersTaskRunTime.delete(e);for(const[r,t]of this.workersTaskRunTime)r>e&&this.workersTaskRunTime.set(r-1,t);return r}initWorkersTaskRunTime(){for(const[e]of this.pool.workers.entries())this.initWorkerTaskRunTime(e)}initWorkerTaskRunTime(e){this.setWorkerTaskRunTime(e,this.defaultWorkerWeight,0)}setWorkerTaskRunTime(e,r,t){this.workersTaskRunTime.set(e,{weight:r,runTime:t})}getWorkerVirtualTaskRunTime(e){return this.pool.workers[e].tasksUsage.avgRunTime}computeWorkerWeight(){let e=0;for(const r of i.cpus()){const t=r.speed.toString().length-1;e+=1/(r.speed/Math.pow(10,t))*Math.pow(10,t)}return Math.round(e/i.cpus().length)}}class T{workerChoiceStrategyType;workerChoiceStrategies;constructor(e,r=c.ROUND_ROBIN){this.workerChoiceStrategyType=r,this.execute.bind(this),this.workerChoiceStrategies=new Map([[c.ROUND_ROBIN,new g(e)],[c.LESS_USED,new d(e)],[c.LESS_BUSY,new m(e)],[c.FAIR_SHARE,new p(e)],[c.WEIGHTED_ROUND_ROBIN,new w(e)]])}getRequiredStatistics(){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).requiredStatistics}setWorkerChoiceStrategy(e){this.workerChoiceStrategyType!==e&&(this.workerChoiceStrategyType=e),this.workerChoiceStrategies.get(this.workerChoiceStrategyType)?.reset()}execute(){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).choose()}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).remove(e)}}class W{numberOfWorkers;filePath;opts;workers=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;constructor(e,r,t){if(this.numberOfWorkers=e,this.filePath=r,this.opts=t,!this.isMain())throw new Error("Cannot start a pool from a worker!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.chooseWorker.bind(this),this.internalExecute.bind(this),this.checkAndEmitFull.bind(this),this.checkAndEmitBusy.bind(this),this.sendToWorker.bind(this),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker();!0===this.opts.enableEvents&&(this.emitter=new k),this.workerChoiceStrategyContext=new T(this,this.opts.workerChoiceStrategy)}checkFilePath(e){if(null==e||"string"==typeof e&&0===e.trim().length)throw new Error("Please specify a file with a worker implementation")}checkNumberOfWorkers(r){if(null==r)throw new Error("Cannot instantiate a pool without specifying the number of workers");if(!Number.isSafeInteger(r))throw new TypeError("Cannot instantiate a pool with a non integer number of workers");if(r<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===e.FIXED&&0===r)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){this.opts.workerChoiceStrategy=e.workerChoiceStrategy??c.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.enableEvents=e.enableEvents??!0}checkValidWorkerChoiceStrategy(e){if(!Object.values(c).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}get numberOfRunningTasks(){return this.promiseResponseMap.size}getWorkerKey(e){return this.workers.findIndex((r=>r.worker===e))}setWorkerChoiceStrategy(e){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e;for(const[e,r]of this.workers.entries())this.setWorker(e,r.worker,{run:0,running:0,runTime:0,avgRunTime:0,error:0});this.workerChoiceStrategyContext.setWorkerChoiceStrategy(e)}internalBusy(){return this.numberOfRunningTasks>=this.numberOfWorkers&&-1===this.findFreeWorkerKey()}findFreeWorkerKey(){return this.workers.findIndex((e=>0===e.tasksUsage.running))}async execute(e){const[r,s]=this.chooseWorker(),i=t.randomUUID(),o=this.internalExecute(r,s,i);return this.checkAndEmitFull(),this.checkAndEmitBusy(),this.sendToWorker(s,{data:e??{},id:i}),o}async destroy(){await Promise.all(this.workers.map((async e=>{await this.destroyWorker(e.worker)})))}setupHook(){}beforePromiseResponseHook(e){++this.workers[e].tasksUsage.running}afterPromiseResponseHook(e,r){const t=this.getWorkerTasksUsage(e);--t.running,++t.run,null!=r.error&&++t.error,this.workerChoiceStrategyContext.getRequiredStatistics().runTime&&(t.runTime+=r.runTime??0,this.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime&&0!==t.run&&(t.avgRunTime=t.runTime/t.run))}chooseWorker(){let r;if(this.type!==e.DYNAMIC||this.full||-1!==this.findFreeWorkerKey())r=this.workerChoiceStrategyContext.execute();else{const e=this.createAndSetupWorker();this.registerWorkerMessageListener(e,(r=>{var t;t=h.HARD,(r.kill===t||null!=r.kill&&0===this.getWorkerTasksUsage(e)?.running)&&this.destroyWorker(e)})),r=this.getWorkerKey(e)}return[r,this.workers[r].worker]}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??a),e.on("error",this.opts.errorHandler??a),e.on("online",this.opts.onlineHandler??a),e.on("exit",this.opts.exitHandler??a),e.once("exit",(()=>{this.removeWorker(e)})),this.pushWorker(e,{run:0,running:0,runTime:0,avgRunTime:0,error:0}),this.afterWorkerSetup(e),e}workerListener(){return e=>{if(null!=e.id){const r=this.promiseResponseMap.get(e.id);null!=r&&(null!=e.error?r.reject(e.error):r.resolve(e.data),this.afterPromiseResponseHook(r.worker,e),this.promiseResponseMap.delete(e.id))}}}async internalExecute(e,r,t){return this.beforePromiseResponseHook(e),await new Promise(((e,s)=>{this.promiseResponseMap.set(t,{resolve:e,reject:s,worker:r})}))}checkAndEmitBusy(){!0===this.opts.enableEvents&&this.busy&&this.emitter?.emit(u.busy)}checkAndEmitFull(){this.type===e.DYNAMIC&&!0===this.opts.enableEvents&&this.full&&this.emitter?.emit(u.full)}getWorkerTasksUsage(e){const r=this.getWorkerKey(e);if(-1!==r)return this.workers[r].tasksUsage;throw new Error("Worker could not be found in the pool")}pushWorker(e,r){this.workers.push({worker:e,tasksUsage:r})}setWorker(e,r,t){this.workers[e]={worker:r,tasksUsage:t}}removeWorker(e){const r=this.getWorkerKey(e);this.workers.splice(r,1),this.workerChoiceStrategyContext.remove(r)}}class f extends W{opts;constructor(e,r,t={}){super(e,r,t),this.opts=t}setupHook(){r.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return r.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.kill()}sendToWorker(e,r){e.send(r)}registerWorkerMessageListener(e,r){e.on("message",r)}createWorker(){return r.fork(this.opts.env)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get full(){return this.workers.length===this.numberOfWorkers}get busy(){return this.internalBusy()}}class y extends W{constructor(e,r,t={}){super(e,r,t)}isMain(){return o.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,r){e.postMessage(r)}registerWorkerMessageListener(e,r){e.port2?.on("message",r)}createWorker(){return new o.Worker(this.filePath,{env:o.SHARE_ENV})}afterWorkerSetup(e){const{port1:r,port2:t}=new o.MessageChannel;e.postMessage({parent:r},[r]),e.port1=r,e.port2=t,this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get full(){return this.workers.length===this.numberOfWorkers}get busy(){return this.internalBusy()}}const S=6e4,R=h.SOFT;class x extends n.AsyncResource{isMain;mainWorker;opts;lastTaskTimestamp;aliveInterval;constructor(e,r,t,s,i={killBehavior:R,maxInactiveTime:S}){super(e),this.isMain=r,this.mainWorker=s,this.opts=i,this.checkFunctionInput(t),this.checkWorkerOptions(this.opts),this.isMain||(this.lastTaskTimestamp=Date.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??S)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",(e=>{this.messageListener(e,t)}))}messageListener(e,r){null!=e.data&&null!=e.id?!0===this.opts.async?this.runInAsyncScope(this.runAsync.bind(this),this,r,e):this.runInAsyncScope(this.run.bind(this),this,r,e):null!=e.parent?this.mainWorker=e.parent:null!=e.kill&&(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??R,this.opts.maxInactiveTime=e.maxInactiveTime??S,this.opts.async=e.async??!1}checkFunctionInput(e){if(null==e)throw new Error("fn parameter is mandatory");if("function"!=typeof e)throw new TypeError("fn parameter is not a function")}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker was not set");return this.mainWorker}checkAlive(){Date.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??S)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}run(e,r){try{const t=Date.now(),s=e(r.data),i=Date.now()-t;this.sendToMainWorker({data:s,id:r.id,runTime:i})}catch(e){const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})}finally{!this.isMain&&(this.lastTaskTimestamp=Date.now())}}runAsync(e,r){const t=Date.now();e(r.data).then((e=>{const s=Date.now()-t;return this.sendToMainWorker({data:e,id:r.id,runTime:s}),null})).catch((e=>{const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})})).finally((()=>{!this.isMain&&(this.lastTaskTimestamp=Date.now())})).catch(a)}}exports.ClusterWorker=class extends x{constructor(e,t={}){super("worker-cluster-pool:poolifier",r.isPrimary,e,r.worker,t)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}},exports.DynamicClusterPool=class extends f{max;constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get full(){return this.workers.length===this.max}get busy(){return this.full&&-1===this.findFreeWorkerKey()}},exports.DynamicThreadPool=class extends y{max;constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get full(){return this.workers.length===this.max}get busy(){return this.full&&-1===this.findFreeWorkerKey()}},exports.FixedClusterPool=f,exports.FixedThreadPool=y,exports.KillBehaviors=h,exports.PoolEvents=u,exports.ThreadWorker=class extends x{constructor(e,r={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,r)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=c; | ||
"use strict";var e,r=require("node:cluster"),t=require("node:crypto"),s=require("node:events"),i=require("node:os"),o=require("node:worker_threads"),n=require("node:async_hooks");!function(e){e.FIXED="fixed",e.DYNAMIC="dynamic"}(e||(e={}));const a=Object.freeze((()=>{})),h=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class u extends s{}const k=Object.freeze({full:"full",busy:"busy"}),c=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LESS_USED:"LESS_USED",LESS_BUSY:"LESS_BUSY",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN"});class l{pool;opts;isDynamicPool;requiredStatistics={runTime:!1,avgRunTime:!1,medRunTime:!1};constructor(r,t={medRunTime:!1}){this.pool=r,this.opts=t,this.checkOptions(),this.isDynamicPool=this.pool.type===e.DYNAMIC,this.choose.bind(this)}checkOptions(){this.requiredStatistics.avgRunTime&&!0===this.opts.medRunTime&&(this.requiredStatistics.medRunTime=!0)}}class d extends l{requiredStatistics={runTime:!0,avgRunTime:!0,medRunTime:!1};workerLastVirtualTaskTimestamp=new Map;reset(){return this.workerLastVirtualTaskTimestamp.clear(),!0}choose(){let e,r=1/0;for(const[t]of this.pool.workerNodes.entries()){this.computeWorkerLastVirtualTaskTimestamp(t);const s=this.workerLastVirtualTaskTimestamp.get(t)?.end??0;s<r&&(r=s,e=t)}return e}remove(e){const r=this.workerLastVirtualTaskTimestamp.delete(e);for(const[r,t]of this.workerLastVirtualTaskTimestamp.entries())r>e&&this.workerLastVirtualTaskTimestamp.set(r-1,t);return r}computeWorkerLastVirtualTaskTimestamp(e){const r=Math.max(performance.now(),this.workerLastVirtualTaskTimestamp.get(e)?.end??-1/0),t=this.requiredStatistics.medRunTime?this.pool.workerNodes[e].tasksUsage.medRunTime:this.pool.workerNodes[e].tasksUsage.avgRunTime;this.workerLastVirtualTaskTimestamp.set(e,{start:r,end:r+(t??0)})}}class p extends l{requiredStatistics={runTime:!0,avgRunTime:!1,medRunTime:!1};reset(){return!0}choose(){const e=this.pool.findFreeWorkerNodeKey();if(-1!==e)return e;let r,t=1/0;for(const[e,s]of this.pool.workerNodes.entries()){const i=s.tasksUsage.runTime;if(0===i)return e;i<t&&(t=i,r=e)}return r}remove(e){return!0}}class m extends l{reset(){return!0}choose(){const e=this.pool.findFreeWorkerNodeKey();if(-1!==e)return e;let r,t=1/0;for(const[e,s]of this.pool.workerNodes.entries()){const i=s.tasksUsage,o=i.run+i.running;if(0===o)return e;o<t&&(t=o,r=e)}return r}remove(e){return!0}}class g extends l{nextWorkerNodeId=0;reset(){return this.nextWorkerNodeId=0,!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.nextWorkerNodeId>this.pool.workerNodes.length-1?this.pool.workerNodes.length-1:this.nextWorkerNodeId),!0}}class T extends l{requiredStatistics={runTime:!0,avgRunTime:!0,medRunTime:!1};currentWorkerNodeId=0;defaultWorkerWeight;workersTaskRunTime=new Map;constructor(e,r){super(e,r),this.defaultWorkerWeight=this.computeWorkerWeight(),this.initWorkersTaskRunTime()}reset(){return this.currentWorkerNodeId=0,this.workersTaskRunTime.clear(),this.initWorkersTaskRunTime(),!0}choose(){const e=this.currentWorkerNodeId;this.isDynamicPool&&!this.workersTaskRunTime.has(e)&&this.initWorkerTaskRunTime(e);const r=this.workersTaskRunTime.get(e)?.runTime??0,t=this.workersTaskRunTime.get(e)?.weight??this.defaultWorkerWeight;return r<t?this.setWorkerTaskRunTime(e,t,r+(this.getWorkerVirtualTaskRunTime(e)??0)):(this.currentWorkerNodeId=this.currentWorkerNodeId===this.pool.workerNodes.length-1?0:this.currentWorkerNodeId+1,this.setWorkerTaskRunTime(this.currentWorkerNodeId,t,0)),e}remove(e){this.currentWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.currentWorkerNodeId=0:this.currentWorkerNodeId=this.currentWorkerNodeId>this.pool.workerNodes.length-1?this.pool.workerNodes.length-1:this.currentWorkerNodeId);const r=this.workersTaskRunTime.delete(e);for(const[r,t]of this.workersTaskRunTime)r>e&&this.workersTaskRunTime.set(r-1,t);return r}initWorkersTaskRunTime(){for(const[e]of this.pool.workerNodes.entries())this.initWorkerTaskRunTime(e)}initWorkerTaskRunTime(e){this.setWorkerTaskRunTime(e,this.defaultWorkerWeight,0)}setWorkerTaskRunTime(e,r,t){this.workersTaskRunTime.set(e,{weight:r,runTime:t})}getWorkerVirtualTaskRunTime(e){return this.requiredStatistics.medRunTime?this.pool.workerNodes[e].tasksUsage.medRunTime:this.pool.workerNodes[e].tasksUsage.avgRunTime}computeWorkerWeight(){let e=0;for(const r of i.cpus()){const t=r.speed.toString().length-1;e+=1/(r.speed/Math.pow(10,t))*Math.pow(10,t)}return Math.round(e/i.cpus().length)}}class w{workerChoiceStrategyType;workerChoiceStrategies;constructor(e,r=c.ROUND_ROBIN,t={medRunTime:!1}){this.workerChoiceStrategyType=r,this.execute.bind(this),this.workerChoiceStrategies=new Map([[c.ROUND_ROBIN,new g(e,t)],[c.LESS_USED,new m(e,t)],[c.LESS_BUSY,new p(e,t)],[c.FAIR_SHARE,new d(e,t)],[c.WEIGHTED_ROUND_ROBIN,new T(e,t)]])}getRequiredStatistics(){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).requiredStatistics}setWorkerChoiceStrategy(e){this.workerChoiceStrategyType!==e&&(this.workerChoiceStrategyType=e),this.workerChoiceStrategies.get(this.workerChoiceStrategyType)?.reset()}execute(){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).choose()}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).remove(e)}}class W extends Array{size;constructor(e=1024,...r){super(),this.checkSize(e),this.size=e,arguments.length>1&&this.push(...r)}push(...e){const r=super.push(...e);return r>this.size&&super.splice(0,r-this.size),this.length}unshift(...e){return super.unshift(...e)>this.size&&super.splice(this.size,e.length),this.length}concat(...e){const r=super.concat(e);return r.size=this.size,r.length>r.size&&r.splice(0,r.length-r.size),r}splice(e,r,...t){let s;return arguments.length>=3&&void 0!==r?(s=super.splice(e,r),this.push(...t)):s=2===arguments.length?super.splice(e,r):super.splice(e),s}resize(e){if(this.checkSize(e),0===e)this.length=0;else if(e<this.size)for(let r=e;r<this.size;r++)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{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;constructor(e,r,t){if(this.numberOfWorkers=e,this.filePath=r,this.opts=t,!this.isMain())throw new Error("Cannot start a pool from a worker!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.chooseWorkerNode.bind(this),this.internalExecute.bind(this),this.checkAndEmitEvents.bind(this),this.sendToWorker.bind(this),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker();!0===this.opts.enableEvents&&(this.emitter=new u),this.workerChoiceStrategyContext=new w(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions)}checkFilePath(e){if(null==e||"string"==typeof e&&0===e.trim().length)throw new Error("Please specify a file with a worker implementation")}checkNumberOfWorkers(r){if(null==r)throw new Error("Cannot instantiate a pool without specifying the number of workers");if(!Number.isSafeInteger(r))throw new TypeError("Cannot instantiate a pool with a non integer number of workers");if(r<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===e.FIXED&&0===r)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){this.opts.workerChoiceStrategy=e.workerChoiceStrategy??c.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??{medRunTime:!1},this.opts.enableEvents=e.enableEvents??!0,this.opts.enableTasksQueue=e.enableTasksQueue??!1}checkValidWorkerChoiceStrategy(e){if(!Object.values(c).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}get numberOfRunningTasks(){return this.workerNodes.reduce(((e,r)=>e+r.tasksUsage.running),0)}get numberOfQueuedTasks(){return!1===this.opts.enableTasksQueue?0:this.workerNodes.reduce(((e,r)=>e+r.tasksQueue.length),0)}getWorkerNodeKey(e){return this.workerNodes.findIndex((r=>r.worker===e))}setWorkerChoiceStrategy(e){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e;for(const[e,r]of this.workerNodes.entries())this.setWorkerNode(e,r.worker,{run:0,running:0,runTime:0,runTimeHistory:new W,avgRunTime:0,medRunTime:0,error:0},r.tasksQueue);this.workerChoiceStrategyContext.setWorkerChoiceStrategy(e)}internalBusy(){return this.numberOfRunningTasks>=this.numberOfWorkers&&-1===this.findFreeWorkerNodeKey()}findFreeWorkerNodeKey(){return this.workerNodes.findIndex((e=>0===e.tasksUsage?.running))}async execute(e){const[r,s]=this.chooseWorkerNode(),i={data:e??{},id:t.randomUUID()},o=this.internalExecute(r,s,i);let n=i;return!0===this.opts.enableTasksQueue&&(this.busy||this.tasksQueueSize(r)>0)&&(n=this.enqueueDequeueTask(r,i)),this.sendToWorker(s.worker,n),this.checkAndEmitEvents(),o}async destroy(){await Promise.all(this.workerNodes.map((async e=>{this.flushTasksQueueByWorker(e.worker),await this.destroyWorker(e.worker)})))}setupHook(){}beforePromiseResponseHook(e){++this.workerNodes[e].tasksUsage.running}afterPromiseResponseHook(e,r){const t=this.getWorkerTasksUsage(e);--t.running,++t.run,null!=r.error&&++t.error,this.workerChoiceStrategyContext.getRequiredStatistics().runTime&&(t.runTime+=r.runTime??0,this.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime&&0!==t.run&&(t.avgRunTime=t.runTime/t.run),this.workerChoiceStrategyContext.getRequiredStatistics().medRunTime&&(t.runTimeHistory.push(r.runTime??0),t.medRunTime=(e=>{if(Array.isArray(e)&&1===e.length)return e[0];const r=e.slice().sort(((e,r)=>e-r)),t=Math.floor(r.length/2);return r.length%2==0?r[t/2]:(r[t-1]+r[t])/2})(t.runTimeHistory)))}chooseWorkerNode(){let r;if(this.type!==e.DYNAMIC||this.full||-1!==this.findFreeWorkerNodeKey())r=this.workerChoiceStrategyContext.execute();else{const e=this.createAndSetupWorker();this.registerWorkerMessageListener(e,(r=>{var t;t=h.HARD,(r.kill===t||null!=r.kill&&0===this.getWorkerTasksUsage(e)?.running)&&(this.flushTasksQueueByWorker(e),this.destroyWorker(e))})),r=this.getWorkerNodeKey(e)}return[r,this.workerNodes[r]]}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??a),e.on("error",this.opts.errorHandler??a),e.on("online",this.opts.onlineHandler??a),e.on("exit",this.opts.exitHandler??a),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.pushWorkerNode(e),this.afterWorkerSetup(e),e}workerListener(){return e=>{if(null!=e.id){const r=this.promiseResponseMap.get(e.id);if(null!=r){null!=e.error?r.reject(e.error):r.resolve(e.data),this.afterPromiseResponseHook(r.worker,e),this.promiseResponseMap.delete(e.id);const t=this.getWorkerNodeKey(r.worker);!0===this.opts.enableTasksQueue&&this.tasksQueueSize(t)>0&&this.sendToWorker(r.worker,this.dequeueTask(t))}}}}async internalExecute(e,r,t){return this.beforePromiseResponseHook(e),await new Promise(((e,s)=>{this.promiseResponseMap.set(t.id,{resolve:e,reject:s,worker:r.worker})}))}checkAndEmitEvents(){!0===this.opts.enableEvents&&(this.busy&&this.emitter?.emit(k.busy),this.type===e.DYNAMIC&&this.full&&this.emitter?.emit(k.full))}getWorkerTasksUsage(e){const r=this.getWorkerNodeKey(e);if(-1!==r)return this.workerNodes[r].tasksUsage;throw new Error("Worker could not be found in the pool worker nodes")}pushWorkerNode(e){return this.workerNodes.push({worker:e,tasksUsage:{run:0,running:0,runTime:0,runTimeHistory:new W,avgRunTime:0,medRunTime:0,error:0},tasksQueue:[]})}setWorkerNode(e,r,t,s){this.workerNodes[e]={worker:r,tasksUsage:t,tasksQueue:s}}removeWorkerNode(e){const r=this.getWorkerNodeKey(e);this.workerNodes.splice(r,1),this.workerChoiceStrategyContext.remove(r)}enqueueDequeueTask(e,r){return this.enqueueTask(e,r),this.dequeueTask(e)}enqueueTask(e,r){this.workerNodes[e].tasksQueue.push(r)}dequeueTask(e){return this.workerNodes[e].tasksQueue.shift()}tasksQueueSize(e){return this.workerNodes[e].tasksQueue.length}flushTasksQueue(e){if(this.tasksQueueSize(e)>0){for(const r of this.workerNodes[e].tasksQueue)this.sendToWorker(this.workerNodes[e].worker,r);this.workerNodes[e].tasksQueue=[]}}flushTasksQueueByWorker(e){const r=this.getWorkerNodeKey(e);this.flushTasksQueue(r)}}class y extends f{opts;constructor(e,r,t={}){super(e,r,t),this.opts=t}setupHook(){r.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return r.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.kill()}sendToWorker(e,r){e.send(r)}registerWorkerMessageListener(e,r){e.on("message",r)}createWorker(){return r.fork(this.opts.env)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get full(){return this.workerNodes.length===this.numberOfWorkers}get busy(){return this.internalBusy()}}class N extends f{constructor(e,r,t={}){super(e,r,t)}isMain(){return o.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,r){e.postMessage(r)}registerWorkerMessageListener(e,r){e.port2?.on("message",r)}createWorker(){return new o.Worker(this.filePath,{env:o.SHARE_ENV})}afterWorkerSetup(e){const{port1:r,port2:t}=new o.MessageChannel;e.postMessage({parent:r},[r]),e.port1=r,e.port2=t,this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get full(){return this.workerNodes.length===this.numberOfWorkers}get busy(){return this.internalBusy()}}const S=6e4,R=h.SOFT;class x extends n.AsyncResource{isMain;mainWorker;opts;lastTaskTimestamp;aliveInterval;constructor(e,r,t,s,i={killBehavior:R,maxInactiveTime:S}){super(e),this.isMain=r,this.mainWorker=s,this.opts=i,this.checkFunctionInput(t),this.checkWorkerOptions(this.opts),this.isMain||(this.lastTaskTimestamp=performance.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??S)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",(e=>{this.messageListener(e,t)}))}messageListener(e,r){null!=e.data&&null!=e.id?!0===this.opts.async?this.runInAsyncScope(this.runAsync.bind(this),this,r,e):this.runInAsyncScope(this.run.bind(this),this,r,e):null!=e.parent?this.mainWorker=e.parent:null!=e.kill&&(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??R,this.opts.maxInactiveTime=e.maxInactiveTime??S,this.opts.async=e.async??!1}checkFunctionInput(e){if(null==e)throw new Error("fn parameter is mandatory");if("function"!=typeof e)throw new TypeError("fn parameter is not a function")}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker was not set");return this.mainWorker}checkAlive(){performance.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??S)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}run(e,r){try{const t=performance.now(),s=e(r.data),i=performance.now()-t;this.sendToMainWorker({data:s,id:r.id,runTime:i})}catch(e){const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})}finally{!this.isMain&&(this.lastTaskTimestamp=performance.now())}}runAsync(e,r){const t=performance.now();e(r.data).then((e=>{const s=performance.now()-t;return this.sendToMainWorker({data:e,id:r.id,runTime:s}),null})).catch((e=>{const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})})).finally((()=>{!this.isMain&&(this.lastTaskTimestamp=performance.now())})).catch(a)}}exports.ClusterWorker=class extends x{constructor(e,t={}){super("worker-cluster-pool:poolifier",r.isPrimary,e,r.worker,t)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}},exports.DynamicClusterPool=class extends y{max;constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get full(){return this.workerNodes.length===this.max}get busy(){return this.full&&-1===this.findFreeWorkerNodeKey()}},exports.DynamicThreadPool=class extends N{max;constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get full(){return this.workerNodes.length===this.max}get busy(){return this.full&&-1===this.findFreeWorkerNodeKey()}},exports.FixedClusterPool=y,exports.FixedThreadPool=N,exports.KillBehaviors=h,exports.PoolEvents=k,exports.ThreadWorker=class extends x{constructor(e,r={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,r)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=c; |
import type { MessageValue, PromiseResponseWrapper } from '../utility-types'; | ||
import { type PoolOptions } from './pool'; | ||
import { PoolEmitter } from './pool'; | ||
import type { IPoolInternal, WorkerType } from './pool-internal'; | ||
import type { IPoolInternal } from './pool-internal'; | ||
import { PoolType } from './pool-internal'; | ||
import type { IPoolWorker } from './pool-worker'; | ||
import type { IWorker, WorkerNode } from './worker'; | ||
import { type WorkerChoiceStrategy } from './selection-strategies/selection-strategies-types'; | ||
@@ -16,3 +16,3 @@ import { WorkerChoiceStrategyContext } from './selection-strategies/worker-choice-strategy-context'; | ||
*/ | ||
export declare abstract class AbstractPool<Worker extends IPoolWorker, Data = unknown, Response = unknown> implements IPoolInternal<Worker, Data, Response> { | ||
export declare abstract class AbstractPool<Worker extends IWorker, Data = unknown, Response = unknown> implements IPoolInternal<Worker, Data, Response> { | ||
readonly numberOfWorkers: number; | ||
@@ -22,12 +22,12 @@ readonly filePath: string; | ||
/** @inheritDoc */ | ||
readonly workers: Array<WorkerType<Worker>>; | ||
readonly workerNodes: Array<WorkerNode<Worker, Data>>; | ||
/** @inheritDoc */ | ||
readonly emitter?: PoolEmitter; | ||
/** | ||
* The promise response map. | ||
* The execution response promise map. | ||
* | ||
* - `key`: The message id of each submitted task. | ||
* - `value`: An object that contains the worker, the promise resolve and reject callbacks. | ||
* - `value`: An object that contains the worker, the execution response promise resolve and reject callbacks. | ||
* | ||
* When we receive a message from the worker we get a map entry with the promise resolve/reject bound to the message. | ||
* When we receive a message from the worker, we get a map entry with the promise resolve/reject bound to the message id. | ||
*/ | ||
@@ -56,12 +56,16 @@ protected promiseResponseMap: Map<string, PromiseResponseWrapper<Worker, Response>>; | ||
/** | ||
* Number of tasks concurrently running in the pool. | ||
* Number of tasks running in the pool. | ||
*/ | ||
private get numberOfRunningTasks(); | ||
/** | ||
* Gets the given worker key. | ||
* Number of tasks queued in the pool. | ||
*/ | ||
private get numberOfQueuedTasks(); | ||
/** | ||
* Gets the given worker its worker node key. | ||
* | ||
* @param worker - The worker. | ||
* @returns The worker key if the worker is found in the pool, `-1` otherwise. | ||
* @returns The worker node key if the worker is found in the pool worker nodes, `-1` otherwise. | ||
*/ | ||
private getWorkerKey; | ||
private getWorkerNodeKey; | ||
/** @inheritDoc */ | ||
@@ -75,3 +79,3 @@ setWorkerChoiceStrategy(workerChoiceStrategy: WorkerChoiceStrategy): void; | ||
/** @inheritDoc */ | ||
findFreeWorkerKey(): number; | ||
findFreeWorkerNodeKey(): number; | ||
/** @inheritDoc */ | ||
@@ -82,10 +86,9 @@ execute(data: Data): Promise<Response>; | ||
/** | ||
* Shutdowns given worker in the pool. | ||
* Shutdowns the given worker. | ||
* | ||
* @param worker - A worker within `workers`. | ||
* @param worker - A worker within `workerNodes`. | ||
*/ | ||
protected abstract destroyWorker(worker: Worker): void | Promise<void>; | ||
/** | ||
* Setup hook that can be overridden by a Poolifier pool implementation | ||
* to run code before workers are created in the abstract constructor. | ||
* Setup hook to run code before worker node are created in the abstract constructor. | ||
* Can be overridden | ||
@@ -104,5 +107,5 @@ * | ||
* | ||
* @param workerKey - The worker key. | ||
* @param workerNodeKey - The worker node key. | ||
*/ | ||
protected beforePromiseResponseHook(workerKey: number): void; | ||
protected beforePromiseResponseHook(workerNodeKey: number): void; | ||
/** | ||
@@ -117,9 +120,9 @@ * Hook executed after the worker task promise resolution. | ||
/** | ||
* Chooses a worker for the next task. | ||
* Chooses a worker node for the next task. | ||
* | ||
* The default uses a round robin algorithm to distribute the load. | ||
* | ||
* @returns [worker key, worker]. | ||
* @returns [worker node key, worker node]. | ||
*/ | ||
protected chooseWorker(): [number, Worker]; | ||
protected chooseWorkerNode(): [number, WorkerNode<Worker, Data>]; | ||
/** | ||
@@ -133,3 +136,3 @@ * Sends a message to the given worker. | ||
/** | ||
* Registers a listener callback on a given worker. | ||
* Registers a listener callback on the given worker. | ||
* | ||
@@ -145,3 +148,3 @@ * @param worker - The worker which should register a listener. | ||
/** | ||
* Function that can be hooked up when a worker has been newly created and moved to the workers registry. | ||
* Function that can be hooked up when a worker has been newly created and moved to the pool worker nodes. | ||
* | ||
@@ -151,7 +154,6 @@ * Can be used to update the `maxListeners` or binding the `main-worker`\<-\>`worker` connection if not bind by default. | ||
* @param worker - The newly created worker. | ||
* @virtual | ||
*/ | ||
protected abstract afterWorkerSetup(worker: Worker): void; | ||
/** | ||
* Creates a new worker for this pool and sets it up completely. | ||
* Creates a new worker and sets it up completely in the pool worker nodes. | ||
* | ||
@@ -162,3 +164,3 @@ * @returns New, completely set up worker. | ||
/** | ||
* This function is the listener registered for each worker. | ||
* This function is the listener registered for each worker message. | ||
* | ||
@@ -169,6 +171,5 @@ * @returns The listener function to execute when a message is received from a worker. | ||
private internalExecute; | ||
private checkAndEmitBusy; | ||
private checkAndEmitFull; | ||
private checkAndEmitEvents; | ||
/** | ||
* Gets the given worker tasks usage in the pool. | ||
* Gets the given worker its tasks usage in the pool. | ||
* | ||
@@ -180,22 +181,29 @@ * @param worker - The worker. | ||
/** | ||
* Pushes the given worker in the pool. | ||
* Pushes the given worker in the pool worker nodes. | ||
* | ||
* @param worker - The worker. | ||
* @param tasksUsage - The worker tasks usage. | ||
* @returns The worker nodes length. | ||
*/ | ||
private pushWorker; | ||
private pushWorkerNode; | ||
/** | ||
* Sets the given worker in the pool. | ||
* Sets the given worker in the pool worker nodes. | ||
* | ||
* @param workerKey - The worker key. | ||
* @param workerNodeKey - The worker node key. | ||
* @param worker - The worker. | ||
* @param tasksUsage - The worker tasks usage. | ||
* @param tasksQueue - The worker task queue. | ||
*/ | ||
private setWorker; | ||
private setWorkerNode; | ||
/** | ||
* Removes the given worker from the pool. | ||
* Removes the given worker from the pool worker nodes. | ||
* | ||
* @param worker - The worker that will be removed. | ||
* @param worker - The worker. | ||
*/ | ||
protected removeWorker(worker: Worker): void; | ||
private removeWorkerNode; | ||
private enqueueDequeueTask; | ||
private enqueueTask; | ||
private dequeueTask; | ||
private tasksQueueSize; | ||
private flushTasksQueue; | ||
private flushTasksQueueByWorker; | ||
} |
import type { IPool } from './pool'; | ||
import type { IPoolWorker } from './pool-worker'; | ||
import type { IWorker, WorkerNode } from './worker'; | ||
/** | ||
@@ -13,21 +13,2 @@ * Internal pool types. | ||
/** | ||
* Internal tasks usage statistics. | ||
*/ | ||
export interface TasksUsage { | ||
run: number; | ||
running: number; | ||
runTime: number; | ||
avgRunTime: number; | ||
error: number; | ||
} | ||
/** | ||
* Internal worker type. | ||
* | ||
* @typeParam Worker - Type of worker type items which manages this pool. | ||
*/ | ||
export interface WorkerType<Worker extends IPoolWorker> { | ||
worker: Worker; | ||
tasksUsage: TasksUsage; | ||
} | ||
/** | ||
* Internal contract definition for a poolifier pool. | ||
@@ -39,7 +20,7 @@ * | ||
*/ | ||
export interface IPoolInternal<Worker extends IPoolWorker, Data = unknown, Response = unknown> extends IPool<Data, Response> { | ||
export interface IPoolInternal<Worker extends IWorker, Data = unknown, Response = unknown> extends IPool<Data, Response> { | ||
/** | ||
* Pool worker type items array. | ||
* Pool worker nodes. | ||
*/ | ||
readonly workers: Array<WorkerType<Worker>>; | ||
readonly workerNodes: Array<WorkerNode<Worker, Data>>; | ||
/** | ||
@@ -64,11 +45,11 @@ * Pool type. | ||
/** | ||
* Finds a free worker key based on the number of tasks the worker has applied. | ||
* Finds a free worker node key based on the number of tasks the worker has applied. | ||
* | ||
* If a worker is found with `0` running tasks, it is detected as free and its key is returned. | ||
* If a worker is found with `0` running tasks, it is detected as free and its worker node key is returned. | ||
* | ||
* If no free worker is found, `-1` is returned. | ||
* | ||
* @returns A worker key if there is one, `-1` otherwise. | ||
* @returns A worker node key if there is one, `-1` otherwise. | ||
*/ | ||
findFreeWorkerKey: () => number; | ||
findFreeWorkerNodeKey: () => number; | ||
} |
/// <reference types="node" /> | ||
import EventEmitter from 'node:events'; | ||
import type { ErrorHandler, ExitHandler, MessageHandler, OnlineHandler } from './pool-worker'; | ||
import type { WorkerChoiceStrategy } from './selection-strategies/selection-strategies-types'; | ||
import type { ErrorHandler, ExitHandler, MessageHandler, OnlineHandler } from './worker'; | ||
import type { WorkerChoiceStrategy, WorkerChoiceStrategyOptions } from './selection-strategies/selection-strategies-types'; | ||
/** | ||
@@ -46,2 +46,6 @@ * Pool events emitter. | ||
/** | ||
* The worker choice strategy options. | ||
*/ | ||
workerChoiceStrategyOptions?: WorkerChoiceStrategyOptions; | ||
/** | ||
* Pool events emission. | ||
@@ -52,2 +56,9 @@ * | ||
enableEvents?: boolean; | ||
/** | ||
* Pool worker tasks queue. | ||
* | ||
* @experimental | ||
* @defaultValue false | ||
*/ | ||
enableTasksQueue?: boolean; | ||
} | ||
@@ -54,0 +65,0 @@ /** |
import type { IPoolInternal } from '../pool-internal'; | ||
import type { IPoolWorker } from '../pool-worker'; | ||
import type { IWorkerChoiceStrategy, RequiredStatistics } from './selection-strategies-types'; | ||
import type { IWorker } from '../worker'; | ||
import type { IWorkerChoiceStrategy, RequiredStatistics, WorkerChoiceStrategyOptions } from './selection-strategies-types'; | ||
/** | ||
@@ -11,8 +11,9 @@ * Worker choice strategy abstract base class. | ||
*/ | ||
export declare abstract class AbstractWorkerChoiceStrategy<Worker extends IPoolWorker, Data = unknown, Response = unknown> implements IWorkerChoiceStrategy { | ||
export declare abstract class AbstractWorkerChoiceStrategy<Worker extends IWorker, Data = unknown, Response = unknown> implements IWorkerChoiceStrategy { | ||
protected readonly pool: IPoolInternal<Worker, Data, Response>; | ||
protected readonly opts: WorkerChoiceStrategyOptions; | ||
/** @inheritDoc */ | ||
protected readonly isDynamicPool: boolean; | ||
/** @inheritDoc */ | ||
requiredStatistics: RequiredStatistics; | ||
readonly requiredStatistics: RequiredStatistics; | ||
/** | ||
@@ -22,4 +23,6 @@ * Constructs a worker choice strategy bound to the pool. | ||
* @param pool - The pool instance. | ||
* @param opts - The worker choice strategy options. | ||
*/ | ||
constructor(pool: IPoolInternal<Worker, Data, Response>); | ||
constructor(pool: IPoolInternal<Worker, Data, Response>, opts?: WorkerChoiceStrategyOptions); | ||
private checkOptions; | ||
/** @inheritDoc */ | ||
@@ -30,3 +33,3 @@ abstract reset(): boolean; | ||
/** @inheritDoc */ | ||
abstract remove(workerKey: number): boolean; | ||
abstract remove(workerNodeKey: number): boolean; | ||
} |
@@ -1,2 +0,2 @@ | ||
import type { IPoolWorker } from '../pool-worker'; | ||
import type { IWorker } from '../worker'; | ||
import { AbstractWorkerChoiceStrategy } from './abstract-worker-choice-strategy'; | ||
@@ -12,7 +12,7 @@ import type { IWorkerChoiceStrategy, RequiredStatistics } from './selection-strategies-types'; | ||
*/ | ||
export declare class FairShareWorkerChoiceStrategy<Worker extends IPoolWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy { | ||
export declare class FairShareWorkerChoiceStrategy<Worker extends IWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy { | ||
/** @inheritDoc */ | ||
readonly requiredStatistics: RequiredStatistics; | ||
/** | ||
* Worker last virtual task execution timestamp. | ||
* Worker last virtual task execution timestamp. | ||
*/ | ||
@@ -25,9 +25,9 @@ private readonly workerLastVirtualTaskTimestamp; | ||
/** @inheritDoc */ | ||
remove(workerKey: number): boolean; | ||
remove(workerNodeKey: number): boolean; | ||
/** | ||
* Computes worker last virtual task timestamp. | ||
* | ||
* @param workerKey - The worker key. | ||
* @param workerNodeKey - The worker node key. | ||
*/ | ||
private computeWorkerLastVirtualTaskTimestamp; | ||
} |
@@ -1,2 +0,2 @@ | ||
import type { IPoolWorker } from '../pool-worker'; | ||
import type { IWorker } from '../worker'; | ||
import { AbstractWorkerChoiceStrategy } from './abstract-worker-choice-strategy'; | ||
@@ -11,3 +11,3 @@ import type { IWorkerChoiceStrategy, RequiredStatistics } from './selection-strategies-types'; | ||
*/ | ||
export declare class LessBusyWorkerChoiceStrategy<Worker extends IPoolWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy { | ||
export declare class LessBusyWorkerChoiceStrategy<Worker extends IWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy { | ||
/** @inheritDoc */ | ||
@@ -20,3 +20,3 @@ readonly requiredStatistics: RequiredStatistics; | ||
/** @inheritDoc */ | ||
remove(workerKey: number): boolean; | ||
remove(workerNodeKey: number): boolean; | ||
} |
@@ -1,2 +0,2 @@ | ||
import type { IPoolWorker } from '../pool-worker'; | ||
import type { IWorker } from '../worker'; | ||
import { AbstractWorkerChoiceStrategy } from './abstract-worker-choice-strategy'; | ||
@@ -11,3 +11,3 @@ import type { IWorkerChoiceStrategy } from './selection-strategies-types'; | ||
*/ | ||
export declare class LessUsedWorkerChoiceStrategy<Worker extends IPoolWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy { | ||
export declare class LessUsedWorkerChoiceStrategy<Worker extends IWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy { | ||
/** @inheritDoc */ | ||
@@ -18,3 +18,3 @@ reset(): boolean; | ||
/** @inheritDoc */ | ||
remove(workerKey: number): boolean; | ||
remove(workerNodeKey: number): boolean; | ||
} |
@@ -1,2 +0,2 @@ | ||
import type { IPoolWorker } from '../pool-worker'; | ||
import type { IWorker } from '../worker'; | ||
import { AbstractWorkerChoiceStrategy } from './abstract-worker-choice-strategy'; | ||
@@ -11,7 +11,7 @@ import type { IWorkerChoiceStrategy } from './selection-strategies-types'; | ||
*/ | ||
export declare class RoundRobinWorkerChoiceStrategy<Worker extends IPoolWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy { | ||
export declare class RoundRobinWorkerChoiceStrategy<Worker extends IWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy { | ||
/** | ||
* Id of the next worker. | ||
* Id of the next worker node. | ||
*/ | ||
private nextWorkerId; | ||
private nextWorkerNodeId; | ||
/** @inheritDoc */ | ||
@@ -22,3 +22,3 @@ reset(): boolean; | ||
/** @inheritDoc */ | ||
remove(workerKey: number): boolean; | ||
remove(workerNodeKey: number): boolean; | ||
} |
@@ -31,2 +31,11 @@ /** | ||
/** | ||
* Worker choice strategy options. | ||
*/ | ||
export interface WorkerChoiceStrategyOptions { | ||
/** | ||
* Use tasks median run time instead of average run time. | ||
*/ | ||
medRunTime?: boolean; | ||
} | ||
/** | ||
* Pool worker tasks usage statistics requirements. | ||
@@ -37,2 +46,3 @@ */ | ||
avgRunTime: boolean; | ||
medRunTime: boolean; | ||
} | ||
@@ -44,3 +54,3 @@ /** | ||
/** | ||
* Required pool tasks usage statistics. | ||
* Required tasks usage statistics. | ||
*/ | ||
@@ -53,11 +63,11 @@ readonly requiredStatistics: RequiredStatistics; | ||
/** | ||
* Chooses a worker in the pool and returns its key. | ||
* Chooses a worker node in the pool and returns its key. | ||
*/ | ||
choose: () => number; | ||
/** | ||
* Removes a worker reference from strategy internals. | ||
* Removes a worker node key from strategy internals. | ||
* | ||
* @param workerKey - The worker key. | ||
* @param workerNodeKey - The worker node key. | ||
*/ | ||
remove: (workerKey: number) => boolean; | ||
remove: (workerNodeKey: number) => boolean; | ||
} |
import type { IPoolInternal } from '../pool-internal'; | ||
import type { IPoolWorker } from '../pool-worker'; | ||
import type { IWorker } from '../worker'; | ||
import { AbstractWorkerChoiceStrategy } from './abstract-worker-choice-strategy'; | ||
import type { IWorkerChoiceStrategy, RequiredStatistics } from './selection-strategies-types'; | ||
import type { IWorkerChoiceStrategy, RequiredStatistics, WorkerChoiceStrategyOptions } from './selection-strategies-types'; | ||
/** | ||
@@ -13,9 +13,9 @@ * Selects the next worker with a weighted round robin scheduling algorithm. | ||
*/ | ||
export declare class WeightedRoundRobinWorkerChoiceStrategy<Worker extends IPoolWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy { | ||
export declare class WeightedRoundRobinWorkerChoiceStrategy<Worker extends IWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy { | ||
/** @inheritDoc */ | ||
readonly requiredStatistics: RequiredStatistics; | ||
/** | ||
* Worker id where the current task will be submitted. | ||
* Worker node id where the current task will be submitted. | ||
*/ | ||
private currentWorkerId; | ||
private currentWorkerNodeId; | ||
/** | ||
@@ -26,3 +26,3 @@ * Default worker weight. | ||
/** | ||
* Per worker virtual task runtime map. | ||
* Workers' virtual task runtime. | ||
*/ | ||
@@ -34,4 +34,5 @@ private readonly workersTaskRunTime; | ||
* @param pool - The pool instance. | ||
* @param opts - The worker choice strategy options. | ||
*/ | ||
constructor(pool: IPoolInternal<Worker, Data, Response>); | ||
constructor(pool: IPoolInternal<Worker, Data, Response>, opts?: WorkerChoiceStrategyOptions); | ||
/** @inheritDoc */ | ||
@@ -42,3 +43,3 @@ reset(): boolean; | ||
/** @inheritDoc */ | ||
remove(workerKey: number): boolean; | ||
remove(workerNodeKey: number): boolean; | ||
private initWorkersTaskRunTime; | ||
@@ -45,0 +46,0 @@ private initWorkerTaskRunTime; |
import type { IPoolInternal } from '../pool-internal'; | ||
import type { IPoolWorker } from '../pool-worker'; | ||
import type { RequiredStatistics, WorkerChoiceStrategy } from './selection-strategies-types'; | ||
import type { IWorker } from '../worker'; | ||
import type { RequiredStatistics, WorkerChoiceStrategy, WorkerChoiceStrategyOptions } from './selection-strategies-types'; | ||
/** | ||
@@ -11,3 +11,3 @@ * The worker choice strategy context. | ||
*/ | ||
export declare class WorkerChoiceStrategyContext<Worker extends IPoolWorker, Data = unknown, Response = unknown> { | ||
export declare class WorkerChoiceStrategyContext<Worker extends IWorker, Data = unknown, Response = unknown> { | ||
private workerChoiceStrategyType; | ||
@@ -19,6 +19,6 @@ private readonly workerChoiceStrategies; | ||
* @param pool - The pool instance. | ||
* @param createWorkerCallback - The worker creation callback for dynamic pool. | ||
* @param workerChoiceStrategy - The worker choice strategy. | ||
* @param workerChoiceStrategyType - The worker choice strategy. | ||
* @param opts - The worker choice strategy options. | ||
*/ | ||
constructor(pool: IPoolInternal<Worker, Data, Response>, workerChoiceStrategyType?: WorkerChoiceStrategy); | ||
constructor(pool: IPoolInternal<Worker, Data, Response>, workerChoiceStrategyType?: WorkerChoiceStrategy, opts?: WorkerChoiceStrategyOptions); | ||
/** | ||
@@ -39,12 +39,12 @@ * Gets the worker choice strategy in the context required statistics. | ||
* | ||
* @returns The key of the chosen one. | ||
* @returns The key of the worker node. | ||
*/ | ||
execute(): number; | ||
/** | ||
* Removes a worker from the worker choice strategy in the context. | ||
* Removes a worker node key from the worker choice strategy in the context. | ||
* | ||
* @param workerKey - The key of the worker to remove. | ||
* @param workerNodeKey - The key of the worker node. | ||
* @returns `true` if the removal is successful, `false` otherwise. | ||
*/ | ||
remove(workerKey: number): boolean; | ||
remove(workerNodeKey: number): boolean; | ||
} |
@@ -6,3 +6,3 @@ /// <reference types="node" /> | ||
import type { KillBehavior } from './worker/worker-options'; | ||
import type { IPoolWorker } from './pools/pool-worker'; | ||
import type { IWorker } from './pools/worker'; | ||
/** | ||
@@ -51,3 +51,3 @@ * Make all properties in T non-readonly. | ||
*/ | ||
export interface PromiseResponseWrapper<Worker extends IPoolWorker, Response = unknown> { | ||
export interface PromiseResponseWrapper<Worker extends IWorker, Response = unknown> { | ||
/** | ||
@@ -62,5 +62,5 @@ * Resolve callback to fulfill the promise. | ||
/** | ||
* The worker handling the promise. | ||
* The worker handling the execution. | ||
*/ | ||
readonly worker: Worker; | ||
} |
@@ -5,1 +5,8 @@ /** | ||
export declare const EMPTY_FUNCTION: () => void; | ||
/** | ||
* Returns the median of the given data set. | ||
* | ||
* @param dataSet - Data set. | ||
* @returns The median of the given data set. | ||
*/ | ||
export declare const median: (dataSet: number[]) => number; |
{ | ||
"name": "poolifier", | ||
"version": "2.4.4", | ||
"version": "2.4.5", | ||
"description": "A fast, easy to use Node.js Worker Thread Pool and Cluster Pool implementation", | ||
@@ -87,3 +87,3 @@ "license": "MIT", | ||
"c8": "^7.13.0", | ||
"eslint": "^8.37.0", | ||
"eslint": "^8.38.0", | ||
"eslint-config-standard": "^17.0.0", | ||
@@ -94,3 +94,3 @@ "eslint-config-standard-with-typescript": "^34.0.1", | ||
"eslint-plugin-import": "^2.27.5", | ||
"eslint-plugin-jsdoc": "^40.1.1", | ||
"eslint-plugin-jsdoc": "^40.1.2", | ||
"eslint-plugin-n": "^15.7.0", | ||
@@ -97,0 +97,0 @@ "eslint-plugin-promise": "^6.1.1", |
@@ -177,3 +177,10 @@ <div align="center"> | ||
- `workerChoiceStrategyOptions` (optional) - The worker choice strategy options object to use in this pool. | ||
Properties: | ||
- `medRunTime` (optional) - Use the tasks median run time instead of the tasks average run time in worker choice strategies. | ||
Default: { medRunTime: false } | ||
- `enableEvents` (optional) - Events emission enablement in this pool. Default: true | ||
- `enableTasksQueue` (optional, experimental) - Tasks queue per worker enablement in this pool. Default: false | ||
@@ -180,0 +187,0 @@ ### `pool = new DynamicThreadPool/DynamicClusterPool(min, max, filePath, opts)` |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
117135
30
1385
280