feat: add custom worker weights to worker choice strategies options
authorJérôme Benoit <jerome.benoit@sap.com>
Fri, 5 May 2023 18:41:43 +0000 (20:41 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Fri, 5 May 2023 18:41:43 +0000 (20:41 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
16 files changed:
CHANGELOG.md
src/pools/abstract-pool.ts
src/pools/cluster/dynamic.ts
src/pools/cluster/fixed.ts
src/pools/pool.ts
src/pools/selection-strategies/abstract-worker-choice-strategy.ts
src/pools/selection-strategies/fair-share-worker-choice-strategy.ts
src/pools/selection-strategies/less-busy-worker-choice-strategy.ts
src/pools/selection-strategies/less-used-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/thread/dynamic.ts
src/pools/thread/fixed.ts
tests/pools/selection-strategies/selection-strategies.test.js
tests/pools/selection-strategies/weighted-round-robin-worker-choice-strategy.test.js
tests/pools/selection-strategies/worker-choice-strategy-context.test.js

index b3ebe01c436563bfe2def02082c02ffa48ae9840..c2a0b95f4659dd589b3b3b089ad2c998b8ba00a5 100644 (file)
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added
 
 - Support multiple task functions per worker.
+- Add custom worker weights support to worker choice strategies options.
 
 ### Changed
 
index edf2b29a6e9de328133735cea8a6c58b18609db0..71067f5ce21f7debd3a687d8eb1d97a36a215079 100644 (file)
@@ -180,6 +180,9 @@ export abstract class AbstractPool<
   /** @inheritDoc */
   public abstract get type (): PoolType
 
+  /** @inheritDoc */
+  public abstract get size (): number
+
   /**
    * Number of tasks running in the pool.
    */
index c42e02b0cbfc0fc2ea0b2cd53f200739650f8267..eb2b996571f665ff14154cf338c387c5dfa28266 100644 (file)
@@ -39,6 +39,11 @@ export class DynamicClusterPool<
     return PoolType.DYNAMIC
   }
 
+  /** @inheritDoc */
+  public get size (): number {
+    return this.max
+  }
+
   /** @inheritDoc */
   protected get full (): boolean {
     return this.workerNodes.length === this.max
index ed4f6fed2826078bfdeac466bcec7e8c4c80bef5..10ee92c57876663d07ae96ecdcde036013395126 100644 (file)
@@ -100,6 +100,11 @@ export class FixedClusterPool<
     return PoolType.FIXED
   }
 
+  /** @inheritDoc */
+  public get size (): number {
+    return this.numberOfWorkers
+  }
+
   /** @inheritDoc */
   protected get full (): boolean {
     return this.workerNodes.length === this.numberOfWorkers
index 992aaaf1099c7e341daf3bd1c6a937597018b5ea..49123f5466d7a0b19ad6f166a04b5f8efb088a46 100644 (file)
@@ -127,6 +127,10 @@ export interface IPool<
    * If it is `'dynamic'`, it provides the `max` property.
    */
   readonly type: PoolType
+  /**
+   * Pool maximum size.
+   */
+  readonly size: number
   /**
    * Pool worker nodes.
    */
index 3dda355ef29a043ad3c9c4d34241cebac6e1f28f..2af13d631e2b77708729544787a575896b6c2e2e 100644 (file)
@@ -1,5 +1,5 @@
 import { DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS } from '../../utils'
-import { type IPool, PoolType } from '../pool'
+import type { IPool } from '../pool'
 import type { IWorker } from '../worker'
 import type {
   IWorkerChoiceStrategy,
@@ -24,8 +24,6 @@ export abstract class AbstractWorkerChoiceStrategy<
    */
   private toggleFindLastFreeWorkerNodeKey: boolean = false
   /** @inheritDoc */
-  protected readonly isDynamicPool: boolean
-  /** @inheritDoc */
   public readonly requiredStatistics: RequiredStatistics = {
     runTime: false,
     avgRunTime: false,
@@ -42,7 +40,6 @@ export abstract class AbstractWorkerChoiceStrategy<
     protected readonly pool: IPool<Worker, Data, Response>,
     protected opts: WorkerChoiceStrategyOptions = DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
   ) {
-    this.isDynamicPool = this.pool.type === PoolType.DYNAMIC
     this.choose = this.choose.bind(this)
   }
 
@@ -55,6 +52,14 @@ export abstract class AbstractWorkerChoiceStrategy<
       this.requiredStatistics.avgRunTime = true
       this.requiredStatistics.medRunTime = opts.medRunTime as boolean
     }
+    if (
+      opts.weights != null &&
+      Object.keys(opts.weights).length < this.pool.size
+    ) {
+      throw new Error(
+        'Worker choice strategy options must have a weight for each worker node.'
+      )
+    }
   }
 
   /** @inheritDoc */
index f72ffb566c94345497267e1d9801e36b51f000af..e42d54d9fbabbe847731fd7b0387d9bb8a94f642 100644 (file)
@@ -39,12 +39,9 @@ export class FairShareWorkerChoiceStrategy<
   }
 
   /**
-   * Worker last virtual task execution timestamp.
+   * Workers' virtual task execution timestamp.
    */
-  private readonly workerLastVirtualTaskTimestamp: Map<
-  number,
-  WorkerVirtualTaskTimestamp
-  > = new Map<number, WorkerVirtualTaskTimestamp>()
+  private workersVirtualTaskTimestamp: WorkerVirtualTaskTimestamp[] = []
 
   /** @inheritDoc */
   public constructor (
@@ -57,7 +54,7 @@ export class FairShareWorkerChoiceStrategy<
 
   /** @inheritDoc */
   public reset (): boolean {
-    this.workerLastVirtualTaskTimestamp.clear()
+    this.workersVirtualTaskTimestamp = []
     return true
   }
 
@@ -65,15 +62,15 @@ export class FairShareWorkerChoiceStrategy<
   public choose (): number {
     let minWorkerVirtualTaskEndTimestamp = Infinity
     let chosenWorkerNodeKey!: number
-    for (const [index] of this.pool.workerNodes.entries()) {
-      this.computeWorkerLastVirtualTaskTimestamp(index)
+    for (const [workerNodeKey] of this.pool.workerNodes.entries()) {
+      this.computeWorkerVirtualTaskTimestamp(workerNodeKey)
       const workerLastVirtualTaskEndTimestamp =
-        this.workerLastVirtualTaskTimestamp.get(index)?.end ?? 0
+        this.workersVirtualTaskTimestamp[workerNodeKey]?.end ?? 0
       if (
         workerLastVirtualTaskEndTimestamp < minWorkerVirtualTaskEndTimestamp
       ) {
         minWorkerVirtualTaskEndTimestamp = workerLastVirtualTaskEndTimestamp
-        chosenWorkerNodeKey = index
+        chosenWorkerNodeKey = workerNodeKey
       }
     }
     return chosenWorkerNodeKey
@@ -81,31 +78,26 @@ export class FairShareWorkerChoiceStrategy<
 
   /** @inheritDoc */
   public remove (workerNodeKey: number): boolean {
-    const deleted = this.workerLastVirtualTaskTimestamp.delete(workerNodeKey)
-    for (const [key, value] of this.workerLastVirtualTaskTimestamp) {
-      if (key > workerNodeKey) {
-        this.workerLastVirtualTaskTimestamp.set(key - 1, value)
-      }
-    }
-    return deleted
+    this.workersVirtualTaskTimestamp.splice(workerNodeKey, 1)
+    return true
   }
 
   /**
-   * Computes worker last virtual task timestamp.
+   * Computes worker virtual task timestamp.
    *
    * @param workerNodeKey - The worker node key.
    */
-  private computeWorkerLastVirtualTaskTimestamp (workerNodeKey: number): void {
+  private computeWorkerVirtualTaskTimestamp (workerNodeKey: number): void {
     const workerVirtualTaskStartTimestamp = Math.max(
       performance.now(),
-      this.workerLastVirtualTaskTimestamp.get(workerNodeKey)?.end ?? -Infinity
+      this.workersVirtualTaskTimestamp[workerNodeKey]?.end ?? -Infinity
     )
     const workerVirtualTaskTRunTime = this.requiredStatistics.medRunTime
       ? this.pool.workerNodes[workerNodeKey].tasksUsage.medRunTime
       : this.pool.workerNodes[workerNodeKey].tasksUsage.avgRunTime
-    this.workerLastVirtualTaskTimestamp.set(workerNodeKey, {
+    this.workersVirtualTaskTimestamp[workerNodeKey] = {
       start: workerVirtualTaskStartTimestamp,
       end: workerVirtualTaskStartTimestamp + (workerVirtualTaskTRunTime ?? 0)
-    })
+    }
   }
 }
index 10fc3bf761f4908d8b1dd402f2ee2161310665f7..79e65c592fd09f3d1e4d506f78463978441ff8ca 100644 (file)
@@ -51,13 +51,13 @@ export class LessBusyWorkerChoiceStrategy<
     }
     let minRunTime = Infinity
     let lessBusyWorkerNodeKey!: number
-    for (const [index, workerNode] of this.pool.workerNodes.entries()) {
+    for (const [workerNodeKey, workerNode] of this.pool.workerNodes.entries()) {
       const workerRunTime = workerNode.tasksUsage.runTime
       if (workerRunTime === 0) {
-        return index
+        return workerNodeKey
       } else if (workerRunTime < minRunTime) {
         minRunTime = workerRunTime
-        lessBusyWorkerNodeKey = index
+        lessBusyWorkerNodeKey = workerNodeKey
       }
     }
     return lessBusyWorkerNodeKey
index e3c395f477e4adb1d8a042ca71317c84111aae60..1503e057ef42fc3890eb0e8cfbc33496a1dd9f5e 100644 (file)
@@ -43,14 +43,14 @@ export class LessUsedWorkerChoiceStrategy<
     }
     let minNumberOfTasks = Infinity
     let lessUsedWorkerNodeKey!: number
-    for (const [index, workerNode] of this.pool.workerNodes.entries()) {
+    for (const [workerNodeKey, workerNode] of this.pool.workerNodes.entries()) {
       const tasksUsage = workerNode.tasksUsage
       const workerTasks = tasksUsage.run + tasksUsage.running
       if (workerTasks === 0) {
-        return index
+        return workerNodeKey
       } else if (workerTasks < minNumberOfTasks) {
         minNumberOfTasks = workerTasks
-        lessUsedWorkerNodeKey = index
+        lessUsedWorkerNodeKey = workerNodeKey
       }
     }
     return lessUsedWorkerNodeKey
index fa0d0f641a4b8618efa7346a3ba4029a93bc533b..0e549cec338b685d36e80ab1ff3d147ef6a3f83f 100644 (file)
@@ -39,6 +39,13 @@ export interface WorkerChoiceStrategyOptions {
    * @defaultValue false
    */
   medRunTime?: boolean
+  /**
+   * Worker weights to use for weighted round robin worker selection strategy.
+   * Weight is the tasks maximum average or median runtime in milliseconds.
+   *
+   * @defaultValue Computed worker weights automatically given the CPU performance.
+   */
+  weights?: Record<number, number>
 }
 
 /**
index 53b2d64d923412e73ae11f9b8cbc23f892acdf4a..b0750949749de7f2d22ec0c97733b2f3ea5ec8b9 100644 (file)
@@ -9,14 +9,6 @@ import type {
   WorkerChoiceStrategyOptions
 } from './selection-strategies-types'
 
-/**
- * Virtual task runtime.
- */
-interface TaskRunTime {
-  weight: number
-  runTime: number
-}
-
 /**
  * Selects the next worker with a weighted round robin scheduling algorithm.
  * Loosely modeled after the weighted round robin queueing algorithm: https://en.wikipedia.org/wiki/Weighted_round_robin.
@@ -48,12 +40,9 @@ export class WeightedRoundRobinWorkerChoiceStrategy<
    */
   private readonly defaultWorkerWeight: number
   /**
-   * Workers' virtual task runtime.
+   * Worker virtual task runtime.
    */
-  private readonly workersTaskRunTime: Map<number, TaskRunTime> = new Map<
-  number,
-  TaskRunTime
-  >()
+  private workerVirtualTaskRunTime: number = 0
 
   /** @inheritDoc */
   public constructor (
@@ -62,45 +51,32 @@ export class WeightedRoundRobinWorkerChoiceStrategy<
   ) {
     super(pool, opts)
     this.checkOptions(this.opts)
-    this.defaultWorkerWeight = this.computeWorkerWeight()
-    this.initWorkersTaskRunTime()
+    this.defaultWorkerWeight = this.computeDefaultWorkerWeight()
   }
 
   /** @inheritDoc */
   public reset (): boolean {
     this.currentWorkerNodeId = 0
-    this.workersTaskRunTime.clear()
-    this.initWorkersTaskRunTime()
+    this.workerVirtualTaskRunTime = 0
     return true
   }
 
   /** @inheritDoc */
   public choose (): number {
     const chosenWorkerNodeKey = this.currentWorkerNodeId
-    if (
-      this.isDynamicPool &&
-      !this.workersTaskRunTime.has(chosenWorkerNodeKey)
-    ) {
-      this.initWorkerTaskRunTime(chosenWorkerNodeKey)
-    }
-    const workerTaskRunTime =
-      this.workersTaskRunTime.get(chosenWorkerNodeKey)?.runTime ?? 0
+    const workerTaskRunTime = this.workerVirtualTaskRunTime ?? 0
     const workerTaskWeight =
-      this.workersTaskRunTime.get(chosenWorkerNodeKey)?.weight ??
-      this.defaultWorkerWeight
+      this.opts.weights?.[chosenWorkerNodeKey] ?? this.defaultWorkerWeight
     if (workerTaskRunTime < workerTaskWeight) {
-      this.setWorkerTaskRunTime(
-        chosenWorkerNodeKey,
-        workerTaskWeight,
+      this.workerVirtualTaskRunTime =
         workerTaskRunTime +
-          (this.getWorkerVirtualTaskRunTime(chosenWorkerNodeKey) ?? 0)
-      )
+        (this.getWorkerVirtualTaskRunTime(chosenWorkerNodeKey) ?? 0)
     } else {
       this.currentWorkerNodeId =
         this.currentWorkerNodeId === this.pool.workerNodes.length - 1
           ? 0
           : this.currentWorkerNodeId + 1
-      this.setWorkerTaskRunTime(this.currentWorkerNodeId, workerTaskWeight, 0)
+      this.workerVirtualTaskRunTime = 0
     }
     return chosenWorkerNodeKey
   }
@@ -116,35 +92,9 @@ export class WeightedRoundRobinWorkerChoiceStrategy<
             ? this.pool.workerNodes.length - 1
             : this.currentWorkerNodeId
       }
+      this.workerVirtualTaskRunTime = 0
     }
-    const deleted = this.workersTaskRunTime.delete(workerNodeKey)
-    for (const [key, value] of this.workersTaskRunTime) {
-      if (key > workerNodeKey) {
-        this.workersTaskRunTime.set(key - 1, value)
-      }
-    }
-    return deleted
-  }
-
-  private initWorkersTaskRunTime (): void {
-    for (const [index] of this.pool.workerNodes.entries()) {
-      this.initWorkerTaskRunTime(index)
-    }
-  }
-
-  private initWorkerTaskRunTime (workerNodeKey: number): void {
-    this.setWorkerTaskRunTime(workerNodeKey, this.defaultWorkerWeight, 0)
-  }
-
-  private setWorkerTaskRunTime (
-    workerNodeKey: number,
-    weight: number,
-    runTime: number
-  ): void {
-    this.workersTaskRunTime.set(workerNodeKey, {
-      weight,
-      runTime
-    })
+    return true
   }
 
   private getWorkerVirtualTaskRunTime (workerNodeKey: number): number {
@@ -153,7 +103,7 @@ export class WeightedRoundRobinWorkerChoiceStrategy<
       : this.pool.workerNodes[workerNodeKey].tasksUsage.avgRunTime
   }
 
-  private computeWorkerWeight (): number {
+  private computeDefaultWorkerWeight (): number {
     let cpusCycleTimeWeight = 0
     for (const cpu of cpus()) {
       // CPU estimated cycle time
index bb177f876c79893b582989d67067b4ec448b2549..56b923c4d2cd0f4c3800aba0b24abd41f83b4799 100644 (file)
@@ -45,6 +45,11 @@ export class DynamicThreadPool<
     return this.workerNodes.length === this.max
   }
 
+  /** @inheritDoc */
+  public get size (): number {
+    return this.max
+  }
+
   /** @inheritDoc */
   protected get busy (): boolean {
     return this.full && this.internalBusy()
index 570c9c8b7f3ed718aaf2309b41e8b8486477d423..41fcee2b60c2c5a36b5f24bc35d1cdce4a03c326 100644 (file)
@@ -96,6 +96,11 @@ export class FixedThreadPool<
     return PoolType.FIXED
   }
 
+  /** @inheritDoc */
+  public get size (): number {
+    return this.numberOfWorkers
+  }
+
   /** @inheritDoc */
   protected get full (): boolean {
     return this.workerNodes.length === this.numberOfWorkers
index f5d41cc89927d1e7468273daa2fa4e80c87c8b73..af63b3e976113702b98ec5947c54fc4ba03e44fe 100644 (file)
@@ -77,20 +77,16 @@ describe('Selection strategies test suite', () => {
           ).nextWorkerNodeId
         ).toBe(0)
       } else if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) {
-        for (const workerNodeKey of pool.workerChoiceStrategyContext.workerChoiceStrategies
-          .get(workerChoiceStrategy)
-          .workerLastVirtualTaskTimestamp.keys()) {
-          expect(
-            pool.workerChoiceStrategyContext.workerChoiceStrategies
-              .get(workerChoiceStrategy)
-              .workerLastVirtualTaskTimestamp.get(workerNodeKey).start
-          ).toBe(0)
-          expect(
-            pool.workerChoiceStrategyContext.workerChoiceStrategies
-              .get(workerChoiceStrategy)
-              .workerLastVirtualTaskTimestamp.get(workerNodeKey).end
-          ).toBe(0)
-        }
+        expect(
+          pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+            workerChoiceStrategy
+          ).workersVirtualTaskTimestamp
+        ).toBeInstanceOf(Array)
+        expect(
+          pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+            workerChoiceStrategy
+          ).workersVirtualTaskTimestamp.length
+        ).toBe(0)
       } else if (
         workerChoiceStrategy === WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
       ) {
@@ -104,20 +100,11 @@ describe('Selection strategies test suite', () => {
             workerChoiceStrategy
           ).defaultWorkerWeight
         ).toBeGreaterThan(0)
-        for (const workerNodeKey of pool.workerChoiceStrategyContext.workerChoiceStrategies
-          .get(workerChoiceStrategy)
-          .workersTaskRunTime.keys()) {
-          expect(
-            pool.workerChoiceStrategyContext.workerChoiceStrategies
-              .get(workerChoiceStrategy)
-              .workersTaskRunTime.get(workerNodeKey).weight
-          ).toBeGreaterThan(0)
-          expect(
-            pool.workerChoiceStrategyContext.workerChoiceStrategies
-              .get(workerChoiceStrategy)
-              .workersTaskRunTime.get(workerNodeKey).runTime
-          ).toBe(0)
-        }
+        expect(
+          pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+            workerChoiceStrategy
+          ).workerVirtualTaskRunTime
+        ).toBe(0)
       }
     }
     await pool.destroy()
@@ -454,7 +441,7 @@ describe('Selection strategies test suite', () => {
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).workerLastVirtualTaskTimestamp.size
+      ).workersVirtualTaskTimestamp.length
     ).toBe(pool.workerNodes.length)
     // We need to clean up the resources after our test
     await pool.destroy()
@@ -474,13 +461,13 @@ describe('Selection strategies test suite', () => {
       promises.push(pool.execute())
     }
     await Promise.all(promises)
-    // if (process.platform !== 'win32') {
-    //   expect(
-    //     pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-    //       pool.workerChoiceStrategyContext.workerChoiceStrategy
-    //     ).workerLastVirtualTaskTimestamp.size
-    //   ).toBe(pool.workerNodes.length)
-    // }
+    if (process.platform !== 'win32') {
+      expect(
+        pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+          pool.workerChoiceStrategyContext.workerChoiceStrategy
+        ).workersVirtualTaskTimestamp.length
+      ).toBe(pool.workerNodes.length)
+    }
     // We need to clean up the resources after our test
     await pool.destroy()
   })
@@ -510,13 +497,13 @@ describe('Selection strategies test suite', () => {
       expect(workerNode.tasksUsage.medRunTime).toBeDefined()
       expect(workerNode.tasksUsage.medRunTime).toBeGreaterThan(0)
     }
-    // if (process.platform !== 'win32') {
-    //   expect(
-    //     pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-    //       pool.workerChoiceStrategyContext.workerChoiceStrategy
-    //     ).workerLastVirtualTaskTimestamp.size
-    //   ).toBe(pool.workerNodes.length)
-    // }
+    if (process.platform !== 'win32') {
+      expect(
+        pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+          pool.workerChoiceStrategyContext.workerChoiceStrategy
+        ).workersVirtualTaskTimestamp.length
+      ).toBe(pool.workerNodes.length)
+    }
     // We need to clean up the resources after our test
     await pool.destroy()
   })
@@ -530,23 +517,27 @@ describe('Selection strategies test suite', () => {
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         workerChoiceStrategy
-      ).workerLastVirtualTaskTimestamp
-    ).toBeDefined()
+      ).workersVirtualTaskTimestamp
+    ).toBeInstanceOf(Array)
+    expect(
+      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+        workerChoiceStrategy
+      ).workersVirtualTaskTimestamp.length
+    ).toBe(0)
     pool.setWorkerChoiceStrategy(workerChoiceStrategy)
-    for (const workerNodeKey of pool.workerChoiceStrategyContext.workerChoiceStrategies
-      .get(pool.workerChoiceStrategyContext.workerChoiceStrategy)
-      .workerLastVirtualTaskTimestamp.keys()) {
-      expect(
-        pool.workerChoiceStrategyContext.workerChoiceStrategies
-          .get(pool.workerChoiceStrategyContext.workerChoiceStrategy)
-          .workerLastVirtualTaskTimestamp.get(workerNodeKey).start
-      ).toBe(0)
-      expect(
-        pool.workerChoiceStrategyContext.workerChoiceStrategies
-          .get(pool.workerChoiceStrategyContext.workerChoiceStrategy)
-          .workerLastVirtualTaskTimestamp.get(workerNodeKey).end
-      ).toBe(0)
-    }
+    expect(
+      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+        workerChoiceStrategy
+      ).workersVirtualTaskTimestamp
+    ).toBeInstanceOf(Array)
+    pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+      workerChoiceStrategy
+    ).workersVirtualTaskTimestamp[0] = 0
+    expect(
+      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+        workerChoiceStrategy
+      ).workersVirtualTaskTimestamp.length
+    ).toBe(1)
     await pool.destroy()
     pool = new DynamicThreadPool(
       min,
@@ -556,23 +547,27 @@ describe('Selection strategies test suite', () => {
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         workerChoiceStrategy
-      ).workerLastVirtualTaskTimestamp
-    ).toBeDefined()
+      ).workersVirtualTaskTimestamp
+    ).toBeInstanceOf(Array)
+    pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+      workerChoiceStrategy
+    ).workersVirtualTaskTimestamp[0] = 0
+    expect(
+      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+        workerChoiceStrategy
+      ).workersVirtualTaskTimestamp.length
+    ).toBe(1)
     pool.setWorkerChoiceStrategy(workerChoiceStrategy)
-    for (const workerNodeKey of pool.workerChoiceStrategyContext.workerChoiceStrategies
-      .get(pool.workerChoiceStrategyContext.workerChoiceStrategy)
-      .workerLastVirtualTaskTimestamp.keys()) {
-      expect(
-        pool.workerChoiceStrategyContext.workerChoiceStrategies
-          .get(pool.workerChoiceStrategyContext.workerChoiceStrategy)
-          .workerLastVirtualTaskTimestamp.get(workerNodeKey).start
-      ).toBe(0)
-      expect(
-        pool.workerChoiceStrategyContext.workerChoiceStrategies
-          .get(pool.workerChoiceStrategyContext.workerChoiceStrategy)
-          .workerLastVirtualTaskTimestamp.get(workerNodeKey).end
-      ).toBe(0)
-    }
+    expect(
+      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+        workerChoiceStrategy
+      ).workersVirtualTaskTimestamp
+    ).toBeInstanceOf(Array)
+    expect(
+      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+        workerChoiceStrategy
+      ).workersVirtualTaskTimestamp.length
+    ).toBe(0)
     // We need to clean up the resources after our test
     await pool.destroy()
   })
@@ -629,8 +624,13 @@ describe('Selection strategies test suite', () => {
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         pool.workerChoiceStrategyContext.workerChoiceStrategy
-      ).workersTaskRunTime.size
-    ).toBe(pool.workerNodes.length)
+      ).defaultWorkerWeight
+    ).toBeGreaterThan(0)
+    expect(
+      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+        pool.workerChoiceStrategyContext.workerChoiceStrategy
+      ).workerVirtualTaskRunTime
+    ).toBeGreaterThanOrEqual(0)
     // We need to clean up the resources after our test
     await pool.destroy()
   })
@@ -656,8 +656,13 @@ describe('Selection strategies test suite', () => {
       expect(
         pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
           pool.workerChoiceStrategyContext.workerChoiceStrategy
-        ).workersTaskRunTime.size
-      ).toBe(pool.workerNodes.length)
+        ).defaultWorkerWeight
+      ).toBeGreaterThan(0)
+      expect(
+        pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+          pool.workerChoiceStrategyContext.workerChoiceStrategy
+        ).workerVirtualTaskRunTime
+      ).toBeGreaterThanOrEqual(0)
     }
     // We need to clean up the resources after our test
     await pool.destroy()
@@ -688,13 +693,16 @@ describe('Selection strategies test suite', () => {
       expect(workerNode.tasksUsage.medRunTime).toBeDefined()
       expect(workerNode.tasksUsage.medRunTime).toBeGreaterThan(0)
     }
-    // if (process.platform !== 'win32') {
-    //   expect(
-    //     pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
-    //       pool.workerChoiceStrategyContext.workerChoiceStrategy
-    //     ).workersTaskRunTime.size
-    //   ).toBe(pool.workerNodes.length)
-    // }
+    expect(
+      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+        pool.workerChoiceStrategyContext.workerChoiceStrategy
+      ).defaultWorkerWeight
+    ).toBeGreaterThan(0)
+    expect(
+      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+        pool.workerChoiceStrategyContext.workerChoiceStrategy
+      ).workerVirtualTaskRunTime
+    ).toBeGreaterThanOrEqual(0)
     // We need to clean up the resources after our test
     await pool.destroy()
   })
@@ -718,7 +726,7 @@ describe('Selection strategies test suite', () => {
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         workerChoiceStrategy
-      ).workersTaskRunTime
+      ).workerVirtualTaskRunTime
     ).toBeDefined()
     pool.setWorkerChoiceStrategy(workerChoiceStrategy)
     expect(
@@ -731,15 +739,11 @@ describe('Selection strategies test suite', () => {
         pool.workerChoiceStrategyContext.workerChoiceStrategy
       ).defaultWorkerWeight
     ).toBeGreaterThan(0)
-    for (const workerNodeKey of pool.workerChoiceStrategyContext.workerChoiceStrategies
-      .get(pool.workerChoiceStrategyContext.workerChoiceStrategy)
-      .workersTaskRunTime.keys()) {
-      expect(
-        pool.workerChoiceStrategyContext.workerChoiceStrategies
-          .get(pool.workerChoiceStrategyContext.workerChoiceStrategy)
-          .workersTaskRunTime.get(workerNodeKey).runTime
-      ).toBe(0)
-    }
+    expect(
+      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+        workerChoiceStrategy
+      ).workerVirtualTaskRunTime
+    ).toBe(0)
     await pool.destroy()
     pool = new DynamicThreadPool(
       min,
@@ -759,7 +763,7 @@ describe('Selection strategies test suite', () => {
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
         workerChoiceStrategy
-      ).workersTaskRunTime
+      ).workerVirtualTaskRunTime
     ).toBeDefined()
     pool.setWorkerChoiceStrategy(workerChoiceStrategy)
     expect(
@@ -772,15 +776,11 @@ describe('Selection strategies test suite', () => {
         pool.workerChoiceStrategyContext.workerChoiceStrategy
       ).defaultWorkerWeight
     ).toBeGreaterThan(0)
-    for (const workerNodeKey of pool.workerChoiceStrategyContext.workerChoiceStrategies
-      .get(pool.workerChoiceStrategyContext.workerChoiceStrategy)
-      .workersTaskRunTime.keys()) {
-      expect(
-        pool.workerChoiceStrategyContext.workerChoiceStrategies
-          .get(pool.workerChoiceStrategyContext.workerChoiceStrategy)
-          .workersTaskRunTime.get(workerNodeKey).runTime
-      ).toBe(0)
-    }
+    expect(
+      pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+        workerChoiceStrategy
+      ).workerVirtualTaskRunTime
+    ).toBe(0)
     // We need to clean up the resources after our test
     await pool.destroy()
   })
index 8e59d81e192d89f5d51dfbab18fdc62be3b5827c..3f94411c98c104b5ee82697e134c31f557886ccf 100644 (file)
@@ -25,17 +25,17 @@ describe('Weighted round robin strategy worker choice strategy test suite', () =
 
   it('Verify that reset() resets internals', () => {
     const strategy = new WeightedRoundRobinWorkerChoiceStrategy(pool)
-    strategy.currentWorkerId = TestUtils.generateRandomInteger()
-    const workersTaskRunTimeClearStub = sinon
-      .stub(strategy.workersTaskRunTime, 'clear')
-      .returns()
-    const initWorkersTaskRunTimeStub = sinon
-      .stub(strategy, 'initWorkersTaskRunTime')
-      .returns()
+    strategy.currentWorkerId = TestUtils.generateRandomInteger(
+      Number.MAX_SAFE_INTEGER,
+      1
+    )
+    strategy.workerVirtualTaskRunTime = TestUtils.generateRandomInteger(
+      Number.MAX_SAFE_INTEGER,
+      1
+    )
     const resetResult = strategy.reset()
     expect(resetResult).toBe(true)
     expect(strategy.currentWorkerNodeId).toBe(0)
-    expect(workersTaskRunTimeClearStub.calledOnce).toBe(true)
-    expect(initWorkersTaskRunTimeStub.calledOnce).toBe(true)
+    expect(strategy.workerVirtualTaskRunTime).toBe(0)
   })
 })
index 79bcf2a165eabfcad1c224ff83824336a0a95fec..060ebb63f352594c706ffac7f51e0f63c56a44f0 100644 (file)
@@ -116,11 +116,6 @@ describe('Worker choice strategy context test suite', () => {
     const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
       fixedPool
     )
-    expect(
-      workerChoiceStrategyContext.workerChoiceStrategies.get(
-        workerChoiceStrategy
-      ).isDynamicPool
-    ).toBe(false)
     expect(
       workerChoiceStrategyContext.workerChoiceStrategies.get(
         workerChoiceStrategy
@@ -145,11 +140,6 @@ describe('Worker choice strategy context test suite', () => {
     const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
       dynamicPool
     )
-    expect(
-      workerChoiceStrategyContext.workerChoiceStrategies.get(
-        workerChoiceStrategy
-      ).isDynamicPool
-    ).toBe(true)
     expect(
       workerChoiceStrategyContext.workerChoiceStrategies.get(
         workerChoiceStrategy
@@ -174,11 +164,6 @@ describe('Worker choice strategy context test suite', () => {
     const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
       fixedPool
     )
-    expect(
-      workerChoiceStrategyContext.workerChoiceStrategies.get(
-        workerChoiceStrategy
-      ).isDynamicPool
-    ).toBe(false)
     workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
     expect(
       workerChoiceStrategyContext.workerChoiceStrategies.get(
@@ -195,11 +180,6 @@ describe('Worker choice strategy context test suite', () => {
     const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
       dynamicPool
     )
-    expect(
-      workerChoiceStrategyContext.workerChoiceStrategies.get(
-        workerChoiceStrategy
-      ).isDynamicPool
-    ).toBe(true)
     workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
     expect(
       workerChoiceStrategyContext.workerChoiceStrategies.get(
@@ -216,11 +196,6 @@ describe('Worker choice strategy context test suite', () => {
     const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
       fixedPool
     )
-    expect(
-      workerChoiceStrategyContext.workerChoiceStrategies.get(
-        workerChoiceStrategy
-      ).isDynamicPool
-    ).toBe(false)
     workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
     expect(
       workerChoiceStrategyContext.workerChoiceStrategies.get(
@@ -237,11 +212,6 @@ describe('Worker choice strategy context test suite', () => {
     const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
       dynamicPool
     )
-    expect(
-      workerChoiceStrategyContext.workerChoiceStrategies.get(
-        workerChoiceStrategy
-      ).isDynamicPool
-    ).toBe(true)
     workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
     expect(
       workerChoiceStrategyContext.workerChoiceStrategies.get(
@@ -258,11 +228,6 @@ describe('Worker choice strategy context test suite', () => {
     const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
       fixedPool
     )
-    expect(
-      workerChoiceStrategyContext.workerChoiceStrategies.get(
-        workerChoiceStrategy
-      ).isDynamicPool
-    ).toBe(false)
     workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
     expect(
       workerChoiceStrategyContext.workerChoiceStrategies.get(
@@ -279,11 +244,6 @@ describe('Worker choice strategy context test suite', () => {
     const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
       dynamicPool
     )
-    expect(
-      workerChoiceStrategyContext.workerChoiceStrategies.get(
-        workerChoiceStrategy
-      ).isDynamicPool
-    ).toBe(true)
     workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
     expect(
       workerChoiceStrategyContext.workerChoiceStrategies.get(
@@ -300,11 +260,6 @@ describe('Worker choice strategy context test suite', () => {
     const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
       fixedPool
     )
-    expect(
-      workerChoiceStrategyContext.workerChoiceStrategies.get(
-        workerChoiceStrategy
-      ).isDynamicPool
-    ).toBe(false)
     workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
     expect(
       workerChoiceStrategyContext.workerChoiceStrategies.get(
@@ -321,11 +276,6 @@ describe('Worker choice strategy context test suite', () => {
     const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
       dynamicPool
     )
-    expect(
-      workerChoiceStrategyContext.workerChoiceStrategies.get(
-        workerChoiceStrategy
-      ).isDynamicPool
-    ).toBe(true)
     workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
     expect(
       workerChoiceStrategyContext.workerChoiceStrategies.get(