feat: add pool option to enable median runtime
authorJérôme Benoit <jerome.benoit@sap.com>
Sun, 9 Apr 2023 10:53:59 +0000 (12:53 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Sun, 9 Apr 2023 10:53:59 +0000 (12:53 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
.eslintrc.js
README.md
src/pools/abstract-pool.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/weighted-round-robin-worker-choice-strategy.ts
src/pools/selection-strategies/worker-choice-strategy-context.ts
tests/pools/abstract/abstract-pool.test.js

index 70a8ec3c10cec2bea12b3d01707155e585a7b406..ae288a7c0ae81acc761208fa7045df0dce0e5eef 100644 (file)
@@ -49,7 +49,6 @@ module.exports = defineConfig({
           'esm',
           'fibonacci',
           'fs',
-          'hrtime',
           'inheritDoc',
           'jsdoc',
           'microjob',
index 13c8c7fe1f43f4da61a24e5b522d00599c245280..da1690207e21ce6cea04dbe87f3768f3641132d6 100644 (file)
--- a/README.md
+++ b/README.md
@@ -174,6 +174,12 @@ Node versions >= 16.x are supported.
   `WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN` and `WorkerChoiceStrategies.FAIR_SHARE` strategies are targeted to heavy and long tasks  
   Default: `WorkerChoiceStrategies.ROUND_ROBIN`
 
+- `workerChoiceStrategyOptions` (optional) - The worker choice strategy options object to use in this pool.  
+  Properties:
+
+  - `medRunTime` (optional) - Use the tasks median run time instead of the tasks average run time in worker choice strategies.
+    Default: { medRunTime: false }
+
 - `enableEvents` (optional) - Events emission enablement in this pool. Default: true
 - `enableTasksQueue` (optional, experimental) - Tasks queue per worker enablement in this pool. Default: false
 
index 1668813354538bfcf5d8a39c38007c5d0f42755d..93516fee4ea6e10c86a3ced09e4c6f182b65d738 100644 (file)
@@ -93,7 +93,11 @@ export abstract class AbstractPool<
     Worker,
     Data,
     Response
-    >(this, this.opts.workerChoiceStrategy)
+    >(
+      this,
+      this.opts.workerChoiceStrategy,
+      this.opts.workerChoiceStrategyOptions
+    )
   }
 
   private checkFilePath (filePath: string): void {
@@ -127,6 +131,8 @@ export abstract class AbstractPool<
     this.opts.workerChoiceStrategy =
       opts.workerChoiceStrategy ?? WorkerChoiceStrategies.ROUND_ROBIN
     this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy)
+    this.opts.workerChoiceStrategyOptions =
+      opts.workerChoiceStrategyOptions ?? { medRunTime: false }
     this.opts.enableEvents = opts.enableEvents ?? true
     this.opts.enableTasksQueue = opts.enableTasksQueue ?? false
   }
index 9d18ed70c7cd0d8204c510cfb52f5a774fe831be..3c660031fb90b233bb93125331c81e53200b6803 100644 (file)
@@ -5,7 +5,10 @@ import type {
   MessageHandler,
   OnlineHandler
 } from './worker'
-import type { WorkerChoiceStrategy } from './selection-strategies/selection-strategies-types'
+import type {
+  WorkerChoiceStrategy,
+  WorkerChoiceStrategyOptions
+} from './selection-strategies/selection-strategies-types'
 
 /**
  * Pool events emitter.
@@ -49,6 +52,10 @@ export interface PoolOptions<Worker> {
    * The worker choice strategy to use in this pool.
    */
   workerChoiceStrategy?: WorkerChoiceStrategy
+  /**
+   * The worker choice strategy options.
+   */
+  workerChoiceStrategyOptions?: WorkerChoiceStrategyOptions
   /**
    * Pool events emission.
    *
index 337b50b1f07eead51a7e6f1dd962bac4654fcc93..31041f3a8783c605d237196f7e89920d04dd27bb 100644 (file)
@@ -3,7 +3,8 @@ import { PoolType } from '../pool-internal'
 import type { IWorker } from '../worker'
 import type {
   IWorkerChoiceStrategy,
-  RequiredStatistics
+  RequiredStatistics,
+  WorkerChoiceStrategyOptions
 } from './selection-strategies-types'
 
 /**
@@ -21,7 +22,7 @@ export abstract class AbstractWorkerChoiceStrategy<
   /** @inheritDoc */
   protected readonly isDynamicPool: boolean
   /** @inheritDoc */
-  public requiredStatistics: RequiredStatistics = {
+  public readonly requiredStatistics: RequiredStatistics = {
     runTime: false,
     avgRunTime: false,
     medRunTime: false
@@ -31,14 +32,23 @@ export abstract class AbstractWorkerChoiceStrategy<
    * Constructs a worker choice strategy bound to the pool.
    *
    * @param pool - The pool instance.
+   * @param opts - The worker choice strategy options.
    */
   public constructor (
-    protected readonly pool: IPoolInternal<Worker, Data, Response>
+    protected readonly pool: IPoolInternal<Worker, Data, Response>,
+    protected readonly opts: WorkerChoiceStrategyOptions = { medRunTime: false }
   ) {
+    this.checkOptions()
     this.isDynamicPool = this.pool.type === PoolType.DYNAMIC
     this.choose.bind(this)
   }
 
+  private checkOptions (): void {
+    if (this.requiredStatistics.avgRunTime && this.opts.medRunTime === true) {
+      this.requiredStatistics.medRunTime = true
+    }
+  }
+
   /** @inheritDoc */
   public abstract reset (): boolean
 
index c2f85f18ae911f0c8e35bbca6ff11712b360dff8..e1d123a994aa77f5d41ce10ccf2a4f3584b6c926 100644 (file)
@@ -88,11 +88,12 @@ export class FairShareWorkerChoiceStrategy<
       performance.now(),
       this.workerLastVirtualTaskTimestamp.get(workerNodeKey)?.end ?? -Infinity
     )
+    const workerVirtualTaskTRunTime = this.requiredStatistics.medRunTime
+      ? this.pool.workerNodes[workerNodeKey].tasksUsage.medRunTime
+      : this.pool.workerNodes[workerNodeKey].tasksUsage.avgRunTime
     this.workerLastVirtualTaskTimestamp.set(workerNodeKey, {
       start: workerVirtualTaskStartTimestamp,
-      end:
-        workerVirtualTaskStartTimestamp +
-        (this.pool.workerNodes[workerNodeKey].tasksUsage.avgRunTime ?? 0)
+      end: workerVirtualTaskStartTimestamp + (workerVirtualTaskTRunTime ?? 0)
     })
   }
 }
index 1bdc02fb189fcf5f21676af2593f2c24854c99dd..c2782e8b2bf844b1a767eafcfe8554b0fc568ec7 100644 (file)
@@ -4,7 +4,8 @@ import type { IWorker } from '../worker'
 import { AbstractWorkerChoiceStrategy } from './abstract-worker-choice-strategy'
 import type {
   IWorkerChoiceStrategy,
-  RequiredStatistics
+  RequiredStatistics,
+  WorkerChoiceStrategyOptions
 } from './selection-strategies-types'
 
 /**
@@ -57,9 +58,13 @@ export class WeightedRoundRobinWorkerChoiceStrategy<
    * Constructs a worker choice strategy that selects with a weighted round robin scheduling algorithm.
    *
    * @param pool - The pool instance.
+   * @param opts - The worker choice strategy options.
    */
-  public constructor (pool: IPoolInternal<Worker, Data, Response>) {
-    super(pool)
+  public constructor (
+    pool: IPoolInternal<Worker, Data, Response>,
+    opts?: WorkerChoiceStrategyOptions
+  ) {
+    super(pool, opts)
     this.defaultWorkerWeight = this.computeWorkerWeight()
     this.initWorkersTaskRunTime()
   }
@@ -146,7 +151,9 @@ export class WeightedRoundRobinWorkerChoiceStrategy<
   }
 
   private getWorkerVirtualTaskRunTime (workerNodeKey: number): number {
-    return this.pool.workerNodes[workerNodeKey].tasksUsage.avgRunTime
+    return this.requiredStatistics.medRunTime
+      ? this.pool.workerNodes[workerNodeKey].tasksUsage.medRunTime
+      : this.pool.workerNodes[workerNodeKey].tasksUsage.avgRunTime
   }
 
   private computeWorkerWeight (): number {
index 0e5fd5468e32195fce4589562c97f3bc15e8aefd..e763f7b2709c083afc81f7c7e53c074e9c59e7a8 100644 (file)
@@ -7,7 +7,8 @@ import { RoundRobinWorkerChoiceStrategy } from './round-robin-worker-choice-stra
 import type {
   IWorkerChoiceStrategy,
   RequiredStatistics,
-  WorkerChoiceStrategy
+  WorkerChoiceStrategy,
+  WorkerChoiceStrategyOptions
 } from './selection-strategies-types'
 import { WorkerChoiceStrategies } from './selection-strategies-types'
 import { WeightedRoundRobinWorkerChoiceStrategy } from './weighted-round-robin-worker-choice-strategy'
@@ -34,10 +35,12 @@ export class WorkerChoiceStrategyContext<
    *
    * @param pool - The pool instance.
    * @param workerChoiceStrategyType - The worker choice strategy.
+   * @param opts - The worker choice strategy options.
    */
   public constructor (
     pool: IPoolInternal<Worker, Data, Response>,
-    private workerChoiceStrategyType: WorkerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
+    private workerChoiceStrategyType: WorkerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN,
+    opts: WorkerChoiceStrategyOptions = { medRunTime: false }
   ) {
     this.execute.bind(this)
     this.workerChoiceStrategies = new Map<
@@ -46,23 +49,26 @@ export class WorkerChoiceStrategyContext<
     >([
       [
         WorkerChoiceStrategies.ROUND_ROBIN,
-        new RoundRobinWorkerChoiceStrategy<Worker, Data, Response>(pool)
+        new RoundRobinWorkerChoiceStrategy<Worker, Data, Response>(pool, opts)
       ],
       [
         WorkerChoiceStrategies.LESS_USED,
-        new LessUsedWorkerChoiceStrategy<Worker, Data, Response>(pool)
+        new LessUsedWorkerChoiceStrategy<Worker, Data, Response>(pool, opts)
       ],
       [
         WorkerChoiceStrategies.LESS_BUSY,
-        new LessBusyWorkerChoiceStrategy<Worker, Data, Response>(pool)
+        new LessBusyWorkerChoiceStrategy<Worker, Data, Response>(pool, opts)
       ],
       [
         WorkerChoiceStrategies.FAIR_SHARE,
-        new FairShareWorkerChoiceStrategy<Worker, Data, Response>(pool)
+        new FairShareWorkerChoiceStrategy<Worker, Data, Response>(pool, opts)
       ],
       [
         WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN,
-        new WeightedRoundRobinWorkerChoiceStrategy<Worker, Data, Response>(pool)
+        new WeightedRoundRobinWorkerChoiceStrategy<Worker, Data, Response>(
+          pool,
+          opts
+        )
       ]
     ])
   }
index 5f3617db402754d52cdb54815219e3460c2c006d..6118897584d8b61c2693e19d8b8363462f266ef8 100644 (file)
@@ -91,6 +91,9 @@ describe('Abstract pool test suite', () => {
     expect(pool.opts.workerChoiceStrategy).toBe(
       WorkerChoiceStrategies.ROUND_ROBIN
     )
+    expect(pool.opts.workerChoiceStrategyOptions).toStrictEqual({
+      medRunTime: false
+    })
     expect(pool.opts.messageHandler).toBeUndefined()
     expect(pool.opts.errorHandler).toBeUndefined()
     expect(pool.opts.onlineHandler).toBeUndefined()
@@ -102,6 +105,7 @@ describe('Abstract pool test suite', () => {
       './tests/worker-files/thread/testWorker.js',
       {
         workerChoiceStrategy: WorkerChoiceStrategies.LESS_USED,
+        workerChoiceStrategyOptions: { medRunTime: true },
         enableEvents: false,
         enableTasksQueue: true,
         messageHandler: testHandler,
@@ -116,6 +120,9 @@ describe('Abstract pool test suite', () => {
     expect(pool.opts.workerChoiceStrategy).toBe(
       WorkerChoiceStrategies.LESS_USED
     )
+    expect(pool.opts.workerChoiceStrategyOptions).toStrictEqual({
+      medRunTime: true
+    })
     expect(pool.opts.messageHandler).toStrictEqual(testHandler)
     expect(pool.opts.errorHandler).toStrictEqual(testHandler)
     expect(pool.opts.onlineHandler).toStrictEqual(testHandler)