fix: fix worker choice strategy retries mechanism on some edge cases
[poolifier.git] / src / pools / selection-strategies / least-elu-worker-choice-strategy.ts
1 import {
2 DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
3 DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
4 } from '../../utils'
5 import type { IPool } from '../pool'
6 import type { IWorker } from '../worker'
7 import { AbstractWorkerChoiceStrategy } from './abstract-worker-choice-strategy'
8 import type {
9 IWorkerChoiceStrategy,
10 StrategyPolicy,
11 TaskStatisticsRequirements,
12 WorkerChoiceStrategyOptions
13 } from './selection-strategies-types'
14
15 /**
16 * Selects the worker with the least ELU.
17 *
18 * @typeParam Worker - Type of worker which manages the strategy.
19 * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
20 * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
21 */
22 export class LeastEluWorkerChoiceStrategy<
23 Worker extends IWorker,
24 Data = unknown,
25 Response = unknown
26 >
27 extends AbstractWorkerChoiceStrategy<Worker, Data, Response>
28 implements IWorkerChoiceStrategy {
29 /** @inheritDoc */
30 public readonly strategyPolicy: StrategyPolicy = {
31 dynamicWorkerUsage: false,
32 dynamicWorkerReady: true
33 }
34
35 /** @inheritDoc */
36 public readonly taskStatisticsRequirements: TaskStatisticsRequirements = {
37 runTime: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
38 waitTime: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
39 elu: {
40 aggregate: true,
41 average: false,
42 median: false
43 }
44 }
45
46 /** @inheritDoc */
47 public constructor (
48 pool: IPool<Worker, Data, Response>,
49 opts: WorkerChoiceStrategyOptions = DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
50 ) {
51 super(pool, opts)
52 this.setTaskStatisticsRequirements(this.opts)
53 }
54
55 /** @inheritDoc */
56 public reset (): boolean {
57 return true
58 }
59
60 /** @inheritDoc */
61 public update (): boolean {
62 return true
63 }
64
65 /** @inheritDoc */
66 public choose (): number | undefined {
67 const chosenWorkerNodeKey = this.leastEluNextWorkerNodeKey()
68 this.assignChosenWorkerNodeKey(chosenWorkerNodeKey)
69 return this.nextWorkerNodeKey
70 }
71
72 /** @inheritDoc */
73 public remove (): boolean {
74 return true
75 }
76
77 private leastEluNextWorkerNodeKey (): number | undefined {
78 let minWorkerElu = Infinity
79 let chosenWorkerNodeKey: number | undefined
80 for (const [workerNodeKey, workerNode] of this.pool.workerNodes.entries()) {
81 const workerUsage = workerNode.usage
82 const workerElu = workerUsage.elu?.active?.aggregate ?? 0
83 if (this.isWorkerNodeEligible(workerNodeKey) && workerElu === 0) {
84 chosenWorkerNodeKey = workerNodeKey
85 break
86 } else if (
87 this.isWorkerNodeEligible(workerNodeKey) &&
88 workerElu < minWorkerElu
89 ) {
90 minWorkerElu = workerElu
91 chosenWorkerNodeKey = workerNodeKey
92 }
93 }
94 return chosenWorkerNodeKey
95 }
96 }