fix: refine possible null exception fix at task response handling
[poolifier.git] / src / pools / selection-strategies / worker-choice-strategy-context.ts
index 122774dd0e48c157d5d7c7ce5bdc4ea590281087..157a61223a251aa1305be38555e67511b37798d6 100644 (file)
@@ -44,8 +44,9 @@ export class WorkerChoiceStrategyContext<
   public constructor (
     pool: IPool<Worker, Data, Response>,
     private workerChoiceStrategy: WorkerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN,
-    opts: WorkerChoiceStrategyOptions = DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
+    private opts: WorkerChoiceStrategyOptions = DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
   ) {
+    this.opts = { ...DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS, ...opts }
     this.execute = this.execute.bind(this)
     this.workerChoiceStrategies = new Map<
     WorkerChoiceStrategy,
@@ -119,7 +120,7 @@ export class WorkerChoiceStrategyContext<
   }
 
   /**
-   * Gets the worker choice strategy task statistics requirements in the context.
+   * Gets the worker choice strategy in the context task statistics requirements.
    *
    * @returns The task statistics requirements.
    */
@@ -146,7 +147,7 @@ export class WorkerChoiceStrategyContext<
   }
 
   /**
-   * Updates the worker node key in the worker choice strategy internals in the context.
+   * Updates the worker node key in the worker choice strategy in the context internals.
    *
    * @returns `true` if the update is successful, `false` otherwise.
    */
@@ -159,19 +160,44 @@ export class WorkerChoiceStrategyContext<
   }
 
   /**
-   * Executes the worker choice strategy algorithm in the context.
+   * Executes the worker choice strategy in the context algorithm.
    *
    * @returns The key of the worker node.
-   * @throws {@link https://nodejs.org/api/errors.html#class-error} If the worker node key is null or undefined.
+   * @throws {@link https://nodejs.org/api/errors.html#class-error} If after configured retries the worker node key is null or undefined.
+   * @throws {@link https://nodejs.org/api/errors.html#class-rangeerror} If the maximum consecutive worker choice strategy executions has been reached.
    */
   public execute (): number {
-    const workerNodeKey = (
-      this.workerChoiceStrategies.get(
-        this.workerChoiceStrategy
-      ) as IWorkerChoiceStrategy
-    ).choose()
+    const workerChoiceStrategy = this.workerChoiceStrategies.get(
+      this.workerChoiceStrategy
+    ) as IWorkerChoiceStrategy
+    let workerNodeKey: number | undefined
+    const maxExecutionCount = 10000
+    let executionCount = 0
+    let chooseCount = 0
+    let retriesCount = 0
+    do {
+      if (workerChoiceStrategy.hasPoolWorkerNodesReady()) {
+        workerNodeKey = workerChoiceStrategy.choose()
+        if (chooseCount > 0) {
+          retriesCount++
+        }
+        chooseCount++
+      }
+      executionCount++
+    } while (
+      executionCount < maxExecutionCount &&
+      (!workerChoiceStrategy.hasPoolWorkerNodesReady() ||
+        (workerNodeKey == null && retriesCount < (this.opts.retries as number)))
+    )
+    if (executionCount >= maxExecutionCount) {
+      throw new RangeError(
+        `Worker choice strategy consecutive executions has exceeded the maximum of ${maxExecutionCount}`
+      )
+    }
     if (workerNodeKey == null) {
-      throw new TypeError('Worker node key chosen is null or undefined')
+      throw new Error(
+        `Worker node key chosen is null or undefined after ${retriesCount} retries`
+      )
     }
     return workerNodeKey
   }
@@ -196,6 +222,7 @@ export class WorkerChoiceStrategyContext<
    * @param opts - The worker choice strategy options.
    */
   public setOptions (opts: WorkerChoiceStrategyOptions): void {
+    this.opts = { ...DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS, ...opts }
     for (const workerChoiceStrategy of this.workerChoiceStrategies.values()) {
       workerChoiceStrategy.setOptions(opts)
     }