poolifier
Advanced tools
Comparing version 2.3.10 to 2.4.0-2
@@ -1,1 +0,1 @@ | ||
"use strict";var e,r=require("cluster"),t=require("events"),s=require("os"),o=require("worker_threads"),i=require("async_hooks");!function(e){e.FIXED="fixed",e.DYNAMIC="dynamic"}(e||(e={}));const n=Object.freeze((()=>{})),a=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class h extends t{}const k=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LESS_RECENTLY_USED:"LESS_RECENTLY_USED",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN"});class c{pool;isDynamicPool;requiredStatistics={runTime:!1};constructor(r){this.pool=r,this.isDynamicPool=this.pool.type===e.DYNAMIC}}class u extends c{requiredStatistics={runTime:!0};workerLastVirtualTaskTimestamp=new Map;reset(){return this.workerLastVirtualTaskTimestamp.clear(),!0}choose(){let e,r=1/0;for(const t of this.pool.workers){this.computeWorkerLastVirtualTaskTimestamp(t);const s=this.workerLastVirtualTaskTimestamp.get(t)?.end??0;s<r&&(r=s,e=t)}return e}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.getWorkerAverageTasksRunTime(e)??0)})}}class l extends c{reset(){return!0}choose(){let e,r=1/0;for(const t of this.pool.workers){const s=this.pool.getWorkerRunningTasks(t);if(!this.isDynamicPool&&0===s)return t;s<r&&(e=t,r=s)}return e}}class p extends c{nextWorkerIndex=0;reset(){return this.nextWorkerIndex=0,!0}choose(){const e=this.pool.workers[this.nextWorkerIndex];return this.nextWorkerIndex=this.nextWorkerIndex===this.pool.workers.length-1?0:this.nextWorkerIndex+1,e}}class g extends c{requiredStatistics={runTime:!0};currentWorkerIndex=0;defaultWorkerWeight;workersTaskRunTime=new Map;constructor(e){super(e),this.defaultWorkerWeight=this.computeWorkerWeight(),this.initWorkersTaskRunTime()}reset(){return this.currentWorkerIndex=0,this.workersTaskRunTime.clear(),this.initWorkersTaskRunTime(),!0}choose(){const e=this.pool.workers[this.currentWorkerIndex];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.currentWorkerIndex=this.currentWorkerIndex===this.pool.workers.length-1?0:this.currentWorkerIndex+1,this.setWorkerTaskRunTime(this.pool.workers[this.currentWorkerIndex],t,0)),e}initWorkersTaskRunTime(){for(const e of this.pool.workers)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.getWorkerAverageTasksRunTime(e)}computeWorkerWeight(){let e=0;for(const r of s.cpus()){const t=r.speed.toString().length-1;e+=1/(r.speed/Math.pow(10,t))*Math.pow(10,t)}return Math.round(e/s.cpus().length)}}function m(e,r=k.ROUND_ROBIN){switch(r){case k.ROUND_ROBIN:return new p(e);case k.LESS_RECENTLY_USED:return new l(e);case k.FAIR_SHARE:return new u(e);case k.WEIGHTED_ROUND_ROBIN:return new g(e);default:throw new Error(`Worker choice strategy '${r}' not found`)}}class T extends c{createDynamicallyWorkerCallback;workerChoiceStrategy;constructor(e,r,t=k.ROUND_ROBIN){super(e),this.createDynamicallyWorkerCallback=r,this.workerChoiceStrategy=m(this.pool,t),this.requiredStatistics=this.workerChoiceStrategy.requiredStatistics}reset(){return this.workerChoiceStrategy.reset()}choose(){const e=this.pool.findFreeWorker();return!1!==e?e:this.pool.busy?this.workerChoiceStrategy.choose():this.createDynamicallyWorkerCallback()}}class W{pool;createDynamicallyWorkerCallback;workerChoiceStrategy;constructor(e,r,t=k.ROUND_ROBIN){this.pool=e,this.createDynamicallyWorkerCallback=r,this.setWorkerChoiceStrategy(t)}getPoolWorkerChoiceStrategy(r=k.ROUND_ROBIN){return this.pool.type===e.DYNAMIC?new T(this.pool,this.createDynamicallyWorkerCallback,r):m(this.pool,r)}getWorkerChoiceStrategy(){return this.workerChoiceStrategy}setWorkerChoiceStrategy(e){this.workerChoiceStrategy?.reset(),this.workerChoiceStrategy=this.getPoolWorkerChoiceStrategy(e)}execute(){return this.workerChoiceStrategy.choose()}}class w{numberOfWorkers;filePath;opts;workers=[];workersTasksUsage=new Map;emitter;promiseMap=new Map;nextMessageId=0;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.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker();!0===this.opts.enableEvents&&(this.emitter=new h),this.workerChoiceStrategyContext=new W(this,(()=>{const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(r=>{var t;t=a.HARD,(r.kill===t||0===this.getWorkerRunningTasks(e))&&this.destroyWorker(e)})),e}),this.opts.workerChoiceStrategy)}checkFilePath(e){if(null==e||0===e.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??k.ROUND_ROBIN,this.opts.enableEvents=e.enableEvents??!0}get numberOfRunningTasks(){return this.promiseMap.size}getWorkerIndex(e){return this.workers.indexOf(e)}getWorkerRunningTasks(e){return this.workersTasksUsage.get(e)?.running}getWorkerAverageTasksRunTime(e){return this.workersTasksUsage.get(e)?.avgRunTime}setWorkerChoiceStrategy(e){this.opts.workerChoiceStrategy=e;for(const e of this.workers)this.resetWorkerTasksUsage(e);this.workerChoiceStrategyContext.setWorkerChoiceStrategy(e)}internalGetBusyStatus(){return this.numberOfRunningTasks>=this.numberOfWorkers&&!1===this.findFreeWorker()}findFreeWorker(){for(const e of this.workers)if(0===this.getWorkerRunningTasks(e))return e;return!1}async execute(e){const r=this.chooseWorker(),t=this.internalExecute(r,this.nextMessageId);return this.checkAndEmitBusy(),this.sendToWorker(r,{data:e??{},id:this.nextMessageId}),++this.nextMessageId,t}async destroy(){await Promise.all(this.workers.map((async e=>{await this.destroyWorker(e)})))}setupHook(){}beforePromiseWorkerResponseHook(e){this.increaseWorkerRunningTasks(e)}afterPromiseWorkerResponseHook(e,r){this.decreaseWorkerRunningTasks(r.worker),this.stepWorkerRunTasks(r.worker,1),this.updateWorkerTasksRunTime(r.worker,e.taskRunTime)}removeWorker(e){this.workers.splice(this.getWorkerIndex(e),1),this.removeWorkerTasksUsage(e)}chooseWorker(){return this.workerChoiceStrategyContext.execute()}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??n),e.on("error",this.opts.errorHandler??n),e.on("online",this.opts.onlineHandler??n),e.on("exit",this.opts.exitHandler??n),e.once("exit",(()=>{this.removeWorker(e)})),this.workers.push(e),this.initWorkerTasksUsage(e),this.afterWorkerSetup(e),e}workerListener(){return e=>{if(void 0!==e.id){const r=this.promiseMap.get(e.id);void 0!==r&&(null!=e.error?r.reject(e.error):r.resolve(e.data),this.afterPromiseWorkerResponseHook(e,r),this.promiseMap.delete(e.id))}}}async internalExecute(e,r){return this.beforePromiseWorkerResponseHook(e),await new Promise(((t,s)=>{this.promiseMap.set(r,{resolve:t,reject:s,worker:e})}))}checkAndEmitBusy(){!0===this.opts.enableEvents&&this.busy&&this.emitter?.emit("busy")}increaseWorkerRunningTasks(e){this.stepWorkerRunningTasks(e,1)}decreaseWorkerRunningTasks(e){this.stepWorkerRunningTasks(e,-1)}stepWorkerRunningTasks(e,r){if(this.checkWorkerTasksUsage(e)){const t=this.workersTasksUsage.get(e);t.running=t.running+r,this.workersTasksUsage.set(e,t)}}stepWorkerRunTasks(e,r){if(this.checkWorkerTasksUsage(e)){const t=this.workersTasksUsage.get(e);t.run=t.run+r,this.workersTasksUsage.set(e,t)}}updateWorkerTasksRunTime(e,r){if(this.workerChoiceStrategyContext.getWorkerChoiceStrategy().requiredStatistics.runTime&&this.checkWorkerTasksUsage(e)){const t=this.workersTasksUsage.get(e);t.runTime+=r??0,0!==t.run&&(t.avgRunTime=t.runTime/t.run),this.workersTasksUsage.set(e,t)}}checkWorkerTasksUsage(e){const r=this.workersTasksUsage.has(e);if(!r)throw new Error("Worker could not be found in workers tasks usage map");return r}initWorkerTasksUsage(e){this.workersTasksUsage.set(e,{run:0,running:0,runTime:0,avgRunTime:0})}removeWorkerTasksUsage(e){this.workersTasksUsage.delete(e)}resetWorkerTasksUsage(e){this.removeWorkerTasksUsage(e),this.initWorkerTasksUsage(e)}}class d 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 busy(){return this.internalGetBusyStatus()}}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 busy(){return this.internalGetBusyStatus()}}const f=6e4,R=a.SOFT;class x extends i.AsyncResource{mainWorker;lastTaskTimestamp;aliveInterval;opts;constructor(e,r,t,s,o={killBehavior:R,maxInactiveTime:f}){super(e),this.mainWorker=s,this.opts=o,this.checkFunctionInput(t),this.checkWorkerOptions(this.opts),this.lastTaskTimestamp=Date.now(),r||(this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??f)/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??f,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??f)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}run(e,r){try{const t=Date.now(),s=e(r.data),o=Date.now()-t;this.sendToMainWorker({data:s,id:r.id,taskRunTime:o})}catch(e){const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})}finally{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.lastTaskTimestamp=Date.now()})).catch(n)}}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 d{max;constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get busy(){return this.workers.length===this.max}},exports.DynamicThreadPool=class extends y{max;constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get busy(){return this.workers.length===this.max}},exports.FixedClusterPool=d,exports.FixedThreadPool=y,exports.KillBehaviors=a,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=k; | ||
"use strict";var e,r=require("node:cluster"),t=require("node:crypto"),s=require("node:events"),o=require("node:os"),i=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"});function k(e,r){return r===e}class u extends s{}const 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};constructor(r){this.pool=r,this.isDynamicPool=this.pool.type===e.DYNAMIC}}class p extends l{requiredStatistics={runTime:!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};reset(){return!0}choose(){let e,r=1/0;for(const[t,s]of this.pool.workers.entries()){const o=s.tasksUsage.runTime;if(!this.isDynamicPool&&0===o)return t;o<r&&(r=o,e=t)}return e}remove(e){return!0}}class d extends l{reset(){return!0}choose(){let e,r=1/0;for(const[t,s]of this.pool.workers.entries()){const o=s.tasksUsage,i=o?.run+o?.running;if(!this.isDynamicPool&&0===i)return t;i<r&&(r=i,e=t)}return e}remove(e){return!0}}class w 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&&(this.nextWorkerId=this.nextWorkerId>this.pool.workers.length-1?this.pool.workers.length-1:this.nextWorkerId),!0}}class g extends l{requiredStatistics={runTime:!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&&(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 o.cpus()){const t=r.speed.toString().length-1;e+=1/(r.speed/Math.pow(10,t))*Math.pow(10,t)}return Math.round(e/o.cpus().length)}}function W(e,r=c.ROUND_ROBIN){switch(r){case c.ROUND_ROBIN:return new w(e);case c.LESS_USED:return new d(e);case c.LESS_BUSY:return new m(e);case c.FAIR_SHARE:return new p(e);case c.WEIGHTED_ROUND_ROBIN:return new g(e);default:throw new Error(`Worker choice strategy '${r}' not found`)}}class T extends l{createWorkerCallback;workerChoiceStrategy;constructor(e,r,t=c.ROUND_ROBIN){super(e),this.createWorkerCallback=r,this.workerChoiceStrategy=W(this.pool,t),this.requiredStatistics=this.workerChoiceStrategy.requiredStatistics}reset(){return this.workerChoiceStrategy.reset()}choose(){const e=this.pool.findFreeWorkerKey();return!1!==e?e:this.pool.busy?this.workerChoiceStrategy.choose():this.createWorkerCallback()}remove(e){return this.workerChoiceStrategy.remove(e)}}class y{pool;createWorkerCallback;workerChoiceStrategy;constructor(e,r,t=c.ROUND_ROBIN){this.pool=e,this.createWorkerCallback=r,this.setWorkerChoiceStrategy(t)}getPoolWorkerChoiceStrategy(r=c.ROUND_ROBIN){return this.pool.type===e.DYNAMIC?new T(this.pool,this.createWorkerCallback,r):W(this.pool,r)}getWorkerChoiceStrategy(){return this.workerChoiceStrategy}getRequiredStatistics(){return this.workerChoiceStrategy.requiredStatistics}setWorkerChoiceStrategy(e){this.workerChoiceStrategy?.reset(),this.workerChoiceStrategy=this.getPoolWorkerChoiceStrategy(e)}execute(){return this.workerChoiceStrategy.choose()}remove(e){return this.workerChoiceStrategy.remove(e)}}class f{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.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker();!0===this.opts.enableEvents&&(this.emitter=new u),this.workerChoiceStrategyContext=new y(this,(()=>{const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(r=>{(k(h.HARD,r.kill)||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){this.opts.workerChoiceStrategy=e.workerChoiceStrategy??c.ROUND_ROBIN,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)}internalGetBusyStatus(){return this.numberOfRunningTasks>=this.numberOfWorkers&&!1===this.findFreeWorkerKey()}findFreeWorkerKey(){const e=this.workers.findIndex((e=>0===e.tasksUsage.running));return-1!==e&&e}async execute(e){const[r,s]=this.chooseWorker(),o=t.randomUUID(),i=this.internalExecute(r,s,o);return this.checkAndEmitBusy(),this.sendToWorker(s,{data:e??{},id:o}),i}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,0!==t.run&&(t.avgRunTime=t.runTime/t.run))}removeWorker(e){const r=this.getWorkerKey(e);this.workers.splice(r,1),this.workerChoiceStrategyContext.remove(r)}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")}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}}}class R 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 busy(){return this.internalGetBusyStatus()}}class S extends f{constructor(e,r,t={}){super(e,r,t)}isMain(){return i.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 i.Worker(this.filePath,{env:i.SHARE_ENV})}afterWorkerSetup(e){const{port1:r,port2:t}=new i.MessageChannel;e.postMessage({parent:r},[r]),e.port1=r,e.port2=t,this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get busy(){return this.internalGetBusyStatus()}}const I=6e4,x=h.SOFT;class v extends n.AsyncResource{mainWorker;lastTaskTimestamp;aliveInterval;opts;constructor(e,r,t,s,o={killBehavior:x,maxInactiveTime:I}){super(e),this.mainWorker=s,this.opts=o,this.checkFunctionInput(t),this.checkWorkerOptions(this.opts),!r&&k(h.HARD,this.opts.killBehavior)&&(this.lastTaskTimestamp=Date.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??I)/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??x,this.opts.maxInactiveTime=e.maxInactiveTime??I,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??I)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}run(e,r){try{const t=Date.now(),s=e(r.data),o=Date.now()-t;this.sendToMainWorker({data:s,id:r.id,taskRunTime:o})}catch(e){const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})}finally{k(h.HARD,this.opts.killBehavior)&&(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((()=>{k(h.HARD,this.opts.killBehavior)&&(this.lastTaskTimestamp=Date.now())})).catch(a)}}exports.ClusterWorker=class extends v{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 R{max;constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get busy(){return this.workers.length===this.max}},exports.DynamicThreadPool=class extends S{max;constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get busy(){return this.workers.length===this.max}},exports.FixedClusterPool=R,exports.FixedThreadPool=S,exports.KillBehaviors=h,exports.ThreadWorker=class extends v{constructor(e,r={}){super("worker-thread-pool:poolifier",i.isMainThread,e,i.parentPort,r)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=c; |
@@ -1,5 +0,5 @@ | ||
import type { MessageValue, PromiseWorkerResponseWrapper } from '../utility-types'; | ||
import type { MessageValue, PromiseResponseWrapper } from '../utility-types'; | ||
import type { PoolOptions } from './pool'; | ||
import { PoolEmitter } from './pool'; | ||
import type { IPoolInternal, TasksUsage } from './pool-internal'; | ||
import type { IPoolInternal, WorkerType } from './pool-internal'; | ||
import { PoolType } from './pool-internal'; | ||
@@ -21,21 +21,15 @@ import type { IPoolWorker } from './pool-worker'; | ||
/** {@inheritDoc} */ | ||
readonly workers: Worker[]; | ||
readonly workers: Array<WorkerType<Worker>>; | ||
/** {@inheritDoc} */ | ||
readonly workersTasksUsage: Map<Worker, TasksUsage>; | ||
/** {@inheritDoc} */ | ||
readonly emitter?: PoolEmitter; | ||
/** | ||
* The promise map. | ||
* The promise response map. | ||
* | ||
* - `key`: This is the message Id of each submitted task. | ||
* - `value`: An object that contains the worker, the resolve function and the reject function. | ||
* - `key`: The message id of each submitted task. | ||
* - `value`: An object that contains the worker, the promise resolve and reject callbacks. | ||
* | ||
* When we receive a message from the worker we get a map entry and resolve/reject the promise based on the message. | ||
* When we receive a message from the worker we get a map entry with the promise resolve/reject bound to the message. | ||
*/ | ||
protected promiseMap: Map<number, PromiseWorkerResponseWrapper<Worker, Response>>; | ||
protected promiseResponseMap: Map<string, PromiseResponseWrapper<Worker, Response>>; | ||
/** | ||
* Id of the next message. | ||
*/ | ||
protected nextMessageId: number; | ||
/** | ||
* Worker choice strategy instance implementing the worker choice algorithm. | ||
@@ -61,9 +55,10 @@ * | ||
get numberOfRunningTasks(): number; | ||
/** | ||
* Gets the given worker key. | ||
* | ||
* @param worker - The worker. | ||
* @returns The worker key. | ||
*/ | ||
private getWorkerKey; | ||
/** {@inheritDoc} */ | ||
getWorkerIndex(worker: Worker): number; | ||
/** {@inheritDoc} */ | ||
getWorkerRunningTasks(worker: Worker): number | undefined; | ||
/** {@inheritDoc} */ | ||
getWorkerAverageTasksRunTime(worker: Worker): number | undefined; | ||
/** {@inheritDoc} */ | ||
setWorkerChoiceStrategy(workerChoiceStrategy: WorkerChoiceStrategy): void; | ||
@@ -74,3 +69,3 @@ /** {@inheritDoc} */ | ||
/** {@inheritDoc} */ | ||
findFreeWorker(): Worker | false; | ||
findFreeWorkerKey(): number | false; | ||
/** {@inheritDoc} */ | ||
@@ -99,5 +94,5 @@ execute(data: Data): Promise<Response>; | ||
* | ||
* @param worker - The worker. | ||
* @param workerKey - The worker key. | ||
*/ | ||
protected beforePromiseWorkerResponseHook(worker: Worker): void; | ||
protected beforePromiseResponseHook(workerKey: number): void; | ||
/** | ||
@@ -107,6 +102,6 @@ * Hook executed after the worker task promise resolution. | ||
* | ||
* @param worker - The worker. | ||
* @param message - The received message. | ||
* @param promise - The Promise response. | ||
*/ | ||
protected afterPromiseWorkerResponseHook(message: MessageValue<Response>, promise: PromiseWorkerResponseWrapper<Worker, Response>): void; | ||
protected afterPromiseResponseHook(worker: Worker, message: MessageValue<Response>): void; | ||
/** | ||
@@ -123,5 +118,5 @@ * Removes the given worker from the pool. | ||
* | ||
* @returns Worker. | ||
* @returns [worker key, worker]. | ||
*/ | ||
protected chooseWorker(): Worker; | ||
protected chooseWorker(): [number, Worker]; | ||
/** | ||
@@ -168,59 +163,23 @@ * Sends a message to the given worker. | ||
/** | ||
* Increases the number of tasks that the given worker has applied. | ||
* Gets worker tasks usage. | ||
* | ||
* @param worker - Worker which running tasks is increased. | ||
*/ | ||
private increaseWorkerRunningTasks; | ||
/** | ||
* Decreases the number of tasks that the given worker has applied. | ||
* | ||
* @param worker - Worker which running tasks is decreased. | ||
*/ | ||
private decreaseWorkerRunningTasks; | ||
/** | ||
* Steps the number of tasks that the given worker has applied. | ||
* | ||
* @param worker - Worker which running tasks are stepped. | ||
* @param step - Number of running tasks step. | ||
*/ | ||
private stepWorkerRunningTasks; | ||
/** | ||
* Steps the number of tasks that the given worker has run. | ||
* | ||
* @param worker - Worker which has run tasks. | ||
* @param step - Number of run tasks step. | ||
*/ | ||
private stepWorkerRunTasks; | ||
/** | ||
* Updates tasks runtime for the given worker. | ||
* | ||
* @param worker - Worker which run the task. | ||
* @param taskRunTime - Worker task runtime. | ||
*/ | ||
private updateWorkerTasksRunTime; | ||
/** | ||
* Checks if the given worker is registered in the workers tasks usage map. | ||
* | ||
* @param worker - Worker to check. | ||
* @returns `true` if the worker is registered in the workers tasks usage map. `false` otherwise. | ||
*/ | ||
private checkWorkerTasksUsage; | ||
/** | ||
* Initializes tasks usage statistics. | ||
* | ||
* @param worker - The worker. | ||
* @returns The worker tasks usage. | ||
*/ | ||
private initWorkerTasksUsage; | ||
private getWorkerTasksUsage; | ||
/** | ||
* Removes worker tasks usage statistics. | ||
* Pushes the given worker. | ||
* | ||
* @param worker - The worker. | ||
* @param tasksUsage - The worker tasks usage. | ||
*/ | ||
private removeWorkerTasksUsage; | ||
private pushWorker; | ||
/** | ||
* Resets worker tasks usage statistics. | ||
* Sets the given worker. | ||
* | ||
* @param workerKey - The worker key. | ||
* @param worker - The worker. | ||
* @param tasksUsage - The worker tasks usage. | ||
*/ | ||
private resetWorkerTasksUsage; | ||
private setWorker; | ||
} |
/// <reference types="node" /> | ||
import type { ClusterSettings, Worker } from 'cluster'; | ||
import type { ClusterSettings, Worker } from 'node:cluster'; | ||
import type { MessageValue } from '../../utility-types'; | ||
@@ -4,0 +4,0 @@ import { AbstractPool } from '../abstract-pool'; |
@@ -18,4 +18,14 @@ import type { IPool } from './pool'; | ||
avgRunTime: number; | ||
error: number; | ||
} | ||
/** | ||
* Internal worker type. | ||
* | ||
* @typeParam Worker - Type of worker which manages this pool. | ||
*/ | ||
export interface WorkerType<Worker extends IPoolWorker> { | ||
worker: Worker; | ||
tasksUsage: TasksUsage; | ||
} | ||
/** | ||
* Internal contract definition for a poolifier pool. | ||
@@ -29,13 +39,6 @@ * | ||
/** | ||
* List of currently available workers. | ||
* Pool workers item array. | ||
*/ | ||
readonly workers: Worker[]; | ||
readonly workers: Array<WorkerType<Worker>>; | ||
/** | ||
* The workers tasks usage map. | ||
* | ||
* `key`: The `Worker` | ||
* `value`: Worker tasks usage statistics. | ||
*/ | ||
readonly workersTasksUsage: Map<Worker, TasksUsage>; | ||
/** | ||
* Pool type. | ||
@@ -57,32 +60,11 @@ * | ||
/** | ||
* Finds a free worker based on the number of tasks the worker has applied. | ||
* Finds a free worker 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 returned. | ||
* If a worker is found with `0` running tasks, it is detected as free and its key is returned. | ||
* | ||
* If no free worker is found, `false` is returned. | ||
* | ||
* @returns A free worker if there is one, otherwise `false`. | ||
* @returns A worker key if there is one, otherwise `false`. | ||
*/ | ||
findFreeWorker: () => Worker | false; | ||
/** | ||
* Gets worker index. | ||
* | ||
* @param worker - The worker. | ||
* @returns The worker index. | ||
*/ | ||
getWorkerIndex: (worker: Worker) => number; | ||
/** | ||
* Gets worker running tasks. | ||
* | ||
* @param worker - The worker. | ||
* @returns The number of tasks currently running on the worker. | ||
*/ | ||
getWorkerRunningTasks: (worker: Worker) => number | undefined; | ||
/** | ||
* Gets worker average tasks runtime. | ||
* | ||
* @param worker - The worker. | ||
* @returns The average tasks runtime on the worker. | ||
*/ | ||
getWorkerAverageTasksRunTime: (worker: Worker) => number | undefined; | ||
findFreeWorkerKey: () => number | false; | ||
} |
/// <reference types="node" /> | ||
import EventEmitter from 'events'; | ||
import EventEmitter from 'node:events'; | ||
import type { ErrorHandler, ExitHandler, MessageHandler, OnlineHandler } from './pool-worker'; | ||
@@ -4,0 +4,0 @@ import type { WorkerChoiceStrategy } from './selection-strategies/selection-strategies-types'; |
@@ -11,3 +11,3 @@ import type { IPoolInternal } from '../pool-internal'; | ||
*/ | ||
export declare abstract class AbstractWorkerChoiceStrategy<Worker extends IPoolWorker, Data, Response> implements IWorkerChoiceStrategy<Worker> { | ||
export declare abstract class AbstractWorkerChoiceStrategy<Worker extends IPoolWorker, Data, Response> implements IWorkerChoiceStrategy { | ||
protected readonly pool: IPoolInternal<Worker, Data, Response>; | ||
@@ -27,3 +27,5 @@ /** {@inheritDoc} */ | ||
/** {@inheritDoc} */ | ||
abstract choose(): Worker; | ||
abstract choose(): number; | ||
/** {@inheritDoc} */ | ||
abstract remove(workerKey: number): boolean; | ||
} |
@@ -13,3 +13,3 @@ import type { IPoolInternal } from '../pool-internal'; | ||
export declare class DynamicPoolWorkerChoiceStrategy<Worker extends IPoolWorker, Data, Response> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> { | ||
private readonly createDynamicallyWorkerCallback; | ||
private readonly createWorkerCallback; | ||
private readonly workerChoiceStrategy; | ||
@@ -20,10 +20,12 @@ /** | ||
* @param pool - The pool instance. | ||
* @param createDynamicallyWorkerCallback - The worker creation callback for dynamic pool. | ||
* @param workerChoiceStrategy - The worker choice strategy when the pull is busy. | ||
* @param createWorkerCallback - The worker creation callback for dynamic pool. | ||
* @param workerChoiceStrategy - The worker choice strategy when the pool is busy. | ||
*/ | ||
constructor(pool: IPoolInternal<Worker, Data, Response>, createDynamicallyWorkerCallback: () => Worker, workerChoiceStrategy?: WorkerChoiceStrategy); | ||
constructor(pool: IPoolInternal<Worker, Data, Response>, createWorkerCallback: () => number, workerChoiceStrategy?: WorkerChoiceStrategy); | ||
/** {@inheritDoc} */ | ||
reset(): boolean; | ||
/** {@inheritDoc} */ | ||
choose(): Worker; | ||
choose(): number; | ||
/** {@inheritDoc} */ | ||
remove(workerKey: number): boolean; | ||
} |
@@ -22,9 +22,11 @@ import type { IPoolWorker } from '../pool-worker'; | ||
/** {@inheritDoc} */ | ||
choose(): Worker; | ||
choose(): number; | ||
/** {@inheritDoc} */ | ||
remove(workerKey: number): boolean; | ||
/** | ||
* Computes worker last virtual task timestamp. | ||
* | ||
* @param worker - The worker. | ||
* @param workerKey - The worker key. | ||
*/ | ||
private computeWorkerLastVirtualTaskTimestamp; | ||
} |
@@ -12,9 +12,11 @@ import type { IPoolWorker } from '../pool-worker'; | ||
/** | ||
* Index for the next worker. | ||
* Id of the next worker. | ||
*/ | ||
private nextWorkerIndex; | ||
private nextWorkerId; | ||
/** {@inheritDoc} */ | ||
reset(): boolean; | ||
/** {@inheritDoc} */ | ||
choose(): Worker; | ||
choose(): number; | ||
/** {@inheritDoc} */ | ||
remove(workerKey: number): boolean; | ||
} |
@@ -1,2 +0,1 @@ | ||
import type { IPoolWorker } from '../pool-worker'; | ||
/** | ||
@@ -11,6 +10,10 @@ * Enumeration of worker choice strategies. | ||
/** | ||
* Less recently used worker selection strategy. | ||
* Less used worker selection strategy. | ||
*/ | ||
readonly LESS_RECENTLY_USED: "LESS_RECENTLY_USED"; | ||
readonly LESS_USED: "LESS_USED"; | ||
/** | ||
* Less busy worker selection strategy. | ||
*/ | ||
readonly LESS_BUSY: "LESS_BUSY"; | ||
/** | ||
* Fair share worker selection strategy. | ||
@@ -36,6 +39,4 @@ */ | ||
* Worker choice strategy interface. | ||
* | ||
* @typeParam Worker - Type of worker which manages the strategy. | ||
*/ | ||
export interface IWorkerChoiceStrategy<Worker extends IPoolWorker> { | ||
export interface IWorkerChoiceStrategy { | ||
/** | ||
@@ -54,5 +55,11 @@ * Is the pool attached to the strategy dynamic?. | ||
/** | ||
* Chooses a worker in the pool. | ||
* Chooses a worker in the pool and returns its key. | ||
*/ | ||
choose: () => Worker; | ||
choose: () => number; | ||
/** | ||
* Removes a worker reference from strategy internals. | ||
* | ||
* @param workerKey - The worker key. | ||
*/ | ||
remove: (workerKey: number) => boolean; | ||
} |
@@ -11,2 +11,2 @@ import type { IPoolInternal } from '../pool-internal'; | ||
*/ | ||
export declare function getWorkerChoiceStrategy<Worker extends IPoolWorker, Data, Response>(pool: IPoolInternal<Worker, Data, Response>, workerChoiceStrategy?: WorkerChoiceStrategy): IWorkerChoiceStrategy<Worker>; | ||
export declare function getWorkerChoiceStrategy<Worker extends IPoolWorker, Data, Response>(pool: IPoolInternal<Worker, Data, Response>, workerChoiceStrategy?: WorkerChoiceStrategy): IWorkerChoiceStrategy; |
@@ -17,5 +17,5 @@ import type { IPoolInternal } from '../pool-internal'; | ||
/** | ||
* Worker index where the current task will be submitted. | ||
* Worker id where the current task will be submitted. | ||
*/ | ||
private currentWorkerIndex; | ||
private currentWorkerId; | ||
/** | ||
@@ -38,3 +38,5 @@ * Default worker weight. | ||
/** {@inheritDoc} */ | ||
choose(): Worker; | ||
choose(): number; | ||
/** {@inheritDoc} */ | ||
remove(workerKey: number): boolean; | ||
private initWorkersTaskRunTime; | ||
@@ -41,0 +43,0 @@ private initWorkerTaskRunTime; |
import type { IPoolInternal } from '../pool-internal'; | ||
import type { IPoolWorker } from '../pool-worker'; | ||
import type { IWorkerChoiceStrategy, WorkerChoiceStrategy } from './selection-strategies-types'; | ||
import type { IWorkerChoiceStrategy, RequiredStatistics, WorkerChoiceStrategy } from './selection-strategies-types'; | ||
/** | ||
@@ -13,3 +13,3 @@ * The worker choice strategy context. | ||
private readonly pool; | ||
private readonly createDynamicallyWorkerCallback; | ||
private readonly createWorkerCallback; | ||
private workerChoiceStrategy; | ||
@@ -20,6 +20,6 @@ /** | ||
* @param pool - The pool instance. | ||
* @param createDynamicallyWorkerCallback - The worker creation callback for dynamic pool. | ||
* @param createWorkerCallback - The worker creation callback for dynamic pool. | ||
* @param workerChoiceStrategy - The worker choice strategy. | ||
*/ | ||
constructor(pool: IPoolInternal<Worker, Data, Response>, createDynamicallyWorkerCallback: () => Worker, workerChoiceStrategy?: WorkerChoiceStrategy); | ||
constructor(pool: IPoolInternal<Worker, Data, Response>, createWorkerCallback: () => number, workerChoiceStrategy?: WorkerChoiceStrategy); | ||
/** | ||
@@ -36,5 +36,12 @@ * Gets the worker choice strategy instance specific to the pool type. | ||
* @returns The worker choice strategy. | ||
* @deprecated Scheduled removal. | ||
*/ | ||
getWorkerChoiceStrategy(): IWorkerChoiceStrategy<Worker>; | ||
getWorkerChoiceStrategy(): IWorkerChoiceStrategy; | ||
/** | ||
* Gets the worker choice strategy required statistics. | ||
* | ||
* @returns The required statistics. | ||
*/ | ||
getRequiredStatistics(): RequiredStatistics; | ||
/** | ||
* Sets the worker choice strategy to use in the context. | ||
@@ -48,5 +55,12 @@ * | ||
* | ||
* @returns The chosen one. | ||
* @returns The key of the chosen one. | ||
*/ | ||
execute(): Worker; | ||
execute(): number; | ||
/** | ||
* Removes a worker in the underlying selection strategy internals. | ||
* | ||
* @param workerKey - The key of the worker to remove. | ||
* @returns `true` if the removal is successful, `false` otherwise. | ||
*/ | ||
remove(workerKey: number): boolean; | ||
} |
/// <reference types="node" /> | ||
import { MessageChannel, Worker } from 'worker_threads'; | ||
import { MessageChannel, Worker } from 'node:worker_threads'; | ||
import type { Draft, MessageValue } from '../../utility-types'; | ||
@@ -4,0 +4,0 @@ import { AbstractPool } from '../abstract-pool'; |
/// <reference types="node" /> | ||
/// <reference types="node" /> | ||
import type { Worker as ClusterWorker } from 'cluster'; | ||
import type { MessagePort } from 'worker_threads'; | ||
import type { Worker as ClusterWorker } from 'node:cluster'; | ||
import type { MessagePort } from 'node:worker_threads'; | ||
import type { KillBehavior } from './worker/worker-options'; | ||
import type { IPoolWorker } from './pools/pool-worker'; | ||
import type { KillBehavior } from './worker/worker-options'; | ||
/** | ||
@@ -24,3 +24,3 @@ * Make all properties in T non-readonly. | ||
*/ | ||
readonly id?: number; | ||
readonly id?: string; | ||
/** | ||
@@ -46,8 +46,8 @@ * Kill code. | ||
/** | ||
* An object holding the worker that will be used to resolve/rejects the promise later on. | ||
* An object holding the execution response promise resolve/reject callbacks. | ||
* | ||
* @typeParam Worker - Type of worker. | ||
* @typeParam Response - Type of response of execution. This can only be serializable data. | ||
* @typeParam Response - Type of execution response. This can only be serializable data. | ||
*/ | ||
export interface PromiseWorkerResponseWrapper<Worker extends IPoolWorker, Response = unknown> { | ||
export interface PromiseResponseWrapper<Worker extends IPoolWorker, Response = unknown> { | ||
/** | ||
@@ -62,5 +62,5 @@ * Resolve callback to fulfill the promise. | ||
/** | ||
* The worker that has the assigned task. | ||
* The worker handling the promise. | ||
*/ | ||
readonly worker: Worker; | ||
} |
@@ -5,7 +5,7 @@ /// <reference types="node" /> | ||
/// <reference types="node" /> | ||
import { AsyncResource } from 'async_hooks'; | ||
import type { Worker } from 'cluster'; | ||
import type { MessagePort } from 'worker_threads'; | ||
import { AsyncResource } from 'node:async_hooks'; | ||
import type { Worker } from 'node:cluster'; | ||
import type { MessagePort } from 'node:worker_threads'; | ||
import type { MessageValue } from '../utility-types'; | ||
import type { WorkerOptions } from './worker-options'; | ||
import { type WorkerOptions } from './worker-options'; | ||
/** | ||
@@ -12,0 +12,0 @@ * Base class that implements some shared logic for all poolifier workers. |
/// <reference types="node" /> | ||
import type { Worker } from 'cluster'; | ||
import type { Worker } from 'node:cluster'; | ||
import type { MessageValue } from '../utility-types'; | ||
@@ -4,0 +4,0 @@ import { AbstractWorker } from './abstract-worker'; |
/// <reference types="node" /> | ||
import type { MessagePort } from 'worker_threads'; | ||
import type { MessagePort } from 'node:worker_threads'; | ||
import type { MessageValue } from '../utility-types'; | ||
@@ -4,0 +4,0 @@ import { AbstractWorker } from './abstract-worker'; |
@@ -10,3 +10,3 @@ /** | ||
/** | ||
* If `lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker will be deleted. | ||
* If `currentTime - lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker will be deleted. | ||
*/ | ||
@@ -55,3 +55,3 @@ readonly HARD: "HARD"; | ||
* - SOFT: If `currentTime - lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker **won't** be deleted. | ||
* - HARD: If `lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker will be deleted. | ||
* - HARD: If `currentTime - lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker will be deleted. | ||
* | ||
@@ -58,0 +58,0 @@ * This option only apply to the newly created workers. |
{ | ||
"name": "poolifier", | ||
"version": "2.3.10", | ||
"version": "2.4.0-2", | ||
"description": "A fast, easy to use Node.js Worker Thread Pool and Cluster Pool implementation", | ||
@@ -13,22 +13,2 @@ "license": "MIT", | ||
}, | ||
"scripts": { | ||
"prepare": "node prepare.js", | ||
"build": "rollup --config --environment BUILD:development", | ||
"build:typedoc": "rollup --config --environment BUILD:development --environment DOCUMENTATION", | ||
"build:prod": "rollup --config", | ||
"benchmark": "npm run build && node -r source-map-support/register benchmarks/internal/bench.js", | ||
"benchmark:debug": "npm run build && node -r source-map-support/register --inspect benchmarks/internal/bench.js", | ||
"benchmark:prod": "npm run build:prod && node -r source-map-support/register benchmarks/internal/bench.js", | ||
"test": "npm run build && c8 mocha 'tests/**/*.test.js'", | ||
"test:debug": "npm run build && mocha --no-parallel --inspect 'tests/**/*.test.js'", | ||
"coverage": "c8 report --reporter=lcov", | ||
"coverage:html": "c8 report --reporter=html", | ||
"format": "prettier . --cache --write; ts-standard . --fix", | ||
"lint": "eslint . --cache", | ||
"lint:fix": "eslint . --cache --fix", | ||
"lint:report": "eslint . --cache --format json --output-file reports/eslint.json", | ||
"release": "release-it", | ||
"typedoc": "typedoc", | ||
"prepublishOnly": "npm run build:prod" | ||
}, | ||
"ts-standard": { | ||
@@ -45,4 +25,3 @@ "globals": [ | ||
"engines": { | ||
"node": ">=16.0.0", | ||
"npm": ">=8.0.0" | ||
"node": ">=16.0.0" | ||
}, | ||
@@ -85,2 +64,6 @@ "repository": { | ||
"url": "https://opencollective.com/poolifier" | ||
}, | ||
{ | ||
"type": "github", | ||
"url": "https://github.com/sponsors/poolifier" | ||
} | ||
@@ -96,3 +79,3 @@ ], | ||
"devDependencies": { | ||
"@commitlint/cli": "^17.4.4", | ||
"@commitlint/cli": "^17.5.1", | ||
"@commitlint/config-conventional": "^17.4.4", | ||
@@ -103,15 +86,15 @@ "@release-it/bumper": "^4.0.2", | ||
"@rollup/plugin-typescript": "^11.0.0", | ||
"@types/node": "^18.15.3", | ||
"@typescript-eslint/eslint-plugin": "^5.55.0", | ||
"@typescript-eslint/parser": "^5.55.0", | ||
"@types/node": "^18.15.11", | ||
"@typescript-eslint/eslint-plugin": "^5.57.1", | ||
"@typescript-eslint/parser": "^5.57.1", | ||
"benny": "^3.7.1", | ||
"c8": "^7.13.0", | ||
"eslint": "^8.36.0", | ||
"eslint": "^8.37.0", | ||
"eslint-config-standard": "^17.0.0", | ||
"eslint-config-standard-with-typescript": "^34.0.1", | ||
"eslint-define-config": "^1.17.0", | ||
"eslint-import-resolver-typescript": "^3.5.3", | ||
"eslint-import-resolver-typescript": "^3.5.4", | ||
"eslint-plugin-import": "^2.27.5", | ||
"eslint-plugin-jsdoc": "^40.0.3", | ||
"eslint-plugin-n": "^15.6.1", | ||
"eslint-plugin-jsdoc": "^40.1.1", | ||
"eslint-plugin-n": "^15.7.0", | ||
"eslint-plugin-promise": "^6.1.1", | ||
@@ -126,15 +109,34 @@ "eslint-plugin-spellcheck": "^0.0.20", | ||
"mochawesome": "^7.1.3", | ||
"prettier": "^2.8.4", | ||
"prettier": "^2.8.7", | ||
"prettier-plugin-organize-imports": "^3.2.2", | ||
"release-it": "^15.9.0", | ||
"rollup": "^3.19.1", | ||
"release-it": "^15.10.1", | ||
"rollup": "^3.20.2", | ||
"rollup-plugin-analyzer": "^4.0.0", | ||
"rollup-plugin-command": "^1.1.3", | ||
"rollup-plugin-delete": "^2.0.0", | ||
"sinon": "^15.0.2", | ||
"sinon": "^15.0.3", | ||
"source-map-support": "^0.5.21", | ||
"ts-standard": "^12.0.2", | ||
"typedoc": "^0.23.27", | ||
"typescript": "^5.0.2" | ||
"typedoc": "^0.23.28", | ||
"typescript": "^5.0.3" | ||
}, | ||
"scripts": { | ||
"preinstall": "npx only-allow pnpm", | ||
"build": "rollup --config --environment BUILD:development", | ||
"build:typedoc": "rollup --config --environment BUILD:development --environment DOCUMENTATION", | ||
"build:prod": "rollup --config", | ||
"benchmark": "pnpm run build && node -r source-map-support/register benchmarks/internal/bench.js", | ||
"benchmark:debug": "pnpm run build && node -r source-map-support/register --inspect benchmarks/internal/bench.js", | ||
"benchmark:prod": "pnpm run build:prod && node -r source-map-support/register benchmarks/internal/bench.js", | ||
"test": "pnpm run build && c8 mocha 'tests/**/*.test.js'", | ||
"test:debug": "pnpm run build && mocha --no-parallel --inspect 'tests/**/*.test.js'", | ||
"coverage": "c8 report --reporter=lcov", | ||
"coverage:html": "c8 report --reporter=html", | ||
"format": "prettier . --cache --write; ts-standard . --fix", | ||
"lint": "eslint . --cache", | ||
"lint:fix": "eslint . --cache --fix", | ||
"lint:report": "eslint . --cache --format json --output-file reports/eslint.json", | ||
"release": "release-it", | ||
"typedoc": "typedoc" | ||
} | ||
} |
@@ -166,3 +166,4 @@ <div align="center"> | ||
- `WorkerChoiceStrategies.ROUND_ROBIN`: Submit tasks to worker in a round robbin fashion | ||
- `WorkerChoiceStrategies.LESS_RECENTLY_USED`: Submit tasks to the less recently used worker | ||
- `WorkerChoiceStrategies.LESS_USED`: Submit tasks to the less used worker | ||
- `WorkerChoiceStrategies.LESS_BUSY`: Submit tasks to the less busy worker | ||
- `WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN` Submit tasks to worker using a weighted round robin scheduling algorithm based on tasks execution time | ||
@@ -207,3 +208,3 @@ - `WorkerChoiceStrategies.FAIR_SHARE`: Submit tasks to worker using a fair share tasks scheduling algorithm based on tasks execution time | ||
**KillBehaviors.SOFT**: If `currentTime - lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker **won't** be deleted. | ||
**KillBehaviors.HARD**: If `lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker will be deleted. | ||
**KillBehaviors.HARD**: If `currentTime - lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker will be deleted. | ||
This option only apply to the newly created workers. | ||
@@ -210,0 +211,0 @@ Default: `KillBehaviors.SOFT` |
Sorry, the diff of this file is not supported yet
Install scripts
Supply chain riskInstall scripts are run when the package is installed. The majority of malware in npm is hidden in install scripts.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
104540
31
1296
270
0
3
1