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