fix: properly account worker choice retries for WRR
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Wed, 20 Dec 2023 11:49:35 +0000 (12:49 +0100)
committerJérôme Benoit <jerome.benoit@piment-noir.org>
Wed, 20 Dec 2023 11:49:35 +0000 (12:49 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
12 files changed:
examples/typescript/http-client-pool/httpd-echo.js
src/pools/abstract-pool.ts
src/pools/selection-strategies/abstract-worker-choice-strategy.ts
src/pools/selection-strategies/interleaved-weighted-round-robin-worker-choice-strategy.ts
src/pools/selection-strategies/selection-strategies-types.ts
src/pools/selection-strategies/weighted-round-robin-worker-choice-strategy.ts
src/pools/selection-strategies/worker-choice-strategy-context.ts
src/utils.ts
tests/pools/abstract-pool.test.mjs
tests/pools/selection-strategies/selection-strategies.test.mjs
tests/pools/selection-strategies/worker-choice-strategy-context.test.mjs
tests/utils.test.mjs

index 53b8ad116592153f7bece55d296db77536e1241e..81b90651a05caa1fd326e29881e57d050f7e6ec1 100644 (file)
@@ -15,7 +15,7 @@ server
 
         console.info(`==== ${request.method} ${request.url} ====`)
         console.info('> Headers')
-        console.log(request.headers)
+        console.info(request.headers)
 
         console.info('> Body')
         console.info(body)
index cc9408f2fed30acb97a0dfdca34d0994c245227e..4bfc3acbc47fb80b001f54f2ab429937fa46e2a9 100644 (file)
@@ -130,10 +130,10 @@ export abstract class AbstractPool<
   /**
    * Constructs a new poolifier pool.
    *
-   * @param minimumNumberOfWorkers - Minimum number of workers that this pool should manage.
+   * @param minimumNumberOfWorkers - Minimum number of workers that this pool manages.
    * @param filePath - Path to the worker file.
    * @param opts - Options for the pool.
-   * @param maximumNumberOfWorkers - Maximum number of workers that this pool should manage.
+   * @param maximumNumberOfWorkers - Maximum number of workers that this pool manages.
    */
   public constructor (
     protected readonly minimumNumberOfWorkers: number,
index b15ade84023167c4a507b1bacb32a98857fe731d..91733b52c96cf1f7eff70142146965290772a3aa 100644 (file)
@@ -1,7 +1,6 @@
-import { cpus } from 'node:os'
 import {
   DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
-  getDefaultInternalWorkerChoiceStrategyOptions
+  buildInternalWorkerChoiceStrategyOptions
 } from '../../utils'
 import type { IPool } from '../pool'
 import type { IWorker } from '../worker'
@@ -112,12 +111,10 @@ export abstract class AbstractWorkerChoiceStrategy<
 
   /** @inheritDoc */
   public setOptions (opts: InternalWorkerChoiceStrategyOptions): void {
-    this.opts = {
-      ...getDefaultInternalWorkerChoiceStrategyOptions(
-        this.pool.info.maxSize + Object.keys(opts?.weights ?? {}).length
-      ),
-      ...opts
-    }
+    this.opts = buildInternalWorkerChoiceStrategyOptions(
+      this.pool.info.maxSize,
+      opts
+    )
     this.setTaskStatisticsRequirements(this.opts)
   }
 
@@ -195,15 +192,4 @@ export abstract class AbstractWorkerChoiceStrategy<
   protected setPreviousWorkerNodeKey (workerNodeKey: number | undefined): void {
     this.previousWorkerNodeKey = workerNodeKey ?? this.previousWorkerNodeKey
   }
-
-  protected computeDefaultWorkerWeight (): number {
-    let cpusCycleTimeWeight = 0
-    for (const cpu of cpus()) {
-      // CPU estimated cycle time
-      const numberOfDigits = cpu.speed.toString().length - 1
-      const cpuCycleTime = 1 / (cpu.speed / Math.pow(10, numberOfDigits))
-      cpusCycleTimeWeight += cpuCycleTime * Math.pow(10, numberOfDigits)
-    }
-    return Math.round(cpusCycleTimeWeight / cpus().length)
-  }
 }
index 8ccb5f7890db974d91cd24b2b247a2a04f2ce67c..0f718e9817159bd6aedb5012e3246786d4696f50 100644 (file)
@@ -37,14 +37,10 @@ export class InterleavedWeightedRoundRobinWorkerChoiceStrategy<
    * Round id.
    */
   private roundId: number = 0
-  /**
-   * Default worker weight.
-   */
-  private readonly defaultWorkerWeight: number
   /**
    * Round weights.
    */
-  private roundWeights: number[]
+  private roundWeights!: number[]
   /**
    * Worker node id.
    */
@@ -60,9 +56,7 @@ export class InterleavedWeightedRoundRobinWorkerChoiceStrategy<
     opts: InternalWorkerChoiceStrategyOptions
   ) {
     super(pool, opts)
-    this.setTaskStatisticsRequirements(this.opts)
-    this.defaultWorkerWeight = this.computeDefaultWorkerWeight()
-    this.roundWeights = this.getRoundWeights()
+    this.setOptions(this.opts)
   }
 
   /** @inheritDoc */
@@ -99,8 +93,7 @@ export class InterleavedWeightedRoundRobinWorkerChoiceStrategy<
         ) {
           this.workerNodeVirtualTaskRunTime = 0
         }
-        const workerWeight =
-          this.opts.weights?.[workerNodeKey] ?? this.defaultWorkerWeight
+        const workerWeight = this.opts.weights?.[workerNodeKey] as number
         if (
           this.isWorkerNodeReady(workerNodeKey) &&
           workerWeight >= this.roundWeights[roundIndex] &&
@@ -160,12 +153,9 @@ export class InterleavedWeightedRoundRobinWorkerChoiceStrategy<
   }
 
   private getRoundWeights (): number[] {
-    if (this.opts.weights == null) {
-      return [this.defaultWorkerWeight]
-    }
     return [
       ...new Set(
-        Object.values(this.opts.weights)
+        Object.values(this.opts.weights as Record<number, number>)
           .slice()
           .sort((a, b) => a - b)
       )
index 59b6a84f90e29dbd05a08dec2020428b7e2fe436..1467ab4f566317742dca3f453d6cfedff860fa16 100644 (file)
@@ -95,7 +95,7 @@ export interface WorkerChoiceStrategyOptions {
    *
    * @defaultValue Weights computed automatically given the CPU performance.
    */
-  readonly weights?: Record<number, number>
+  weights?: Record<number, number>
 }
 
 /**
index fea72350ffafbb50b44f9a56935f17038952ef91..20e58752c7f5f39494b76d9eb349e4d09755adc8 100644 (file)
@@ -34,10 +34,6 @@ export class WeightedRoundRobinWorkerChoiceStrategy<
     elu: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS
   }
 
-  /**
-   * Default worker weight.
-   */
-  private readonly defaultWorkerWeight: number
   /**
    * Worker node virtual task runtime.
    */
@@ -49,8 +45,7 @@ export class WeightedRoundRobinWorkerChoiceStrategy<
     opts: InternalWorkerChoiceStrategyOptions
   ) {
     super(pool, opts)
-    this.setTaskStatisticsRequirements(this.opts)
-    this.defaultWorkerWeight = this.computeDefaultWorkerWeight()
+    this.setOptions(this.opts)
   }
 
   /** @inheritDoc */
@@ -94,10 +89,9 @@ export class WeightedRoundRobinWorkerChoiceStrategy<
   }
 
   private weightedRoundRobinNextWorkerNodeKey (): number | undefined {
-    const workerWeight =
-      this.opts.weights?.[
-        this.nextWorkerNodeKey ?? this.previousWorkerNodeKey
-      ] ?? this.defaultWorkerWeight
+    const workerWeight = this.opts.weights?.[
+      this.nextWorkerNodeKey ?? this.previousWorkerNodeKey
+    ] as number
     if (this.workerNodeVirtualTaskRunTime < workerWeight) {
       this.workerNodeVirtualTaskRunTime =
         this.workerNodeVirtualTaskRunTime +
index 6f15daa0645174b4ce55008dbd46057a40f9c927..9a015347bc2a430172855116224da8cfc89f2800 100644 (file)
@@ -1,4 +1,4 @@
-import { getDefaultInternalWorkerChoiceStrategyOptions } from '../../utils'
+import { buildInternalWorkerChoiceStrategyOptions } from '../../utils'
 import type { IPool } from '../pool'
 import type { IWorker } from '../worker'
 import { FairShareWorkerChoiceStrategy } from './fair-share-worker-choice-strategy'
@@ -46,12 +46,10 @@ export class WorkerChoiceStrategyContext<
     private workerChoiceStrategy: WorkerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN,
     private opts?: InternalWorkerChoiceStrategyOptions
   ) {
-    this.opts = {
-      ...getDefaultInternalWorkerChoiceStrategyOptions(
-        pool.info.maxSize + Object.keys(this.opts?.weights ?? {}).length
-      ),
-      ...this.opts
-    }
+    this.opts = buildInternalWorkerChoiceStrategyOptions(
+      pool.info.maxSize,
+      this.opts
+    )
     this.execute = this.execute.bind(this)
     this.workerChoiceStrategies = new Map<
     WorkerChoiceStrategy,
@@ -233,12 +231,10 @@ export class WorkerChoiceStrategyContext<
     pool: IPool<Worker, Data, Response>,
     opts?: InternalWorkerChoiceStrategyOptions
   ): void {
-    this.opts = {
-      ...getDefaultInternalWorkerChoiceStrategyOptions(
-        pool.info.maxSize + Object.keys(opts?.weights ?? {}).length
-      ),
-      ...opts
-    }
+    this.opts = buildInternalWorkerChoiceStrategyOptions(
+      pool.info.maxSize,
+      opts
+    )
     for (const workerChoiceStrategy of this.workerChoiceStrategies.values()) {
       workerChoiceStrategy.setOptions(this.opts)
     }
index 2dd3947c2f42bf081473dc08ccb34bf4cf30e4b1..20493767db87d80689a6cb067df3e22f72b759ce 100644 (file)
@@ -2,6 +2,7 @@ import * as os from 'node:os'
 import { getRandomValues } from 'node:crypto'
 import { Worker as ClusterWorker } from 'node:cluster'
 import { Worker as ThreadWorker } from 'node:worker_threads'
+import { cpus } from 'node:os'
 import type {
   InternalWorkerChoiceStrategyOptions,
   MeasurementStatisticsRequirements
@@ -27,7 +28,7 @@ export const EMPTY_FUNCTION: () => void = Object.freeze(() => {
  * @param retries - The number of worker choice retries.
  * @returns The default worker choice strategy options.
  */
-export const getDefaultInternalWorkerChoiceStrategyOptions = (
+const getDefaultInternalWorkerChoiceStrategyOptions = (
   retries: number
 ): InternalWorkerChoiceStrategyOptions => {
   return {
@@ -280,3 +281,45 @@ export const once = <T, A extends any[], R>(
     return result
   }
 }
+
+const clone = <T extends object>(object: T): T => {
+  return JSON.parse(JSON.stringify(object)) as T
+}
+
+export const buildInternalWorkerChoiceStrategyOptions = (
+  poolMaxSize: number,
+  opts?: InternalWorkerChoiceStrategyOptions
+): InternalWorkerChoiceStrategyOptions => {
+  opts = clone(opts ?? {})
+  if (opts.weights == null) {
+    opts.weights = getDefaultWeights(poolMaxSize)
+  }
+  return {
+    ...getDefaultInternalWorkerChoiceStrategyOptions(
+      poolMaxSize + Object.keys(opts?.weights ?? {}).length
+    ),
+    ...opts
+  }
+}
+
+const getDefaultWeights = (
+  poolMaxSize: number,
+  defaultWorkerWeight: number = getDefaultWorkerWeight()
+): Record<number, number> => {
+  const weights: Record<number, number> = {}
+  for (let workerNodeKey = 0; workerNodeKey < poolMaxSize; workerNodeKey++) {
+    weights[workerNodeKey] = defaultWorkerWeight
+  }
+  return weights
+}
+
+const getDefaultWorkerWeight = (): number => {
+  let cpusCycleTimeWeight = 0
+  for (const cpu of cpus()) {
+    // CPU estimated cycle time
+    const numberOfDigits = cpu.speed.toString().length - 1
+    const cpuCycleTime = 1 / (cpu.speed / Math.pow(10, numberOfDigits))
+    cpusCycleTimeWeight += cpuCycleTime * Math.pow(10, numberOfDigits)
+  }
+  return Math.round(cpusCycleTimeWeight / cpus().length)
+}
index 29f038c77b1e6e614b9ab9f15ee9cdbc1511cf7f..d70d017b715c61023c4c50c20c35986aeb175de0 100644 (file)
@@ -229,18 +229,30 @@ describe('Abstract pool test suite', () => {
       workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN
     })
     expect(pool.workerChoiceStrategyContext.opts).toStrictEqual({
-      retries: pool.info.maxSize,
+      retries:
+        pool.info.maxSize +
+        Object.keys(pool.workerChoiceStrategyContext.opts.weights).length,
       runTime: { median: false },
       waitTime: { median: false },
-      elu: { median: false }
+      elu: { median: false },
+      weights: expect.objectContaining({
+        0: expect.any(Number),
+        [pool.info.maxSize - 1]: expect.any(Number)
+      })
     })
     for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext
       .workerChoiceStrategies) {
       expect(workerChoiceStrategy.opts).toStrictEqual({
-        retries: pool.info.maxSize,
+        retries:
+          pool.info.maxSize +
+          Object.keys(workerChoiceStrategy.opts.weights).length,
         runTime: { median: false },
         waitTime: { median: false },
-        elu: { median: false }
+        elu: { median: false },
+        weights: expect.objectContaining({
+          0: expect.any(Number),
+          [pool.info.maxSize - 1]: expect.any(Number)
+        })
       })
     }
     await pool.destroy()
@@ -459,18 +471,30 @@ describe('Abstract pool test suite', () => {
     )
     expect(pool.opts.workerChoiceStrategyOptions).toBeUndefined()
     expect(pool.workerChoiceStrategyContext.opts).toStrictEqual({
-      retries: pool.info.maxSize,
+      retries:
+        pool.info.maxSize +
+        Object.keys(pool.workerChoiceStrategyContext.opts.weights).length,
       runTime: { median: false },
       waitTime: { median: false },
-      elu: { median: false }
+      elu: { median: false },
+      weights: expect.objectContaining({
+        0: expect.any(Number),
+        [pool.info.maxSize - 1]: expect.any(Number)
+      })
     })
     for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext
       .workerChoiceStrategies) {
       expect(workerChoiceStrategy.opts).toStrictEqual({
-        retries: pool.info.maxSize,
+        retries:
+          pool.info.maxSize +
+          Object.keys(workerChoiceStrategy.opts.weights).length,
         runTime: { median: false },
         waitTime: { median: false },
-        elu: { median: false }
+        elu: { median: false },
+        weights: expect.objectContaining({
+          0: expect.any(Number),
+          [pool.info.maxSize - 1]: expect.any(Number)
+        })
       })
     }
     expect(
@@ -501,18 +525,30 @@ describe('Abstract pool test suite', () => {
       elu: { median: true }
     })
     expect(pool.workerChoiceStrategyContext.opts).toStrictEqual({
-      retries: pool.info.maxSize,
+      retries:
+        pool.info.maxSize +
+        Object.keys(pool.workerChoiceStrategyContext.opts.weights).length,
       runTime: { median: true },
       waitTime: { median: false },
-      elu: { median: true }
+      elu: { median: true },
+      weights: expect.objectContaining({
+        0: expect.any(Number),
+        [pool.info.maxSize - 1]: expect.any(Number)
+      })
     })
     for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext
       .workerChoiceStrategies) {
       expect(workerChoiceStrategy.opts).toStrictEqual({
-        retries: pool.info.maxSize,
+        retries:
+          pool.info.maxSize +
+          Object.keys(workerChoiceStrategy.opts.weights).length,
         runTime: { median: true },
         waitTime: { median: false },
-        elu: { median: true }
+        elu: { median: true },
+        weights: expect.objectContaining({
+          0: expect.any(Number),
+          [pool.info.maxSize - 1]: expect.any(Number)
+        })
       })
     }
     expect(
@@ -543,18 +579,30 @@ describe('Abstract pool test suite', () => {
       elu: { median: false }
     })
     expect(pool.workerChoiceStrategyContext.opts).toStrictEqual({
-      retries: pool.info.maxSize,
+      retries:
+        pool.info.maxSize +
+        Object.keys(pool.workerChoiceStrategyContext.opts.weights).length,
       runTime: { median: false },
       waitTime: { median: false },
-      elu: { median: false }
+      elu: { median: false },
+      weights: expect.objectContaining({
+        0: expect.any(Number),
+        [pool.info.maxSize - 1]: expect.any(Number)
+      })
     })
     for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext
       .workerChoiceStrategies) {
       expect(workerChoiceStrategy.opts).toStrictEqual({
-        retries: pool.info.maxSize,
+        retries:
+          pool.info.maxSize +
+          Object.keys(workerChoiceStrategy.opts.weights).length,
         runTime: { median: false },
         waitTime: { median: false },
-        elu: { median: false }
+        elu: { median: false },
+        weights: expect.objectContaining({
+          0: expect.any(Number),
+          [pool.info.maxSize - 1]: expect.any(Number)
+        })
       })
     }
     expect(
index 200e83c7c83af1eaca13ec86e62efee724402d3b..9ee22fdde9b04ac16bc3a858928cac73ba47867b 100644 (file)
@@ -68,10 +68,16 @@ describe('Selection strategies test suite', () => {
       )
       expect(pool.opts.workerChoiceStrategyOptions).toBeUndefined()
       expect(pool.workerChoiceStrategyContext.opts).toStrictEqual({
-        retries: pool.info.maxSize,
+        retries:
+          pool.info.maxSize +
+          Object.keys(pool.workerChoiceStrategyContext.opts.weights).length,
         runTime: { median: false },
         waitTime: { median: false },
-        elu: { median: false }
+        elu: { median: false },
+        weights: expect.objectContaining({
+          0: expect.any(Number),
+          [pool.info.maxSize - 1]: expect.any(Number)
+        })
       })
       await pool.destroy()
     }
@@ -88,10 +94,16 @@ describe('Selection strategies test suite', () => {
       )
       expect(pool.opts.workerChoiceStrategyOptions).toBeUndefined()
       expect(pool.workerChoiceStrategyContext.opts).toStrictEqual({
-        retries: pool.info.maxSize,
+        retries:
+          pool.info.maxSize +
+          Object.keys(pool.workerChoiceStrategyContext.opts.weights).length,
         runTime: { median: false },
         waitTime: { median: false },
-        elu: { median: false }
+        elu: { median: false },
+        weights: expect.objectContaining({
+          0: expect.any(Number),
+          [pool.info.maxSize - 1]: expect.any(Number)
+        })
       })
       await pool.destroy()
     }
@@ -116,11 +128,6 @@ describe('Selection strategies test suite', () => {
       if (
         workerChoiceStrategy === WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
       ) {
-        expect(
-          pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-            workerChoiceStrategy
-          ).defaultWorkerWeight
-        ).toBeGreaterThan(0)
         expect(
           pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
             workerChoiceStrategy
@@ -130,11 +137,6 @@ describe('Selection strategies test suite', () => {
         workerChoiceStrategy ===
         WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
       ) {
-        expect(
-          pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-            workerChoiceStrategy
-          ).defaultWorkerWeight
-        ).toBeGreaterThan(0)
         expect(
           pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
             workerChoiceStrategy
@@ -153,12 +155,8 @@ describe('Selection strategies test suite', () => {
         expect(
           pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
             workerChoiceStrategy
-          ).roundWeights
-        ).toStrictEqual([
-          pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-            workerChoiceStrategy
-          ).defaultWorkerWeight
-        ])
+          ).roundWeights.length
+        ).toBe(1)
       }
     }
     await pool.destroy()
@@ -1639,11 +1637,6 @@ describe('Selection strategies test suite', () => {
         pool.workerChoiceStrategyContext.workerChoiceStrategy
       ).previousWorkerNodeKey
     ).toBe(0)
-    expect(
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).defaultWorkerWeight
-    ).toBeGreaterThan(0)
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         pool.workerChoiceStrategyContext.workerChoiceStrategy
@@ -1718,11 +1711,6 @@ describe('Selection strategies test suite', () => {
         pool.workerChoiceStrategyContext.workerChoiceStrategy
       ).previousWorkerNodeKey
     ).toBe(0)
-    expect(
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).defaultWorkerWeight
-    ).toBeGreaterThan(0)
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         pool.workerChoiceStrategyContext.workerChoiceStrategy
@@ -1802,11 +1790,6 @@ describe('Selection strategies test suite', () => {
         pool.workerChoiceStrategyContext.workerChoiceStrategy
       ).previousWorkerNodeKey
     ).toBe(0)
-    expect(
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).defaultWorkerWeight
-    ).toBeGreaterThan(0)
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         pool.workerChoiceStrategyContext.workerChoiceStrategy
@@ -1832,11 +1815,6 @@ describe('Selection strategies test suite', () => {
         workerChoiceStrategy
       ).previousWorkerNodeKey
     ).toBeDefined()
-    expect(
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        workerChoiceStrategy
-      ).defaultWorkerWeight
-    ).toBeDefined()
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         workerChoiceStrategy
@@ -1853,11 +1831,6 @@ describe('Selection strategies test suite', () => {
         pool.workerChoiceStrategyContext.workerChoiceStrategy
       ).previousWorkerNodeKey
     ).toBe(0)
-    expect(
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).defaultWorkerWeight
-    ).toBeGreaterThan(0)
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         pool.workerChoiceStrategyContext.workerChoiceStrategy
@@ -1879,11 +1852,6 @@ describe('Selection strategies test suite', () => {
         workerChoiceStrategy
       ).previousWorkerNodeKey
     ).toBeDefined()
-    expect(
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        workerChoiceStrategy
-      ).defaultWorkerWeight
-    ).toBeDefined()
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         workerChoiceStrategy
@@ -1900,11 +1868,6 @@ describe('Selection strategies test suite', () => {
         pool.workerChoiceStrategyContext.workerChoiceStrategy
       ).previousWorkerNodeKey
     ).toBe(0)
-    expect(
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).defaultWorkerWeight
-    ).toBeGreaterThan(0)
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         pool.workerChoiceStrategyContext.workerChoiceStrategy
@@ -2045,11 +2008,6 @@ describe('Selection strategies test suite', () => {
         max * maxMultiplier
       )
     }
-    expect(
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).defaultWorkerWeight
-    ).toBeGreaterThan(0)
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         pool.workerChoiceStrategyContext.workerChoiceStrategy
@@ -2073,12 +2031,8 @@ describe('Selection strategies test suite', () => {
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).roundWeights
-    ).toStrictEqual([
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).defaultWorkerWeight
-    ])
+      ).roundWeights.length
+    ).toBe(1)
     // We need to clean up the resources after our test
     await pool.destroy()
   })
@@ -2131,11 +2085,6 @@ describe('Selection strategies test suite', () => {
         max * maxMultiplier
       )
     }
-    expect(
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).defaultWorkerWeight
-    ).toBeGreaterThan(0)
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         pool.workerChoiceStrategyContext.workerChoiceStrategy
@@ -2159,12 +2108,8 @@ describe('Selection strategies test suite', () => {
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).roundWeights
-    ).toStrictEqual([
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).defaultWorkerWeight
-    ])
+      ).roundWeights.length
+    ).toBe(1)
     // We need to clean up the resources after our test
     await pool.destroy()
   })
@@ -2196,11 +2141,6 @@ describe('Selection strategies test suite', () => {
         workerChoiceStrategy
       ).previousWorkerNodeKey
     ).toBeDefined()
-    expect(
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        workerChoiceStrategy
-      ).defaultWorkerWeight
-    ).toBeDefined()
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         workerChoiceStrategy
@@ -2230,17 +2170,8 @@ describe('Selection strategies test suite', () => {
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).defaultWorkerWeight
-    ).toBeGreaterThan(0)
-    expect(
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).roundWeights
-    ).toStrictEqual([
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).defaultWorkerWeight
-    ])
+      ).roundWeights.length
+    ).toBe(1)
     await pool.destroy()
     pool = new DynamicThreadPool(
       min,
@@ -2267,11 +2198,6 @@ describe('Selection strategies test suite', () => {
         workerChoiceStrategy
       ).previousWorkerNodeKey
     ).toBeDefined()
-    expect(
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        workerChoiceStrategy
-      ).defaultWorkerWeight
-    ).toBeDefined()
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         workerChoiceStrategy
@@ -2301,17 +2227,8 @@ describe('Selection strategies test suite', () => {
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).defaultWorkerWeight
-    ).toBeGreaterThan(0)
-    expect(
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).roundWeights
-    ).toStrictEqual([
-      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-        pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).defaultWorkerWeight
-    ])
+      ).roundWeights.length
+    ).toBe(1)
     // We need to clean up the resources after our test
     await pool.destroy()
   })
index f076f4ddda866dc79735530a8ec4003202dee772..2b8d35d8f485f1ac45ffa6fd203ce33dd6f04073 100644 (file)
@@ -96,7 +96,10 @@ describe('Worker choice strategy context test suite', () => {
     )
     expect(() => workerChoiceStrategyContext.execute()).toThrow(
       new Error(
-        `Worker node key chosen is null or undefined after ${fixedPool.info.maxSize} retries`
+        `Worker node key chosen is null or undefined after ${
+          fixedPool.info.maxSize +
+          Object.keys(workerChoiceStrategyContext.opts.weights).length
+        } retries`
       )
     )
     const workerChoiceStrategyNullStub = createStubInstance(
@@ -112,7 +115,10 @@ describe('Worker choice strategy context test suite', () => {
     )
     expect(() => workerChoiceStrategyContext.execute()).toThrow(
       new Error(
-        `Worker node key chosen is null or undefined after ${fixedPool.info.maxSize} retries`
+        `Worker node key chosen is null or undefined after ${
+          fixedPool.info.maxSize +
+          Object.keys(workerChoiceStrategyContext.opts.weights).length
+        } retries`
       )
     )
   })
index 4b4e1e136119c0249894ba03f31d6245201ca3e0..fd4f776bf2409c8a725e8d3631be9e8981e545c4 100644 (file)
@@ -9,8 +9,8 @@ import {
   EMPTY_FUNCTION,
   availableParallelism,
   average,
+  buildInternalWorkerChoiceStrategyOptions,
   exponentialDelay,
-  getDefaultInternalWorkerChoiceStrategyOptions,
   getWorkerId,
   getWorkerType,
   isAsyncFunction,
@@ -35,18 +35,6 @@ describe('Utils test suite', () => {
     expect(EMPTY_FUNCTION).toStrictEqual(expect.any(Function))
   })
 
-  it('Verify getDefaultInternalWorkerChoiceStrategyOptions() values', () => {
-    const poolMaxSize = 10
-    expect(
-      getDefaultInternalWorkerChoiceStrategyOptions(poolMaxSize)
-    ).toStrictEqual({
-      retries: poolMaxSize,
-      runTime: { median: false },
-      waitTime: { median: false },
-      elu: { median: false }
-    })
-  })
-
   it('Verify DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS values', () => {
     expect(DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS).toStrictEqual({
       aggregate: false,
@@ -243,6 +231,24 @@ describe('Utils test suite', () => {
     expect(max(1, 1)).toBe(1)
   })
 
+  it('Verify buildInternalWorkerChoiceStrategyOptions() behavior', () => {
+    const poolMaxSize = 10
+    const internalWorkerChoiceStrategyOptions =
+      buildInternalWorkerChoiceStrategyOptions(poolMaxSize)
+    expect(internalWorkerChoiceStrategyOptions).toStrictEqual({
+      retries:
+        poolMaxSize +
+        Object.keys(internalWorkerChoiceStrategyOptions.weights).length,
+      runTime: { median: false },
+      waitTime: { median: false },
+      elu: { median: false },
+      weights: expect.objectContaining({
+        0: expect.any(Number),
+        [poolMaxSize - 1]: expect.any(Number)
+      })
+    })
+  })
+
   // it('Verify once()', () => {
   //   let called = 0
   //   const fn = () => ++called