poolifier
Advanced tools
Comparing version 2.4.2 to 2.4.3
@@ -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({ROUND_ROBIN:"ROUND_ROBIN",LESS_USED:"LESS_USED",LESS_BUSY:"LESS_BUSY",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN"});class c{pool;isDynamicPool;requiredStatistics={runTime:!1,avgRunTime:!1};constructor(r){this.pool=r,this.isDynamicPool=this.pool.type===e.DYNAMIC,this.choose.bind(this)}}class l extends c{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 p extends c{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 m extends c{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 d extends c{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 c{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 g{createWorkerCallback;workerChoiceStrategyType;workerChoiceStrategies;constructor(e,r,t=u.ROUND_ROBIN){this.createWorkerCallback=r,this.workerChoiceStrategyType=t,this.execute.bind(this),this.workerChoiceStrategies=new Map([[u.ROUND_ROBIN,new d(e)],[u.LESS_USED,new m(e)],[u.LESS_BUSY,new p(e)],[u.FAIR_SHARE,new l(e)],[u.WEIGHTED_ROUND_ROBIN,new w(e)]])}getRequiredStatistics(){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).requiredStatistics}setWorkerChoiceStrategy(e){this.workerChoiceStrategyType===e?this.workerChoiceStrategies.get(e)?.reset():this.workerChoiceStrategyType=e}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategyType);return e.isDynamicPool&&!e.pool.full&&-1===e.pool.findFreeWorkerKey()?this.createWorkerCallback():e.choose()}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).remove(e)}}class T{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 g(this,(()=>{const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(r=>{var t;t=h.HARD,(r.kill===t||0===this.getWorkerTasksUsage(e)?.running)&&this.destroyWorker(e)})),this.getWorkerKey(e)}),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){if(this.opts.workerChoiceStrategy=e.workerChoiceStrategy??u.ROUND_ROBIN,!Object.values(u).includes(this.opts.workerChoiceStrategy))throw new Error(`Invalid worker choice strategy '${this.opts.workerChoiceStrategy}'`);this.opts.enableEvents=e.enableEvents??!0}get numberOfRunningTasks(){return this.promiseResponseMap.size}getWorkerKey(e){return this.workers.findIndex((r=>r.worker===e))}setWorkerChoiceStrategy(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.taskRunTime??0,this.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime&&0!==t.run&&(t.avgRunTime=t.runTime/t.run))}chooseWorker(){const e=this.workerChoiceStrategyContext.execute();return[e,this.workers[e].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(void 0!==e.id){const r=this.promiseResponseMap.get(e.id);void 0!==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("busy")}checkAndEmitFull(){this.type===e.DYNAMIC&&!0===this.opts.enableEvents&&this.full&&this.emitter?.emit("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 W extends T{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 f extends T{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 y=6e4,R=h.SOFT;class S extends n.AsyncResource{isMain;mainWorker;lastTaskTimestamp;aliveInterval;opts;constructor(e,r,t,s,i={killBehavior:R,maxInactiveTime:y}){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??y)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",(e=>{this.messageListener(e,t)}))}messageListener(e,r){void 0!==e.data&&void 0!==e.id?!0===this.opts.async?this.runInAsyncScope(this.runAsync.bind(this),this,r,e):this.runInAsyncScope(this.run.bind(this),this,r,e):void 0!==e.parent?this.mainWorker=e.parent:void 0!==e.kill&&(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??R,this.opts.maxInactiveTime=e.maxInactiveTime??y,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??y)&&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,taskRunTime: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,taskRunTime: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 S{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 W{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 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.FixedClusterPool=W,exports.FixedThreadPool=f,exports.KillBehaviors=h,exports.ThreadWorker=class extends S{constructor(e,r={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,r)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=u; | ||
"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({ROUND_ROBIN:"ROUND_ROBIN",LESS_USED:"LESS_USED",LESS_BUSY:"LESS_BUSY",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN"});class c{pool;isDynamicPool;requiredStatistics={runTime:!1,avgRunTime:!1};constructor(r){this.pool=r,this.isDynamicPool=this.pool.type===e.DYNAMIC,this.choose.bind(this)}}class l extends c{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 p extends c{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 m extends c{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 d extends c{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 c{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 g{createWorkerCallback;workerChoiceStrategyType;workerChoiceStrategies;constructor(e,r,t=u.ROUND_ROBIN){this.createWorkerCallback=r,this.workerChoiceStrategyType=t,this.execute.bind(this),this.workerChoiceStrategies=new Map([[u.ROUND_ROBIN,new d(e)],[u.LESS_USED,new m(e)],[u.LESS_BUSY,new p(e)],[u.FAIR_SHARE,new l(e)],[u.WEIGHTED_ROUND_ROBIN,new w(e)]])}getRequiredStatistics(){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).requiredStatistics}setWorkerChoiceStrategy(e){this.workerChoiceStrategyType===e?this.workerChoiceStrategies.get(e)?.reset():this.workerChoiceStrategyType=e}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategyType);return e.isDynamicPool&&!e.pool.full&&-1===e.pool.findFreeWorkerKey()?this.createWorkerCallback():e.choose()}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).remove(e)}}class T{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 g(this,(()=>{const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(r=>{var t;t=h.HARD,(r.kill===t||0===this.getWorkerTasksUsage(e)?.running)&&this.destroyWorker(e)})),this.getWorkerKey(e)}),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){if(this.opts.workerChoiceStrategy=e.workerChoiceStrategy??u.ROUND_ROBIN,!Object.values(u).includes(this.opts.workerChoiceStrategy))throw new Error(`Invalid worker choice strategy '${this.opts.workerChoiceStrategy}'`);this.opts.enableEvents=e.enableEvents??!0}get numberOfRunningTasks(){return this.promiseResponseMap.size}getWorkerKey(e){return this.workers.findIndex((r=>r.worker===e))}setWorkerChoiceStrategy(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.taskRunTime??0,this.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime&&0!==t.run&&(t.avgRunTime=t.runTime/t.run))}chooseWorker(){const e=this.workerChoiceStrategyContext.execute();return[e,this.workers[e].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("busy")}checkAndEmitFull(){this.type===e.DYNAMIC&&!0===this.opts.enableEvents&&this.full&&this.emitter?.emit("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 W extends T{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 f extends T{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 y=6e4,R=h.SOFT;class S extends n.AsyncResource{isMain;mainWorker;opts;lastTaskTimestamp;aliveInterval;constructor(e,r,t,s,i={killBehavior:R,maxInactiveTime:y}){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??y)/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??y,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??y)&&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,taskRunTime: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,taskRunTime: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 S{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 W{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 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.FixedClusterPool=W,exports.FixedThreadPool=f,exports.KillBehaviors=h,exports.ThreadWorker=class extends S{constructor(e,r={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,r)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=u; |
@@ -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({ROUND_ROBIN:"ROUND_ROBIN",LESS_USED:"LESS_USED",LESS_BUSY:"LESS_BUSY",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN"});class c{pool;isDynamicPool;requiredStatistics={runTime:!1,avgRunTime:!1};constructor(r){this.pool=r,this.isDynamicPool=this.pool.type===e.DYNAMIC,this.choose.bind(this)}}class l extends c{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 p extends c{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 m extends c{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 d extends c{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 c{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 g{createWorkerCallback;workerChoiceStrategyType;workerChoiceStrategies;constructor(e,r,t=u.ROUND_ROBIN){this.createWorkerCallback=r,this.workerChoiceStrategyType=t,this.execute.bind(this),this.workerChoiceStrategies=new Map([[u.ROUND_ROBIN,new d(e)],[u.LESS_USED,new m(e)],[u.LESS_BUSY,new p(e)],[u.FAIR_SHARE,new l(e)],[u.WEIGHTED_ROUND_ROBIN,new w(e)]])}getRequiredStatistics(){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).requiredStatistics}setWorkerChoiceStrategy(e){this.workerChoiceStrategyType===e?this.workerChoiceStrategies.get(e)?.reset():this.workerChoiceStrategyType=e}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategyType);return e.isDynamicPool&&!e.pool.full&&-1===e.pool.findFreeWorkerKey()?this.createWorkerCallback():e.choose()}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).remove(e)}}class T{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 g(this,(()=>{const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(r=>{var t;t=h.HARD,(r.kill===t||0===this.getWorkerTasksUsage(e)?.running)&&this.destroyWorker(e)})),this.getWorkerKey(e)}),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){if(this.opts.workerChoiceStrategy=e.workerChoiceStrategy??u.ROUND_ROBIN,!Object.values(u).includes(this.opts.workerChoiceStrategy))throw new Error(`Invalid worker choice strategy '${this.opts.workerChoiceStrategy}'`);this.opts.enableEvents=e.enableEvents??!0}get numberOfRunningTasks(){return this.promiseResponseMap.size}getWorkerKey(e){return this.workers.findIndex((r=>r.worker===e))}setWorkerChoiceStrategy(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.taskRunTime??0,this.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime&&0!==t.run&&(t.avgRunTime=t.runTime/t.run))}chooseWorker(){const e=this.workerChoiceStrategyContext.execute();return[e,this.workers[e].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(void 0!==e.id){const r=this.promiseResponseMap.get(e.id);void 0!==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("busy")}checkAndEmitFull(){this.type===e.DYNAMIC&&!0===this.opts.enableEvents&&this.full&&this.emitter?.emit("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 W extends T{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 f extends T{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 y=6e4,R=h.SOFT;class S extends n.AsyncResource{isMain;mainWorker;lastTaskTimestamp;aliveInterval;opts;constructor(e,r,t,s,i={killBehavior:R,maxInactiveTime:y}){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??y)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",(e=>{this.messageListener(e,t)}))}messageListener(e,r){void 0!==e.data&&void 0!==e.id?!0===this.opts.async?this.runInAsyncScope(this.runAsync.bind(this),this,r,e):this.runInAsyncScope(this.run.bind(this),this,r,e):void 0!==e.parent?this.mainWorker=e.parent:void 0!==e.kill&&(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??R,this.opts.maxInactiveTime=e.maxInactiveTime??y,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??y)&&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,taskRunTime: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,taskRunTime: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 S{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 W{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 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.FixedClusterPool=W,exports.FixedThreadPool=f,exports.KillBehaviors=h,exports.ThreadWorker=class extends S{constructor(e,r={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,r)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=u; | ||
"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({ROUND_ROBIN:"ROUND_ROBIN",LESS_USED:"LESS_USED",LESS_BUSY:"LESS_BUSY",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN"});class c{pool;isDynamicPool;requiredStatistics={runTime:!1,avgRunTime:!1};constructor(r){this.pool=r,this.isDynamicPool=this.pool.type===e.DYNAMIC,this.choose.bind(this)}}class l extends c{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 p extends c{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 m extends c{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 d extends c{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 c{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 g{createWorkerCallback;workerChoiceStrategyType;workerChoiceStrategies;constructor(e,r,t=u.ROUND_ROBIN){this.createWorkerCallback=r,this.workerChoiceStrategyType=t,this.execute.bind(this),this.workerChoiceStrategies=new Map([[u.ROUND_ROBIN,new d(e)],[u.LESS_USED,new m(e)],[u.LESS_BUSY,new p(e)],[u.FAIR_SHARE,new l(e)],[u.WEIGHTED_ROUND_ROBIN,new w(e)]])}getRequiredStatistics(){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).requiredStatistics}setWorkerChoiceStrategy(e){this.workerChoiceStrategyType===e?this.workerChoiceStrategies.get(e)?.reset():this.workerChoiceStrategyType=e}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategyType);return e.isDynamicPool&&!e.pool.full&&-1===e.pool.findFreeWorkerKey()?this.createWorkerCallback():e.choose()}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategyType).remove(e)}}class T{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 g(this,(()=>{const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(r=>{var t;t=h.HARD,(r.kill===t||0===this.getWorkerTasksUsage(e)?.running)&&this.destroyWorker(e)})),this.getWorkerKey(e)}),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){if(this.opts.workerChoiceStrategy=e.workerChoiceStrategy??u.ROUND_ROBIN,!Object.values(u).includes(this.opts.workerChoiceStrategy))throw new Error(`Invalid worker choice strategy '${this.opts.workerChoiceStrategy}'`);this.opts.enableEvents=e.enableEvents??!0}get numberOfRunningTasks(){return this.promiseResponseMap.size}getWorkerKey(e){return this.workers.findIndex((r=>r.worker===e))}setWorkerChoiceStrategy(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.taskRunTime??0,this.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime&&0!==t.run&&(t.avgRunTime=t.runTime/t.run))}chooseWorker(){const e=this.workerChoiceStrategyContext.execute();return[e,this.workers[e].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("busy")}checkAndEmitFull(){this.type===e.DYNAMIC&&!0===this.opts.enableEvents&&this.full&&this.emitter?.emit("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 W extends T{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 f extends T{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 y=6e4,R=h.SOFT;class S extends n.AsyncResource{isMain;mainWorker;opts;lastTaskTimestamp;aliveInterval;constructor(e,r,t,s,i={killBehavior:R,maxInactiveTime:y}){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??y)/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??y,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??y)&&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,taskRunTime: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,taskRunTime: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 S{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 W{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 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.FixedClusterPool=W,exports.FixedThreadPool=f,exports.KillBehaviors=h,exports.ThreadWorker=class extends S{constructor(e,r={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,r)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=u; |
@@ -20,5 +20,5 @@ import type { MessageValue, PromiseResponseWrapper } from '../utility-types'; | ||
readonly opts: PoolOptions<Worker>; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
readonly workers: Array<WorkerType<Worker>>; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
readonly emitter?: PoolEmitter; | ||
@@ -51,3 +51,3 @@ /** | ||
private checkPoolOptions; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
abstract get type(): PoolType; | ||
@@ -65,17 +65,17 @@ /** | ||
private getWorkerKey; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
setWorkerChoiceStrategy(workerChoiceStrategy: WorkerChoiceStrategy): void; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
abstract get full(): boolean; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
abstract get busy(): boolean; | ||
protected internalBusy(): boolean; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
findFreeWorkerKey(): number; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
execute(data: Data): Promise<Response>; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
destroy(): Promise<void>; | ||
/** | ||
* Shutdowns given worker. | ||
* Shutdowns given worker in the pool. | ||
* | ||
@@ -88,2 +88,5 @@ * @param worker - A worker within `workers`. | ||
* to run code before workers are created in the abstract constructor. | ||
* Can be overridden | ||
* | ||
* @virtual | ||
*/ | ||
@@ -142,2 +145,3 @@ protected setupHook(): void; | ||
* @param worker - The newly created worker. | ||
* @virtual | ||
*/ | ||
@@ -161,3 +165,3 @@ protected abstract afterWorkerSetup(worker: Worker): void; | ||
/** | ||
* Gets worker tasks usage. | ||
* Gets the given worker tasks usage in the pool. | ||
* | ||
@@ -164,0 +168,0 @@ * @param worker - The worker. |
@@ -26,8 +26,8 @@ import { PoolType } from '../pool-internal'; | ||
constructor(min: number, max: number, filePath: string, opts?: ClusterPoolOptions); | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
get type(): PoolType; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
get full(): boolean; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
get busy(): boolean; | ||
} |
@@ -46,22 +46,22 @@ /// <reference types="node" /> | ||
constructor(numberOfWorkers: number, filePath: string, opts?: ClusterPoolOptions); | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
protected setupHook(): void; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
protected isMain(): boolean; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
destroyWorker(worker: Worker): void; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
protected sendToWorker(worker: Worker, message: MessageValue<Data>): void; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
registerWorkerMessageListener<Message extends Data | Response>(worker: Worker, listener: (message: MessageValue<Message>) => void): void; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
protected createWorker(): Worker; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
protected afterWorkerSetup(worker: Worker): void; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
get type(): PoolType; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
get full(): boolean; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
get busy(): boolean; | ||
} |
@@ -5,2 +5,4 @@ import type { IPool } from './pool'; | ||
* Internal pool types. | ||
* | ||
* @enum | ||
*/ | ||
@@ -7,0 +9,0 @@ export declare enum PoolType { |
@@ -13,5 +13,5 @@ import type { IPoolInternal } from '../pool-internal'; | ||
readonly pool: IPoolInternal<Worker, Data, Response>; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
readonly isDynamicPool: boolean; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
requiredStatistics: RequiredStatistics; | ||
@@ -24,8 +24,8 @@ /** | ||
constructor(pool: IPoolInternal<Worker, Data, Response>); | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
abstract reset(): boolean; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
abstract choose(): number; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
abstract remove(workerKey: number): boolean; | ||
} |
@@ -13,3 +13,3 @@ import type { IPoolWorker } from '../pool-worker'; | ||
export declare class FairShareWorkerChoiceStrategy<Worker extends IPoolWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy<Worker, Data, Response> { | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
readonly requiredStatistics: RequiredStatistics; | ||
@@ -20,7 +20,7 @@ /** | ||
private readonly workerLastVirtualTaskTimestamp; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
reset(): boolean; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
choose(): number; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
remove(workerKey: number): boolean; | ||
@@ -27,0 +27,0 @@ /** |
@@ -12,10 +12,10 @@ import type { IPoolWorker } from '../pool-worker'; | ||
export declare class LessBusyWorkerChoiceStrategy<Worker extends IPoolWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy<Worker, Data, Response> { | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
readonly requiredStatistics: RequiredStatistics; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
reset(): boolean; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
choose(): number; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
remove(workerKey: number): boolean; | ||
} |
@@ -12,8 +12,8 @@ import type { IPoolWorker } from '../pool-worker'; | ||
export declare class LessUsedWorkerChoiceStrategy<Worker extends IPoolWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy<Worker, Data, Response> { | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
reset(): boolean; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
choose(): number; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
remove(workerKey: number): boolean; | ||
} |
@@ -16,8 +16,8 @@ import type { IPoolWorker } from '../pool-worker'; | ||
private nextWorkerId; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
reset(): boolean; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
choose(): number; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
remove(workerKey: number): boolean; | ||
} |
@@ -14,3 +14,3 @@ import type { IPoolInternal } from '../pool-internal'; | ||
export declare class WeightedRoundRobinWorkerChoiceStrategy<Worker extends IPoolWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy<Worker, Data, Response> { | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
readonly requiredStatistics: RequiredStatistics; | ||
@@ -35,7 +35,7 @@ /** | ||
constructor(pool: IPoolInternal<Worker, Data, Response>); | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
reset(): boolean; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
choose(): number; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
remove(workerKey: number): boolean; | ||
@@ -42,0 +42,0 @@ private initWorkersTaskRunTime; |
@@ -27,8 +27,8 @@ import type { PoolOptions } from '../pool'; | ||
constructor(min: number, max: number, filePath: string, opts?: PoolOptions<ThreadWorkerWithMessageChannel>); | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
get type(): PoolType; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
get full(): boolean; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
get busy(): boolean; | ||
} |
@@ -32,20 +32,20 @@ /// <reference types="node" /> | ||
constructor(numberOfThreads: number, filePath: string, opts?: PoolOptions<ThreadWorkerWithMessageChannel>); | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
protected isMain(): boolean; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
destroyWorker(worker: ThreadWorkerWithMessageChannel): Promise<void>; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
protected sendToWorker(worker: ThreadWorkerWithMessageChannel, message: MessageValue<Data>): void; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
registerWorkerMessageListener<Message extends Data | Response>(messageChannel: ThreadWorkerWithMessageChannel, listener: (message: MessageValue<Message>) => void): void; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
protected createWorker(): ThreadWorkerWithMessageChannel; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
protected afterWorkerSetup(worker: ThreadWorkerWithMessageChannel): void; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
get type(): PoolType; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
get full(): boolean; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
get busy(): boolean; | ||
} |
@@ -20,2 +20,3 @@ /// <reference types="node" /> | ||
protected mainWorker: MainWorker | undefined | null; | ||
protected readonly opts: WorkerOptions; | ||
/** | ||
@@ -30,6 +31,2 @@ * Timestamp of the last task processed by this worker. | ||
/** | ||
* Options for the worker. | ||
*/ | ||
readonly opts: WorkerOptions; | ||
/** | ||
* Constructs a new poolifier worker. | ||
@@ -36,0 +33,0 @@ * |
@@ -28,6 +28,6 @@ /// <reference types="node" /> | ||
constructor(fn: (data: Data) => Response, opts?: WorkerOptions); | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
protected sendToMainWorker(message: MessageValue<Response>): void; | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
protected handleError(e: Error | string): string; | ||
} |
@@ -28,4 +28,4 @@ /// <reference types="node" /> | ||
constructor(fn: (data: Data) => Response, opts?: WorkerOptions); | ||
/** {@inheritDoc} */ | ||
/** @inheritDoc */ | ||
protected sendToMainWorker(message: MessageValue<Response>): void; | ||
} |
{ | ||
"name": "poolifier", | ||
"version": "2.4.2", | ||
"version": "2.4.3", | ||
"description": "A fast, easy to use Node.js Worker Thread Pool and Cluster Pool implementation", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
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
1274
104681