fix: fix worker node removal handling in worker choice strategies
[poolifier.git] / src / pools / selection-strategies / worker-choice-strategy-context.ts
CommitLineData
d35e5717
JB
1import type { IPool } from '../pool.js'
2import type { IWorker } from '../worker.js'
39618ede 3import { getWorkerChoiceStrategyRetries } from '../../utils.js'
d35e5717
JB
4import { FairShareWorkerChoiceStrategy } from './fair-share-worker-choice-strategy.js'
5import { InterleavedWeightedRoundRobinWorkerChoiceStrategy } from './interleaved-weighted-round-robin-worker-choice-strategy.js'
6import { LeastBusyWorkerChoiceStrategy } from './least-busy-worker-choice-strategy.js'
7import { LeastUsedWorkerChoiceStrategy } from './least-used-worker-choice-strategy.js'
8import { LeastEluWorkerChoiceStrategy } from './least-elu-worker-choice-strategy.js'
9import { RoundRobinWorkerChoiceStrategy } from './round-robin-worker-choice-strategy.js'
bdaf31cd
JB
10import type {
11 IWorkerChoiceStrategy,
6c6afb84 12 StrategyPolicy,
87de9ff5 13 TaskStatisticsRequirements,
39618ede
JB
14 WorkerChoiceStrategy,
15 WorkerChoiceStrategyOptions
d35e5717
JB
16} from './selection-strategies-types.js'
17import { WorkerChoiceStrategies } from './selection-strategies-types.js'
18import { WeightedRoundRobinWorkerChoiceStrategy } from './weighted-round-robin-worker-choice-strategy.js'
bdaf31cd
JB
19
20/**
21 * The worker choice strategy context.
22 *
38e795c1 23 * @typeParam Worker - Type of worker.
e102732c
JB
24 * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
25 * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
bdaf31cd
JB
26 */
27export class WorkerChoiceStrategyContext<
f06e48d8 28 Worker extends IWorker,
b2b1d84e
JB
29 Data = unknown,
30 Response = unknown
bdaf31cd 31> {
39618ede 32 /**
0268f52c 33 * The worker choice strategy instances registered in the context.
39618ede 34 */
b529c323 35 private readonly workerChoiceStrategies: Map<
95c83464 36 WorkerChoiceStrategy,
17393ac8 37 IWorkerChoiceStrategy
b529c323 38 >
bdaf31cd 39
39618ede
JB
40 /**
41 * The number of worker choice strategy execution retries.
42 */
43 private readonly retries: number
44
bdaf31cd
JB
45 /**
46 * Worker choice strategy context constructor.
47 *
38e795c1 48 * @param pool - The pool instance.
d710242d 49 * @param workerChoiceStrategy - The worker choice strategy.
da309861 50 * @param opts - The worker choice strategy options.
bdaf31cd
JB
51 */
52 public constructor (
c4855468 53 pool: IPool<Worker, Data, Response>,
d710242d 54 private workerChoiceStrategy: WorkerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN,
39618ede 55 opts?: WorkerChoiceStrategyOptions
bdaf31cd 56 ) {
7254e419 57 this.execute = this.execute.bind(this)
b529c323
JB
58 this.workerChoiceStrategies = new Map<
59 WorkerChoiceStrategy,
17393ac8 60 IWorkerChoiceStrategy
b529c323
JB
61 >([
62 [
63 WorkerChoiceStrategies.ROUND_ROBIN,
7254e419
JB
64 new (RoundRobinWorkerChoiceStrategy.bind(this))<Worker, Data, Response>(
65 pool,
39618ede 66 opts
7254e419 67 )
b529c323
JB
68 ],
69 [
e4543b14
JB
70 WorkerChoiceStrategies.LEAST_USED,
71 new (LeastUsedWorkerChoiceStrategy.bind(this))<Worker, Data, Response>(
7254e419 72 pool,
39618ede 73 opts
7254e419 74 )
b529c323
JB
75 ],
76 [
e4543b14
JB
77 WorkerChoiceStrategies.LEAST_BUSY,
78 new (LeastBusyWorkerChoiceStrategy.bind(this))<Worker, Data, Response>(
7254e419 79 pool,
39618ede 80 opts
7254e419 81 )
b529c323
JB
82 ],
83 [
058a9457
JB
84 WorkerChoiceStrategies.LEAST_ELU,
85 new (LeastEluWorkerChoiceStrategy.bind(this))<Worker, Data, Response>(
86 pool,
39618ede 87 opts
058a9457
JB
88 )
89 ],
90 [
b529c323 91 WorkerChoiceStrategies.FAIR_SHARE,
7254e419 92 new (FairShareWorkerChoiceStrategy.bind(this))<Worker, Data, Response>(
da309861 93 pool,
39618ede 94 opts
da309861 95 )
7254e419
JB
96 ],
97 [
98 WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN,
99 new (WeightedRoundRobinWorkerChoiceStrategy.bind(this))<
100 Worker,
101 Data,
102 Response
39618ede 103 >(pool, opts)
feec6e8c
JB
104 ],
105 [
106 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN,
107 new (InterleavedWeightedRoundRobinWorkerChoiceStrategy.bind(this))<
108 Worker,
109 Data,
110 Response
39618ede 111 >(pool, opts)
b529c323
JB
112 ]
113 ])
39618ede 114 this.retries = getWorkerChoiceStrategyRetries(pool, opts)
bdaf31cd
JB
115 }
116
6c6afb84
JB
117 /**
118 * Gets the strategy policy in the context.
119 *
120 * @returns The strategy policy.
121 */
122 public getStrategyPolicy (): StrategyPolicy {
67f3f2d6
JB
123 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
124 return this.workerChoiceStrategies.get(this.workerChoiceStrategy)!
125 .strategyPolicy
6c6afb84
JB
126 }
127
97a2abc3 128 /**
8990357d 129 * Gets the worker choice strategy in the context task statistics requirements.
97a2abc3 130 *
87de9ff5 131 * @returns The task statistics requirements.
97a2abc3 132 */
87de9ff5 133 public getTaskStatisticsRequirements (): TaskStatisticsRequirements {
67f3f2d6
JB
134 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
135 return this.workerChoiceStrategies.get(this.workerChoiceStrategy)!
136 .taskStatisticsRequirements
97a2abc3
JB
137 }
138
bdaf31cd 139 /**
bdede008 140 * Sets the worker choice strategy to use in the context.
bdaf31cd 141 *
38e795c1 142 * @param workerChoiceStrategy - The worker choice strategy to set.
bdaf31cd
JB
143 */
144 public setWorkerChoiceStrategy (
145 workerChoiceStrategy: WorkerChoiceStrategy
146 ): void {
d710242d
JB
147 if (this.workerChoiceStrategy !== workerChoiceStrategy) {
148 this.workerChoiceStrategy = workerChoiceStrategy
b2b1d84e 149 }
d710242d 150 this.workerChoiceStrategies.get(this.workerChoiceStrategy)?.reset()
bdaf31cd
JB
151 }
152
138d29a8 153 /**
8990357d 154 * Updates the worker node key in the worker choice strategy in the context internals.
138d29a8
JB
155 *
156 * @returns `true` if the update is successful, `false` otherwise.
157 */
a4958de2 158 public update (workerNodeKey: number): boolean {
67f3f2d6
JB
159 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
160 return this.workerChoiceStrategies
161 .get(this.workerChoiceStrategy)!
162 .update(workerNodeKey)
138d29a8
JB
163 }
164
bdaf31cd 165 /**
8990357d 166 * Executes the worker choice strategy in the context algorithm.
bdaf31cd 167 *
f06e48d8 168 * @returns The key of the worker node.
e868df5b 169 * @throws {@link https://nodejs.org/api/errors.html#class-error} If after computed retries the worker node key is null or undefined.
bdaf31cd 170 */
c923ce56 171 public execute (): number {
e44639e9
JB
172 return this.executeStrategy(
173 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
174 this.workerChoiceStrategies.get(this.workerChoiceStrategy)!
175 )
613a91dc
JB
176 }
177
178 /**
179 * Executes the given worker choice strategy.
180 *
181 * @param workerChoiceStrategy - The worker choice strategy.
182 * @returns The key of the worker node.
e44639e9 183 * @throws {@link https://nodejs.org/api/errors.html#class-error} If after computed retries the worker node key is null or undefined.
613a91dc
JB
184 */
185 private executeStrategy (workerChoiceStrategy: IWorkerChoiceStrategy): number {
fb5a7307 186 let workerNodeKey: number | undefined
fb5a7307
JB
187 let chooseCount = 0
188 let retriesCount = 0
189 do {
613a91dc
JB
190 workerNodeKey = workerChoiceStrategy.choose()
191 if (workerNodeKey == null && chooseCount > 0) {
192 retriesCount++
fb5a7307 193 }
613a91dc 194 chooseCount++
39618ede 195 } while (workerNodeKey == null && retriesCount < this.retries)
fb5a7307 196 if (workerNodeKey == null) {
e695d66f 197 throw new Error(
fb5a7307 198 `Worker node key chosen is null or undefined after ${retriesCount} retries`
4ba8492f 199 )
b0d6ed8f
JB
200 }
201 return workerNodeKey
bdaf31cd 202 }
97a2abc3
JB
203
204 /**
c7e196ba 205 * Removes the worker node key from the worker choice strategy in the context.
97a2abc3 206 *
501aea93 207 * @param workerNodeKey - The worker node key.
97a2abc3
JB
208 * @returns `true` if the removal is successful, `false` otherwise.
209 */
f06e48d8 210 public remove (workerNodeKey: number): boolean {
67f3f2d6
JB
211 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
212 return this.workerChoiceStrategies
213 .get(this.workerChoiceStrategy)!
214 .remove(workerNodeKey)
95c83464 215 }
a20f0ba5
JB
216
217 /**
218 * Sets the worker choice strategies in the context options.
219 *
220 * @param opts - The worker choice strategy options.
221 */
39618ede 222 public setOptions (opts: WorkerChoiceStrategyOptions | undefined): void {
0509fd43 223 for (const workerChoiceStrategy of this.workerChoiceStrategies.values()) {
39618ede 224 workerChoiceStrategy.setOptions(opts)
0509fd43 225 }
a20f0ba5 226 }
bdaf31cd 227}