]> Piment Noir Git Repositories - poolifier.git/commitdiff
perf: switch default worker choice strategy to LEAST_USED
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Sun, 16 Nov 2025 15:59:29 +0000 (16:59 +0100)
committerJérôme Benoit <jerome.benoit@piment-noir.org>
Sun, 16 Nov 2025 15:59:29 +0000 (16:59 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
docs/api.md
src/pools/abstract-pool.ts
src/pools/pool.ts
src/pools/selection-strategies/worker-choice-strategies-context.ts
tests/pools/abstract-pool.test.mjs
tests/pools/cluster/dynamic.test.mjs
tests/pools/selection-strategies/selection-strategies.test.mjs
tests/pools/selection-strategies/worker-choice-strategies-context.test.mjs
tests/pools/thread/dynamic.test.mjs

index 79243daa2728d1146910b22611f038cb7466436d..1d877dc106c0aac6e14932c071a35acbd698968c 100644 (file)
@@ -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:
index f96355b1ea5f8044b91ee91a34e2c3b86d9b6820..4ae124fabec5d7854bf41e178c2a18bcba46c8b6 100644 (file)
@@ -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
       )
index 7a03b7c68124a1e42cb4264d525324b0f4870315..9f54fc4d056b2832dec1c5a68d0bc9e9e4f0bcad 100644 (file)
@@ -347,7 +347,7 @@ export interface PoolOptions<Worker extends IWorker> {
   tasksQueueOptions?: TasksQueueOptions
   /**
    * The default worker choice strategy to use in this pool.
-   * @defaultValue WorkerChoiceStrategies.ROUND_ROBIN
+   * @defaultValue WorkerChoiceStrategies.LEAST_USED
    */
   workerChoiceStrategy?: WorkerChoiceStrategy
   /**
index 47107aee305818ea3f556697fe1ab933969ff6f3..66a27865a0f113cd583ee63923febbd0f025f7b6 100644 (file)
@@ -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<Worker, Data, Response>,
     workerChoiceStrategies: WorkerChoiceStrategy[] = [
-      WorkerChoiceStrategies.ROUND_ROBIN,
+      WorkerChoiceStrategies.LEAST_USED,
     ],
     opts?: WorkerChoiceStrategyOptions
   ) {
index a65360ae921e06eb08535eb8b13ab65280dd396f..952e599f88cd8249f8a868bea142990e56b544ba 100644 (file)
@@ -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' },
index 3dfaee0b576a99ecd41d733c084e88f130244af2..bf42098a21d632c130820019fb6301b27045524d 100644 (file)
@@ -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()
   })
index c188949d3a5f8bf25dd938253fadb576f5d892c9..7d7cab819e681f0b3f3390b8dc65c436be89a304 100644 (file)
@@ -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()
index c3bf192cd42e1cbd45bfa955571b4b02401987aa..8bec9a952112e22678c2906d13fee0f4a1bdb815 100644 (file)
@@ -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
     )
index c6e2de7ac102f7e9d26eb425d1d7ae1dcbb7c137..8c51c9e4169e4736790c69b58c0656e58cea1d2c 100644 (file)
@@ -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()
   })