From 1dadb963a6cd45ca19285f4ec0d71d2ad9b0f530 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sun, 16 Nov 2025 16:59:29 +0100 Subject: [PATCH] perf: switch default worker choice strategy to LEAST_USED MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- docs/api.md | 2 +- src/pools/abstract-pool.ts | 2 +- src/pools/pool.ts | 2 +- .../worker-choice-strategies-context.ts | 4 +-- tests/pools/abstract-pool.test.mjs | 30 +++++++++---------- tests/pools/cluster/dynamic.test.mjs | 6 ---- .../selection-strategies.test.mjs | 6 ++-- .../worker-choice-strategies-context.test.mjs | 16 +++++----- tests/pools/thread/dynamic.test.mjs | 6 ---- 9 files changed, 31 insertions(+), 43 deletions(-) diff --git a/docs/api.md b/docs/api.md index 79243daa2..1d877dc10 100644 --- a/docs/api.md +++ b/docs/api.md @@ -116,7 +116,7 @@ An object with these properties: - `WorkerChoiceStrategies.FAIR_SHARE`: Submit tasks to worker by using a [fair share scheduling algorithm](./worker-choice-strategies.md) based on tasks execution time (the default) or ELU active time `WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN`, `WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN` and `WorkerChoiceStrategies.FAIR_SHARE` strategies are targeted to heavy and long tasks. - Default: `WorkerChoiceStrategies.ROUND_ROBIN` + Default: `WorkerChoiceStrategies.LEAST_USED` - `workerChoiceStrategyOptions` (optional) - The worker choice strategy options object to use in this pool. Properties: diff --git a/src/pools/abstract-pool.ts b/src/pools/abstract-pool.ts index f96355b1e..4ae124fab 100644 --- a/src/pools/abstract-pool.ts +++ b/src/pools/abstract-pool.ts @@ -1516,7 +1516,7 @@ export abstract class AbstractPool< this.opts.startWorkers = opts.startWorkers ?? true checkValidWorkerChoiceStrategy(opts.workerChoiceStrategy) this.opts.workerChoiceStrategy = - opts.workerChoiceStrategy ?? WorkerChoiceStrategies.ROUND_ROBIN + opts.workerChoiceStrategy ?? WorkerChoiceStrategies.LEAST_USED this.checkValidWorkerChoiceStrategyOptions( opts.workerChoiceStrategyOptions ) diff --git a/src/pools/pool.ts b/src/pools/pool.ts index 7a03b7c68..9f54fc4d0 100644 --- a/src/pools/pool.ts +++ b/src/pools/pool.ts @@ -347,7 +347,7 @@ export interface PoolOptions { tasksQueueOptions?: TasksQueueOptions /** * The default worker choice strategy to use in this pool. - * @defaultValue WorkerChoiceStrategies.ROUND_ROBIN + * @defaultValue WorkerChoiceStrategies.LEAST_USED */ workerChoiceStrategy?: WorkerChoiceStrategy /** diff --git a/src/pools/selection-strategies/worker-choice-strategies-context.ts b/src/pools/selection-strategies/worker-choice-strategies-context.ts index 47107aee3..66a27865a 100644 --- a/src/pools/selection-strategies/worker-choice-strategies-context.ts +++ b/src/pools/selection-strategies/worker-choice-strategies-context.ts @@ -60,13 +60,13 @@ export class WorkerChoiceStrategiesContext< * Worker choice strategies context constructor. * @param pool - The pool instance. * @param workerChoiceStrategies - The worker choice strategies. - * @defaultValue [WorkerChoiceStrategies.ROUND_ROBIN] + * @defaultValue [WorkerChoiceStrategies.LEAST_USED] * @param opts - The worker choice strategy options. */ public constructor ( private readonly pool: IPool, workerChoiceStrategies: WorkerChoiceStrategy[] = [ - WorkerChoiceStrategies.ROUND_ROBIN, + WorkerChoiceStrategies.LEAST_USED, ], opts?: WorkerChoiceStrategyOptions ) { diff --git a/tests/pools/abstract-pool.test.mjs b/tests/pools/abstract-pool.test.mjs index a65360ae9..952e599f8 100644 --- a/tests/pools/abstract-pool.test.mjs +++ b/tests/pools/abstract-pool.test.mjs @@ -228,7 +228,7 @@ describe('Abstract pool test suite', () => { enableTasksQueue: false, restartWorkerOnError: true, startWorkers: true, - workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN, + workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED, }) for (const [, workerChoiceStrategy] of pool.workerChoiceStrategiesContext .workerChoiceStrategies) { @@ -751,7 +751,7 @@ describe('Abstract pool test suite', () => { ) expect(pool.info).toStrictEqual({ busyWorkerNodes: 0, - defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN, + defaultStrategy: WorkerChoiceStrategies.LEAST_USED, executedTasks: 0, executingTasks: 0, failedTasks: 0, @@ -774,7 +774,7 @@ describe('Abstract pool test suite', () => { ) expect(pool.info).toStrictEqual({ busyWorkerNodes: 0, - defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN, + defaultStrategy: WorkerChoiceStrategies.LEAST_USED, dynamicWorkerNodes: 0, executedTasks: 0, executingTasks: 0, @@ -1139,7 +1139,7 @@ describe('Abstract pool test suite', () => { expect(poolReady).toBe(1) expect(poolInfo).toStrictEqual({ busyWorkerNodes: 0, - defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN, + defaultStrategy: WorkerChoiceStrategies.LEAST_USED, dynamicWorkerNodes: 0, executedTasks: 0, executingTasks: 0, @@ -1188,7 +1188,7 @@ describe('Abstract pool test suite', () => { expect(poolBusy).toBe(1) expect(poolBusyInfo).toStrictEqual({ busyWorkerNodes: numberOfWorkers, - defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN, + defaultStrategy: WorkerChoiceStrategies.LEAST_USED, executedTasks: expect.any(Number), executingTasks: expect.any(Number), failedTasks: expect.any(Number), @@ -1206,7 +1206,7 @@ describe('Abstract pool test suite', () => { expect(poolBusyEnd).toBe(1) expect(poolBusyEndInfo).toStrictEqual({ busyWorkerNodes: expect.any(Number), - defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN, + defaultStrategy: WorkerChoiceStrategies.LEAST_USED, executedTasks: expect.any(Number), executingTasks: expect.any(Number), failedTasks: expect.any(Number), @@ -1256,7 +1256,7 @@ describe('Abstract pool test suite', () => { expect(poolFull).toBe(1) expect(poolFullInfo).toStrictEqual({ busyWorkerNodes: expect.any(Number), - defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN, + defaultStrategy: WorkerChoiceStrategies.LEAST_USED, dynamicWorkerNodes: Math.floor(numberOfWorkers / 2), executedTasks: expect.any(Number), executingTasks: expect.any(Number), @@ -1276,7 +1276,7 @@ describe('Abstract pool test suite', () => { expect(poolFullEnd).toBe(1) expect(poolFullEndInfo).toStrictEqual({ busyWorkerNodes: expect.any(Number), - defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN, + defaultStrategy: WorkerChoiceStrategies.LEAST_USED, dynamicWorkerNodes: 0, executedTasks: expect.any(Number), executingTasks: expect.any(Number), @@ -1330,7 +1330,7 @@ describe('Abstract pool test suite', () => { backPressure: true, backPressureWorkerNodes: numberOfWorkers, busyWorkerNodes: expect.any(Number), - defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN, + defaultStrategy: WorkerChoiceStrategies.LEAST_USED, executedTasks: expect.any(Number), executingTasks: expect.any(Number), failedTasks: expect.any(Number), @@ -1354,7 +1354,7 @@ describe('Abstract pool test suite', () => { backPressure: false, backPressureWorkerNodes: expect.any(Number), busyWorkerNodes: expect.any(Number), - defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN, + defaultStrategy: WorkerChoiceStrategies.LEAST_USED, executedTasks: expect.any(Number), executingTasks: expect.any(Number), failedTasks: expect.any(Number), @@ -1402,7 +1402,7 @@ describe('Abstract pool test suite', () => { expect(poolEmpty).toBe(1) expect(poolInfo).toStrictEqual({ busyWorkerNodes: 0, - defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN, + defaultStrategy: WorkerChoiceStrategies.LEAST_USED, dynamicWorkerNodes: 0, executedTasks: expect.any(Number), executingTasks: expect.any(Number), @@ -1608,7 +1608,7 @@ describe('Abstract pool test suite', () => { ]) expect([ ...dynamicThreadPool.workerChoiceStrategiesContext.workerChoiceStrategies.keys(), - ]).toStrictEqual([WorkerChoiceStrategies.ROUND_ROBIN]) + ]).toStrictEqual([WorkerChoiceStrategies.LEAST_USED]) const echoTaskFunction = data => { return data } @@ -1626,7 +1626,7 @@ describe('Abstract pool test suite', () => { expect([ ...dynamicThreadPool.workerChoiceStrategiesContext.workerChoiceStrategies.keys(), ]).toStrictEqual([ - WorkerChoiceStrategies.ROUND_ROBIN, + WorkerChoiceStrategies.LEAST_USED, WorkerChoiceStrategies.LEAST_ELU, ]) expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([ @@ -1746,7 +1746,7 @@ describe('Abstract pool test suite', () => { expect([ ...dynamicThreadPool.workerChoiceStrategiesContext.workerChoiceStrategies.keys(), ]).toStrictEqual([ - WorkerChoiceStrategies.ROUND_ROBIN, + WorkerChoiceStrategies.LEAST_USED, WorkerChoiceStrategies.LEAST_ELU, ]) expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([ @@ -1761,7 +1761,7 @@ describe('Abstract pool test suite', () => { expect(dynamicThreadPool.taskFunctions.get('echo')).toBeUndefined() expect([ ...dynamicThreadPool.workerChoiceStrategiesContext.workerChoiceStrategies.keys(), - ]).toStrictEqual([WorkerChoiceStrategies.ROUND_ROBIN]) + ]).toStrictEqual([WorkerChoiceStrategies.LEAST_USED]) expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([ { name: DEFAULT_TASK_NAME }, { name: 'test' }, diff --git a/tests/pools/cluster/dynamic.test.mjs b/tests/pools/cluster/dynamic.test.mjs index 3dfaee0b5..bf42098a2 100644 --- a/tests/pools/cluster/dynamic.test.mjs +++ b/tests/pools/cluster/dynamic.test.mjs @@ -130,12 +130,6 @@ describe('Dynamic cluster pool test suite', () => { ) expect(exitEvents).toBe(max - min) expect(longRunningPool.workerNodes.length).toBe(min) - expect( - longRunningPool.workerChoiceStrategiesContext.workerChoiceStrategies.get( - longRunningPool.workerChoiceStrategiesContext - .defaultWorkerChoiceStrategy - ).nextWorkerNodeKey - ).toBeLessThan(longRunningPool.workerNodes.length) // We need to clean up the resources after our test await longRunningPool.destroy() }) diff --git a/tests/pools/selection-strategies/selection-strategies.test.mjs b/tests/pools/selection-strategies/selection-strategies.test.mjs index c188949d3..7d7cab819 100644 --- a/tests/pools/selection-strategies/selection-strategies.test.mjs +++ b/tests/pools/selection-strategies/selection-strategies.test.mjs @@ -28,17 +28,17 @@ describe('Selection strategies test suite', () => { ) }) - it('Verify ROUND_ROBIN strategy is the default at pool creation', async () => { + it('Verify LEAST_USED strategy is the default at pool creation', async () => { const pool = new DynamicThreadPool( min, max, './tests/worker-files/thread/testWorker.mjs' ) expect(pool.opts.workerChoiceStrategy).toBe( - WorkerChoiceStrategies.ROUND_ROBIN + WorkerChoiceStrategies.LEAST_USED ) expect(pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe( - WorkerChoiceStrategies.ROUND_ROBIN + WorkerChoiceStrategies.LEAST_USED ) // We need to clean up the resources after our test await pool.destroy() diff --git a/tests/pools/selection-strategies/worker-choice-strategies-context.test.mjs b/tests/pools/selection-strategies/worker-choice-strategies-context.test.mjs index c3bf192cd..8bec9a952 100644 --- a/tests/pools/selection-strategies/worker-choice-strategies-context.test.mjs +++ b/tests/pools/selection-strategies/worker-choice-strategies-context.test.mjs @@ -50,7 +50,7 @@ describe('Worker choice strategies context test suite', () => { workerChoiceStrategiesContext.workerChoiceStrategies.get( workerChoiceStrategiesContext.defaultWorkerChoiceStrategy ) - ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy) + ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy) workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext( dynamicPool ) @@ -59,7 +59,7 @@ describe('Worker choice strategies context test suite', () => { workerChoiceStrategiesContext.workerChoiceStrategies.get( workerChoiceStrategiesContext.defaultWorkerChoiceStrategy ) - ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy) + ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy) }) it('Verify that constructor() initializes the context with retries attribute properly set', () => { @@ -82,7 +82,7 @@ describe('Worker choice strategies context test suite', () => { fixedPool ) expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe( - WorkerChoiceStrategies.ROUND_ROBIN + WorkerChoiceStrategies.LEAST_USED ) const workerChoiceStrategyUndefinedStub = workerChoiceStrategiesContext.workerChoiceStrategies.get( @@ -135,7 +135,7 @@ describe('Worker choice strategies context test suite', () => { fixedPool ) expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe( - WorkerChoiceStrategies.ROUND_ROBIN + WorkerChoiceStrategies.LEAST_USED ) const workerChoiceStrategyStub = workerChoiceStrategiesContext.workerChoiceStrategies.get( @@ -165,7 +165,7 @@ describe('Worker choice strategies context test suite', () => { fixedPool ) expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe( - WorkerChoiceStrategies.ROUND_ROBIN + WorkerChoiceStrategies.LEAST_USED ) const workerChoiceStrategyStub = workerChoiceStrategiesContext.workerChoiceStrategies.get( @@ -184,7 +184,7 @@ describe('Worker choice strategies context test suite', () => { dynamicPool ) expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe( - WorkerChoiceStrategies.ROUND_ROBIN + WorkerChoiceStrategies.LEAST_USED ) const workerChoiceStrategyStub = workerChoiceStrategiesContext.workerChoiceStrategies.get( @@ -206,7 +206,7 @@ describe('Worker choice strategies context test suite', () => { workerChoiceStrategiesContext.workerChoiceStrategies.get( workerChoiceStrategiesContext.defaultWorkerChoiceStrategy ) - ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy) + ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy) workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy( WorkerChoiceStrategies.ROUND_ROBIN ) @@ -225,7 +225,7 @@ describe('Worker choice strategies context test suite', () => { workerChoiceStrategiesContext.workerChoiceStrategies.get( workerChoiceStrategiesContext.defaultWorkerChoiceStrategy ) - ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy) + ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy) workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy( WorkerChoiceStrategies.ROUND_ROBIN ) diff --git a/tests/pools/thread/dynamic.test.mjs b/tests/pools/thread/dynamic.test.mjs index c6e2de7ac..8c51c9e41 100644 --- a/tests/pools/thread/dynamic.test.mjs +++ b/tests/pools/thread/dynamic.test.mjs @@ -130,12 +130,6 @@ describe('Dynamic thread pool test suite', () => { ) expect(exitEvents).toBe(max - min) expect(longRunningPool.workerNodes.length).toBe(min) - expect( - longRunningPool.workerChoiceStrategiesContext.workerChoiceStrategies.get( - longRunningPool.workerChoiceStrategiesContext - .defaultWorkerChoiceStrategy - ).nextWorkerNodeKey - ).toBeLessThan(longRunningPool.workerNodes.length) // We need to clean up the resources after our test await longRunningPool.destroy() }) -- 2.43.0