refactor: remove unneeded worker choice strategy storage in intermediate
[poolifier.git] / src / pools / selection-strategies / abstract-worker-choice-strategy.ts
1 import {
2 DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
3 buildWorkerChoiceStrategyOptions
4 } from '../../utils.js'
5 import type { IPool } from '../pool.js'
6 import type { IWorker } from '../worker.js'
7 import type {
8 IWorkerChoiceStrategy,
9 MeasurementStatisticsRequirements,
10 StrategyPolicy,
11 TaskStatisticsRequirements,
12 WorkerChoiceStrategyOptions
13 } from './selection-strategies-types.js'
14
15 /**
16 * Worker choice strategy abstract base class.
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 abstract class AbstractWorkerChoiceStrategy<
23 Worker extends IWorker,
24 Data = unknown,
25 Response = unknown
26 > implements IWorkerChoiceStrategy {
27 /**
28 * The next worker node key.
29 */
30 protected nextWorkerNodeKey: number | undefined = 0
31
32 /**
33 * The previous worker node key.
34 */
35 protected previousWorkerNodeKey: number = 0
36
37 /** @inheritDoc */
38 public readonly strategyPolicy: StrategyPolicy = {
39 dynamicWorkerUsage: false,
40 dynamicWorkerReady: true
41 }
42
43 /** @inheritDoc */
44 public readonly taskStatisticsRequirements: TaskStatisticsRequirements = {
45 runTime: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
46 waitTime: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
47 elu: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS
48 }
49
50 /**
51 * Constructs a worker choice strategy bound to the pool.
52 *
53 * @param pool - The pool instance.
54 * @param opts - The worker choice strategy options.
55 */
56 public constructor (
57 protected readonly pool: IPool<Worker, Data, Response>,
58 protected opts?: WorkerChoiceStrategyOptions
59 ) {
60 this.opts = buildWorkerChoiceStrategyOptions<Worker, Data, Response>(
61 this.pool,
62 this.opts
63 )
64 this.setTaskStatisticsRequirements(this.opts)
65 this.choose = this.choose.bind(this)
66 }
67
68 protected setTaskStatisticsRequirements (
69 opts: WorkerChoiceStrategyOptions | undefined
70 ): void {
71 this.toggleMedianMeasurementStatisticsRequirements(
72 this.taskStatisticsRequirements.runTime,
73 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
74 opts!.runTime!.median
75 )
76 this.toggleMedianMeasurementStatisticsRequirements(
77 this.taskStatisticsRequirements.waitTime,
78 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
79 opts!.waitTime!.median
80 )
81 this.toggleMedianMeasurementStatisticsRequirements(
82 this.taskStatisticsRequirements.elu,
83 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
84 opts!.elu!.median
85 )
86 }
87
88 private toggleMedianMeasurementStatisticsRequirements (
89 measurementStatisticsRequirements: MeasurementStatisticsRequirements,
90 toggleMedian: boolean
91 ): void {
92 if (measurementStatisticsRequirements.average && toggleMedian) {
93 measurementStatisticsRequirements.average = false
94 measurementStatisticsRequirements.median = toggleMedian
95 }
96 if (measurementStatisticsRequirements.median && !toggleMedian) {
97 measurementStatisticsRequirements.average = true
98 measurementStatisticsRequirements.median = toggleMedian
99 }
100 }
101
102 protected resetWorkerNodeKeyProperties (): void {
103 this.nextWorkerNodeKey = 0
104 this.previousWorkerNodeKey = 0
105 }
106
107 /** @inheritDoc */
108 public abstract reset (): boolean
109
110 /** @inheritDoc */
111 public abstract update (workerNodeKey: number): boolean
112
113 /** @inheritDoc */
114 public abstract choose (): number | undefined
115
116 /** @inheritDoc */
117 public abstract remove (workerNodeKey: number): boolean
118
119 /** @inheritDoc */
120 public setOptions (opts: WorkerChoiceStrategyOptions | undefined): void {
121 this.opts = buildWorkerChoiceStrategyOptions<Worker, Data, Response>(
122 this.pool,
123 opts
124 )
125 this.setTaskStatisticsRequirements(this.opts)
126 }
127
128 /** @inheritDoc */
129 public hasPoolWorkerNodesReady (): boolean {
130 return this.pool.workerNodes.some(workerNode => workerNode.info.ready)
131 }
132
133 /**
134 * Whether the worker node is ready or not.
135 *
136 * @param workerNodeKey - The worker node key.
137 * @returns Whether the worker node is ready or not.
138 */
139 protected isWorkerNodeReady (workerNodeKey: number): boolean {
140 return this.pool.workerNodes[workerNodeKey]?.info?.ready ?? false
141 }
142
143 /**
144 * Check the next worker node readiness.
145 */
146 protected checkNextWorkerNodeReadiness (): void {
147 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
148 if (!this.isWorkerNodeReady(this.nextWorkerNodeKey!)) {
149 delete this.nextWorkerNodeKey
150 }
151 }
152
153 /**
154 * Gets the worker node task runtime.
155 * If the task statistics require the average runtime, the average runtime is returned.
156 * If the task statistics require the median runtime , the median runtime is returned.
157 *
158 * @param workerNodeKey - The worker node key.
159 * @returns The worker node task runtime.
160 */
161 protected getWorkerNodeTaskRunTime (workerNodeKey: number): number {
162 return this.taskStatisticsRequirements.runTime.median
163 ? this.pool.workerNodes[workerNodeKey].usage.runTime.median ?? 0
164 : this.pool.workerNodes[workerNodeKey].usage.runTime.average ?? 0
165 }
166
167 /**
168 * Gets the worker node task wait time.
169 * If the task statistics require the average wait time, the average wait time is returned.
170 * If the task statistics require the median wait time, the median wait time is returned.
171 *
172 * @param workerNodeKey - The worker node key.
173 * @returns The worker node task wait time.
174 */
175 protected getWorkerNodeTaskWaitTime (workerNodeKey: number): number {
176 return this.taskStatisticsRequirements.waitTime.median
177 ? this.pool.workerNodes[workerNodeKey].usage.waitTime.median ?? 0
178 : this.pool.workerNodes[workerNodeKey].usage.waitTime.average ?? 0
179 }
180
181 /**
182 * Gets the worker node task ELU.
183 * If the task statistics require the average ELU, the average ELU is returned.
184 * If the task statistics require the median ELU, the median ELU is returned.
185 *
186 * @param workerNodeKey - The worker node key.
187 * @returns The worker node task ELU.
188 */
189 protected getWorkerNodeTaskElu (workerNodeKey: number): number {
190 return this.taskStatisticsRequirements.elu.median
191 ? this.pool.workerNodes[workerNodeKey].usage.elu.active.median ?? 0
192 : this.pool.workerNodes[workerNodeKey].usage.elu.active.average ?? 0
193 }
194
195 /**
196 * Sets safely the previous worker node key.
197 *
198 * @param workerNodeKey - The worker node key.
199 */
200 protected setPreviousWorkerNodeKey (workerNodeKey: number | undefined): void {
201 this.previousWorkerNodeKey = workerNodeKey ?? this.previousWorkerNodeKey
202 }
203 }