X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fpools%2Fabstract-pool.ts;h=b9a7cd398920fc77a754d3d4047414beae94ba46;hb=80662c0aec09419d198a5daba024674922ca5945;hp=edf2b29a6e9de328133735cea8a6c58b18609db0;hpb=557991e8ddb66a72a47d62606e142e8be86677f2;p=poolifier.git diff --git a/src/pools/abstract-pool.ts b/src/pools/abstract-pool.ts index edf2b29a..b9a7cd39 100644 --- a/src/pools/abstract-pool.ts +++ b/src/pools/abstract-pool.ts @@ -3,6 +3,7 @@ import type { MessageValue, PromiseResponseWrapper } from '../utility-types' import { DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS, EMPTY_FUNCTION, + isPlainObject, median } from '../utils' import { KillBehaviors, isKillBehavior } from '../worker/worker-options' @@ -126,7 +127,7 @@ export abstract class AbstractPool< ) } else if (!Number.isSafeInteger(numberOfWorkers)) { throw new TypeError( - 'Cannot instantiate a pool with a non integer number of workers' + 'Cannot instantiate a pool with a non safe integer number of workers' ) } else if (numberOfWorkers < 0) { throw new RangeError( @@ -138,20 +139,28 @@ export abstract class AbstractPool< } private checkPoolOptions (opts: PoolOptions): void { - this.opts.workerChoiceStrategy = - opts.workerChoiceStrategy ?? WorkerChoiceStrategies.ROUND_ROBIN - this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy) - this.opts.workerChoiceStrategyOptions = - opts.workerChoiceStrategyOptions ?? DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS - this.opts.enableEvents = opts.enableEvents ?? true - this.opts.enableTasksQueue = opts.enableTasksQueue ?? false - if (this.opts.enableTasksQueue) { - this.checkValidTasksQueueOptions( - opts.tasksQueueOptions as TasksQueueOptions - ) - this.opts.tasksQueueOptions = this.buildTasksQueueOptions( - opts.tasksQueueOptions as TasksQueueOptions + if (isPlainObject(opts)) { + this.opts.workerChoiceStrategy = + opts.workerChoiceStrategy ?? WorkerChoiceStrategies.ROUND_ROBIN + this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy) + this.opts.workerChoiceStrategyOptions = + opts.workerChoiceStrategyOptions ?? + DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS + this.checkValidWorkerChoiceStrategyOptions( + this.opts.workerChoiceStrategyOptions ) + this.opts.enableEvents = opts.enableEvents ?? true + this.opts.enableTasksQueue = opts.enableTasksQueue ?? false + if (this.opts.enableTasksQueue) { + this.checkValidTasksQueueOptions( + opts.tasksQueueOptions as TasksQueueOptions + ) + this.opts.tasksQueueOptions = this.buildTasksQueueOptions( + opts.tasksQueueOptions as TasksQueueOptions + ) + } + } else { + throw new TypeError('Invalid pool options: must be a plain object') } } @@ -165,9 +174,30 @@ export abstract class AbstractPool< } } + private checkValidWorkerChoiceStrategyOptions ( + workerChoiceStrategyOptions: WorkerChoiceStrategyOptions + ): void { + if (!isPlainObject(workerChoiceStrategyOptions)) { + throw new TypeError( + 'Invalid worker choice strategy options: must be a plain object' + ) + } + if ( + workerChoiceStrategyOptions.weights != null && + Object.keys(workerChoiceStrategyOptions.weights).length !== this.size + ) { + throw new Error( + 'Invalid worker choice strategy options: must have a weight for each worker node' + ) + } + } + private checkValidTasksQueueOptions ( tasksQueueOptions: TasksQueueOptions ): void { + if (tasksQueueOptions != null && !isPlainObject(tasksQueueOptions)) { + throw new TypeError('Invalid tasks queue options: must be a plain object') + } if ((tasksQueueOptions?.concurrency as number) <= 0) { throw new Error( `Invalid worker tasks concurrency '${ @@ -180,6 +210,9 @@ export abstract class AbstractPool< /** @inheritDoc */ public abstract get type (): PoolType + /** @inheritDoc */ + public abstract get size (): number + /** * Number of tasks running in the pool. */ @@ -245,6 +278,7 @@ export abstract class AbstractPool< public setWorkerChoiceStrategyOptions ( workerChoiceStrategyOptions: WorkerChoiceStrategyOptions ): void { + this.checkValidWorkerChoiceStrategyOptions(workerChoiceStrategyOptions) this.opts.workerChoiceStrategyOptions = workerChoiceStrategyOptions this.workerChoiceStrategyContext.setOptions( this.opts.workerChoiceStrategyOptions @@ -299,7 +333,7 @@ export abstract class AbstractPool< protected internalBusy (): boolean { return ( this.workerNodes.findIndex(workerNode => { - return workerNode.tasksUsage?.running === 0 + return workerNode.tasksUsage.running === 0 }) === -1 ) } @@ -331,6 +365,7 @@ export abstract class AbstractPool< } else { this.executeTask(workerNodeKey, submittedTask) } + this.workerChoiceStrategyContext.update(workerNodeKey) this.checkAndEmitEvents() // eslint-disable-next-line @typescript-eslint/return-await return res @@ -389,7 +424,8 @@ export abstract class AbstractPool< worker: Worker, message: MessageValue ): void { - const workerTasksUsage = this.getWorkerTasksUsage(worker) + const workerNodeKey = this.getWorkerNodeKey(worker) + const workerTasksUsage = this.workerNodes[workerNodeKey].tasksUsage --workerTasksUsage.running ++workerTasksUsage.run if (message.error != null) { @@ -404,8 +440,11 @@ export abstract class AbstractPool< workerTasksUsage.avgRunTime = workerTasksUsage.runTime / workerTasksUsage.run } - if (this.workerChoiceStrategyContext.getRequiredStatistics().medRunTime) { - workerTasksUsage.runTimeHistory.push(message.runTime ?? 0) + if ( + this.workerChoiceStrategyContext.getRequiredStatistics().medRunTime && + message.runTime != null + ) { + workerTasksUsage.runTimeHistory.push(message.runTime) workerTasksUsage.medRunTime = median(workerTasksUsage.runTimeHistory) } } @@ -423,13 +462,14 @@ export abstract class AbstractPool< if (this.type === PoolType.DYNAMIC && !this.full && this.internalBusy()) { const workerCreated = this.createAndSetupWorker() this.registerWorkerMessageListener(workerCreated, message => { + const currentWorkerNodeKey = this.getWorkerNodeKey(workerCreated) if ( isKillBehavior(KillBehaviors.HARD, message.kill) || (message.kill != null && - this.getWorkerTasksUsage(workerCreated)?.running === 0) + this.workerNodes[currentWorkerNodeKey].tasksUsage.running === 0) ) { // Kill message received from the worker: no new tasks are submitted to that worker for a while ( > maxInactiveTime) - this.flushTasksQueueByWorker(workerCreated) + this.flushTasksQueue(currentWorkerNodeKey) void (this.destroyWorker(workerCreated) as Promise) } }) @@ -555,21 +595,6 @@ export abstract class AbstractPool< workerNode.tasksUsage = tasksUsage } - /** - * Gets the given worker its tasks usage in the pool. - * - * @param worker - The worker. - * @throws Error if the worker is not found in the pool worker nodes. - * @returns The worker tasks usage. - */ - private getWorkerTasksUsage (worker: Worker): TasksUsage { - const workerNodeKey = this.getWorkerNodeKey(worker) - if (workerNodeKey !== -1) { - return this.workerNodes[workerNodeKey].tasksUsage - } - throw new Error('Worker could not be found in the pool worker nodes') - } - /** * Pushes the given worker in the pool worker nodes. * @@ -652,11 +677,6 @@ export abstract class AbstractPool< } } - private flushTasksQueueByWorker (worker: Worker): void { - const workerNodeKey = this.getWorkerNodeKey(worker) - this.flushTasksQueue(workerNodeKey) - } - private flushTasksQueues (): void { for (const [workerNodeKey] of this.workerNodes.entries()) { this.flushTasksQueue(workerNodeKey)