refactor: use Array.from to build Array() from Map()
[poolifier.git] / src / pools / selection-strategies / worker-choice-strategies-context.ts
CommitLineData
bcfb06ce
JB
1import type { IPool } from '../pool.js'
2import type { IWorker } from '../worker.js'
3import type {
4 IWorkerChoiceStrategy,
5 StrategyPolicy,
6 TaskStatisticsRequirements,
7 WorkerChoiceStrategy,
8 WorkerChoiceStrategyOptions
9} from './selection-strategies-types.js'
10import { WorkerChoiceStrategies } from './selection-strategies-types.js'
11import {
19b8be8b
JB
12 buildWorkerChoiceStrategiesPolicy,
13 buildWorkerChoiceStrategiesTaskStatisticsRequirements,
bcfb06ce
JB
14 getWorkerChoiceStrategiesRetries,
15 getWorkerChoiceStrategy
16} from './selection-strategies-utils.js'
17
18/**
19 * The worker choice strategies context.
20 *
21 * @typeParam Worker - Type of worker.
22 * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
23 * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
24 */
25export class WorkerChoiceStrategiesContext<
26 Worker extends IWorker,
27 Data = unknown,
28 Response = unknown
29> {
30 /**
31 * The number of worker choice strategies execution retries.
32 */
33 public retriesCount: number
34
35 /**
36 * The default worker choice strategy in the context.
37 */
38 private defaultWorkerChoiceStrategy: WorkerChoiceStrategy
39
40 /**
41 * The worker choice strategies registered in the context.
42 */
43 private readonly workerChoiceStrategies: Map<
44 WorkerChoiceStrategy,
45 IWorkerChoiceStrategy
46 >
47
19b8be8b
JB
48 /**
49 * The active worker choice strategies in the context policy.
50 */
51 private workerChoiceStrategiesPolicy: StrategyPolicy
52
53 /**
54 * The active worker choice strategies in the context task statistics requirements.
55 */
56 private workerChoiceStrategiesTaskStatisticsRequirements: TaskStatisticsRequirements
57
bcfb06ce
JB
58 /**
59 * The maximum number of worker choice strategies execution retries.
60 */
61 private readonly retries: number
62
63 /**
64 * Worker choice strategies context constructor.
65 *
66 * @param pool - The pool instance.
67 * @param workerChoiceStrategies - The worker choice strategies. @defaultValue [WorkerChoiceStrategies.ROUND_ROBIN]
68 * @param opts - The worker choice strategy options.
69 */
70 public constructor (
71 private readonly pool: IPool<Worker, Data, Response>,
72 workerChoiceStrategies: WorkerChoiceStrategy[] = [
73 WorkerChoiceStrategies.ROUND_ROBIN
74 ],
75 opts?: WorkerChoiceStrategyOptions
76 ) {
77 this.execute = this.execute.bind(this)
78 this.defaultWorkerChoiceStrategy = workerChoiceStrategies[0]
79 this.workerChoiceStrategies = new Map<
80 WorkerChoiceStrategy,
81 IWorkerChoiceStrategy
82 >()
83 for (const workerChoiceStrategy of workerChoiceStrategies) {
84 this.addWorkerChoiceStrategy(workerChoiceStrategy, this.pool, opts)
85 }
19b8be8b
JB
86 this.workerChoiceStrategiesPolicy = buildWorkerChoiceStrategiesPolicy(
87 this.workerChoiceStrategies
88 )
89 this.workerChoiceStrategiesTaskStatisticsRequirements =
90 buildWorkerChoiceStrategiesTaskStatisticsRequirements(
91 this.workerChoiceStrategies
92 )
bcfb06ce 93 this.retriesCount = 0
d0bd5062
JB
94 this.retries = getWorkerChoiceStrategiesRetries<Worker, Data, Response>(
95 this.pool,
96 opts
97 )
bcfb06ce
JB
98 }
99
100 /**
85bbc7ab 101 * Gets the active worker choice strategies in the context policy.
bcfb06ce
JB
102 *
103 * @returns The strategies policy.
104 */
105 public getPolicy (): StrategyPolicy {
19b8be8b 106 return this.workerChoiceStrategiesPolicy
bcfb06ce
JB
107 }
108
109 /**
110 * Gets the active worker choice strategies in the context task statistics requirements.
111 *
19b8be8b 112 * @returns The strategies task statistics requirements.
bcfb06ce
JB
113 */
114 public getTaskStatisticsRequirements (): TaskStatisticsRequirements {
19b8be8b 115 return this.workerChoiceStrategiesTaskStatisticsRequirements
bcfb06ce
JB
116 }
117
118 /**
119 * Sets the default worker choice strategy to use in the context.
120 *
121 * @param workerChoiceStrategy - The default worker choice strategy to set.
122 * @param opts - The worker choice strategy options.
123 */
124 public setDefaultWorkerChoiceStrategy (
125 workerChoiceStrategy: WorkerChoiceStrategy,
126 opts?: WorkerChoiceStrategyOptions
127 ): void {
85bbc7ab
JB
128 if (workerChoiceStrategy !== this.defaultWorkerChoiceStrategy) {
129 this.defaultWorkerChoiceStrategy = workerChoiceStrategy
130 this.addWorkerChoiceStrategy(workerChoiceStrategy, this.pool, opts)
131 }
bcfb06ce
JB
132 }
133
134 /**
135 * Updates the worker node key in the active worker choice strategies in the context internals.
136 *
137 * @returns `true` if the update is successful, `false` otherwise.
138 */
139 public update (workerNodeKey: number): boolean {
63af5400
JB
140 return Array.from(
141 this.workerChoiceStrategies,
142 ([_, workerChoiceStrategy]) => workerChoiceStrategy.update(workerNodeKey)
143 ).every(r => r)
bcfb06ce
JB
144 }
145
146 /**
19b8be8b 147 * Executes the given worker choice strategy in the context algorithm.
bcfb06ce
JB
148 *
149 * @param workerChoiceStrategy - The worker choice strategy algorithm to execute. @defaultValue this.defaultWorkerChoiceStrategy
150 * @returns The key of the worker node.
151 * @throws {@link https://nodejs.org/api/errors.html#class-error} If after computed retries the worker node key is null or undefined.
152 */
153 public execute (
154 workerChoiceStrategy: WorkerChoiceStrategy = this
155 .defaultWorkerChoiceStrategy
156 ): number {
157 return this.executeStrategy(
158 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
159 this.workerChoiceStrategies.get(workerChoiceStrategy)!
160 )
161 }
162
163 /**
164 * Executes the given worker choice strategy.
165 *
166 * @param workerChoiceStrategy - The worker choice strategy.
167 * @returns The key of the worker node.
168 * @throws {@link https://nodejs.org/api/errors.html#class-error} If after computed retries the worker node key is null or undefined.
169 */
170 private executeStrategy (workerChoiceStrategy: IWorkerChoiceStrategy): number {
171 let workerNodeKey: number | undefined
172 let chooseCount = 0
173 let retriesCount = 0
174 do {
175 workerNodeKey = workerChoiceStrategy.choose()
176 if (workerNodeKey == null && chooseCount > 0) {
177 ++retriesCount
178 ++this.retriesCount
179 }
180 ++chooseCount
181 } while (workerNodeKey == null && retriesCount < this.retries)
182 if (workerNodeKey == null) {
183 throw new Error(
184 `Worker node key chosen is null or undefined after ${retriesCount} retries`
185 )
186 }
187 return workerNodeKey
188 }
189
190 /**
191 * Removes the worker node key from the active worker choice strategies in the context.
192 *
193 * @param workerNodeKey - The worker node key.
194 * @returns `true` if the removal is successful, `false` otherwise.
195 */
196 public remove (workerNodeKey: number): boolean {
63af5400
JB
197 return Array.from(
198 this.workerChoiceStrategies,
199 ([_, workerChoiceStrategy]) => workerChoiceStrategy.remove(workerNodeKey)
200 ).every(r => r)
bcfb06ce
JB
201 }
202
203 /**
204 * Sets the active worker choice strategies in the context options.
205 *
206 * @param opts - The worker choice strategy options.
207 */
208 public setOptions (opts: WorkerChoiceStrategyOptions | undefined): void {
209 for (const workerChoiceStrategy of this.workerChoiceStrategies.values()) {
210 workerChoiceStrategy.setOptions(opts)
211 }
212 }
213
85bbc7ab
JB
214 /**
215 * Synchronizes the active worker choice strategies in the context with the given worker choice strategies.
216 *
217 * @param workerChoiceStrategies - The worker choice strategies to synchronize.
218 * @param opts - The worker choice strategy options.
219 */
220 public syncWorkerChoiceStrategies (
221 workerChoiceStrategies: Set<WorkerChoiceStrategy>,
222 opts?: WorkerChoiceStrategyOptions
223 ): void {
224 for (const workerChoiceStrategy of this.workerChoiceStrategies.keys()) {
225 if (!workerChoiceStrategies.has(workerChoiceStrategy)) {
226 this.removeWorkerChoiceStrategy(workerChoiceStrategy)
227 }
228 }
229 for (const workerChoiceStrategy of workerChoiceStrategies) {
230 if (!this.workerChoiceStrategies.has(workerChoiceStrategy)) {
231 this.addWorkerChoiceStrategy(workerChoiceStrategy, this.pool, opts)
232 }
233 }
19b8be8b
JB
234 this.workerChoiceStrategiesPolicy = buildWorkerChoiceStrategiesPolicy(
235 this.workerChoiceStrategies
236 )
237 this.workerChoiceStrategiesTaskStatisticsRequirements =
238 buildWorkerChoiceStrategiesTaskStatisticsRequirements(
239 this.workerChoiceStrategies
240 )
85bbc7ab
JB
241 }
242
243 /**
244 * Adds a worker choice strategy to the context.
245 *
246 * @param workerChoiceStrategy - The worker choice strategy to add.
247 * @param opts - The worker choice strategy options.
248 * @returns The worker choice strategies.
249 */
bcfb06ce
JB
250 private addWorkerChoiceStrategy (
251 workerChoiceStrategy: WorkerChoiceStrategy,
252 pool: IPool<Worker, Data, Response>,
253 opts?: WorkerChoiceStrategyOptions
254 ): Map<WorkerChoiceStrategy, IWorkerChoiceStrategy> {
255 if (!this.workerChoiceStrategies.has(workerChoiceStrategy)) {
256 return this.workerChoiceStrategies.set(
257 workerChoiceStrategy,
258 getWorkerChoiceStrategy<Worker, Data, Response>(
259 workerChoiceStrategy,
260 pool,
261 this,
262 opts
263 )
264 )
265 }
266 return this.workerChoiceStrategies
267 }
268
85bbc7ab
JB
269 /**
270 * Removes a worker choice strategy from the context.
271 *
272 * @param workerChoiceStrategy - The worker choice strategy to remove.
273 * @returns `true` if the worker choice strategy is removed, `false` otherwise.
274 */
275 private removeWorkerChoiceStrategy (
276 workerChoiceStrategy: WorkerChoiceStrategy
277 ): boolean {
278 return this.workerChoiceStrategies.delete(workerChoiceStrategy)
279 }
bcfb06ce 280}