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