perf: factor out strategies helper
[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 StrategyPolicy,
7 TaskStatisticsRequirements,
8 WorkerChoiceStrategyOptions
9 } from './selection-strategies-types.js'
10 import {
11 buildWorkerChoiceStrategyOptions,
12 toggleMedianMeasurementStatisticsRequirements
13 } from './selection-strategies-utils.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 = 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.choose = this.choose.bind(this)
61 this.setOptions(this.opts)
62 }
63
64 protected setTaskStatisticsRequirements (
65 opts: WorkerChoiceStrategyOptions | undefined
66 ): void {
67 toggleMedianMeasurementStatisticsRequirements(
68 this.taskStatisticsRequirements.runTime,
69 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
70 opts!.runTime!.median
71 )
72 toggleMedianMeasurementStatisticsRequirements(
73 this.taskStatisticsRequirements.waitTime,
74 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
75 opts!.waitTime!.median
76 )
77 toggleMedianMeasurementStatisticsRequirements(
78 this.taskStatisticsRequirements.elu,
79 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
80 opts!.elu!.median
81 )
82 }
83
84 protected resetWorkerNodeKeyProperties (): void {
85 this.nextWorkerNodeKey = 0
86 this.previousWorkerNodeKey = 0
87 }
88
89 /** @inheritDoc */
90 public abstract reset (): boolean
91
92 /** @inheritDoc */
93 public abstract update (workerNodeKey: number): boolean
94
95 /** @inheritDoc */
96 public abstract choose (): number | undefined
97
98 /** @inheritDoc */
99 public abstract remove (workerNodeKey: number): boolean
100
101 /** @inheritDoc */
102 public setOptions (opts: WorkerChoiceStrategyOptions | undefined): void {
103 this.opts = buildWorkerChoiceStrategyOptions<Worker, Data, Response>(
104 this.pool,
105 opts
106 )
107 this.setTaskStatisticsRequirements(this.opts)
108 }
109
110 /**
111 * Whether the worker node is ready or not.
112 *
113 * @param workerNodeKey - The worker node key.
114 * @returns Whether the worker node is ready or not.
115 */
116 protected isWorkerNodeReady (workerNodeKey: number): boolean {
117 return this.pool.workerNodes[workerNodeKey]?.info?.ready ?? false
118 }
119
120 /**
121 * Check the next worker node key.
122 */
123 protected checkNextWorkerNodeKey (): void {
124 if (
125 this.nextWorkerNodeKey != null &&
126 (this.nextWorkerNodeKey < 0 ||
127 !this.isWorkerNodeReady(this.nextWorkerNodeKey))
128 ) {
129 delete this.nextWorkerNodeKey
130 }
131 }
132
133 /**
134 * Gets the worker node task runtime.
135 * If the task statistics require the average runtime, the average runtime is returned.
136 * If the task statistics require the median runtime , the median runtime is returned.
137 *
138 * @param workerNodeKey - The worker node key.
139 * @returns The worker node task runtime.
140 */
141 protected getWorkerNodeTaskRunTime (workerNodeKey: number): number {
142 return this.taskStatisticsRequirements.runTime.median
143 ? this.pool.workerNodes[workerNodeKey].usage.runTime.median ?? 0
144 : this.pool.workerNodes[workerNodeKey].usage.runTime.average ?? 0
145 }
146
147 /**
148 * Gets the worker node task wait time.
149 * If the task statistics require the average wait time, the average wait time is returned.
150 * If the task statistics require the median wait time, the median wait time is returned.
151 *
152 * @param workerNodeKey - The worker node key.
153 * @returns The worker node task wait time.
154 */
155 protected getWorkerNodeTaskWaitTime (workerNodeKey: number): number {
156 return this.taskStatisticsRequirements.waitTime.median
157 ? this.pool.workerNodes[workerNodeKey].usage.waitTime.median ?? 0
158 : this.pool.workerNodes[workerNodeKey].usage.waitTime.average ?? 0
159 }
160
161 /**
162 * Gets the worker node task ELU.
163 * If the task statistics require the average ELU, the average ELU is returned.
164 * If the task statistics require the median ELU, the median ELU is returned.
165 *
166 * @param workerNodeKey - The worker node key.
167 * @returns The worker node task ELU.
168 */
169 protected getWorkerNodeTaskElu (workerNodeKey: number): number {
170 return this.taskStatisticsRequirements.elu.median
171 ? this.pool.workerNodes[workerNodeKey].usage.elu.active.median ?? 0
172 : this.pool.workerNodes[workerNodeKey].usage.elu.active.average ?? 0
173 }
174
175 /**
176 * Sets safely the previous worker node key.
177 *
178 * @param workerNodeKey - The worker node key.
179 */
180 protected setPreviousWorkerNodeKey (workerNodeKey: number | undefined): void {
181 this.previousWorkerNodeKey =
182 workerNodeKey != null && workerNodeKey >= 0
183 ? workerNodeKey
184 : this.previousWorkerNodeKey
185 }
186 }