feat: add per task function strategy support
[poolifier.git] / src / pools / selection-strategies / abstract-worker-choice-strategy.ts
CommitLineData
e9ed6eee 1import type { IPool } from '../pool.js'
bcfb06ce 2import { DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS } from '../utils.js'
d35e5717 3import type { IWorker } from '../worker.js'
10fcfaf4
JB
4import type {
5 IWorkerChoiceStrategy,
57441b79 6 MeasurementStatisticsRequirements,
6c6afb84 7 StrategyPolicy,
39618ede
JB
8 TaskStatisticsRequirements,
9 WorkerChoiceStrategyOptions
d35e5717 10} from './selection-strategies-types.js'
bcfb06ce 11import { buildWorkerChoiceStrategyOptions } from './selection-strategies-utils.js'
bdaf31cd
JB
12
13/**
9cd39dd4 14 * Worker choice strategy abstract base class.
bdaf31cd 15 *
38e795c1 16 * @typeParam Worker - Type of worker which manages the strategy.
e102732c
JB
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.
bdaf31cd
JB
19 */
20export abstract class AbstractWorkerChoiceStrategy<
f06e48d8 21 Worker extends IWorker,
b2b1d84e
JB
22 Data = unknown,
23 Response = unknown
17393ac8 24> implements IWorkerChoiceStrategy {
d33be430 25 /**
9b106837 26 * The next worker node key.
d33be430 27 */
b1aae695 28 protected nextWorkerNodeKey: number | undefined = 0
d33be430 29
7c7bb289
JB
30 /**
31 * The previous worker node key.
32 */
c63a35a0 33 protected previousWorkerNodeKey = 0
7c7bb289 34
6c6afb84
JB
35 /** @inheritDoc */
36 public readonly strategyPolicy: StrategyPolicy = {
b1aae695 37 dynamicWorkerUsage: false,
f6bc9f26 38 dynamicWorkerReady: true
6c6afb84
JB
39 }
40
afc003b2 41 /** @inheritDoc */
87de9ff5 42 public readonly taskStatisticsRequirements: TaskStatisticsRequirements = {
3c93feb9
JB
43 runTime: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
44 waitTime: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
45 elu: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS
10fcfaf4 46 }
bdaf31cd
JB
47
48 /**
6533c3e6 49 * Constructs a worker choice strategy bound to the pool.
bdaf31cd 50 *
38e795c1 51 * @param pool - The pool instance.
da309861 52 * @param opts - The worker choice strategy options.
bdaf31cd
JB
53 */
54 public constructor (
c4855468 55 protected readonly pool: IPool<Worker, Data, Response>,
39618ede 56 protected opts?: WorkerChoiceStrategyOptions
b8f3418c 57 ) {
7254e419 58 this.choose = this.choose.bind(this)
04d875a3 59 this.setOptions(this.opts)
b8f3418c 60 }
bdaf31cd 61
932fc8be 62 protected setTaskStatisticsRequirements (
39618ede 63 opts: WorkerChoiceStrategyOptions | undefined
932fc8be 64 ): void {
57441b79
JB
65 this.toggleMedianMeasurementStatisticsRequirements(
66 this.taskStatisticsRequirements.runTime,
67f3f2d6 67 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
39618ede 68 opts!.runTime!.median
57441b79
JB
69 )
70 this.toggleMedianMeasurementStatisticsRequirements(
71 this.taskStatisticsRequirements.waitTime,
67f3f2d6 72 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
39618ede 73 opts!.waitTime!.median
57441b79
JB
74 )
75 this.toggleMedianMeasurementStatisticsRequirements(
76 this.taskStatisticsRequirements.elu,
67f3f2d6 77 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
39618ede 78 opts!.elu!.median
57441b79
JB
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
5df69fab 89 }
57441b79
JB
90 if (measurementStatisticsRequirements.median && !toggleMedian) {
91 measurementStatisticsRequirements.average = true
92 measurementStatisticsRequirements.median = toggleMedian
5df69fab 93 }
da309861
JB
94 }
95
39a43af7
JB
96 protected resetWorkerNodeKeyProperties (): void {
97 this.nextWorkerNodeKey = 0
98 this.previousWorkerNodeKey = 0
99 }
100
afc003b2 101 /** @inheritDoc */
a6f7f1b4 102 public abstract reset (): boolean
ea7a90d3 103
138d29a8 104 /** @inheritDoc */
a4958de2 105 public abstract update (workerNodeKey: number): boolean
138d29a8 106
afc003b2 107 /** @inheritDoc */
b1aae695 108 public abstract choose (): number | undefined
97a2abc3 109
afc003b2 110 /** @inheritDoc */
f06e48d8 111 public abstract remove (workerNodeKey: number): boolean
a20f0ba5
JB
112
113 /** @inheritDoc */
39618ede
JB
114 public setOptions (opts: WorkerChoiceStrategyOptions | undefined): void {
115 this.opts = buildWorkerChoiceStrategyOptions<Worker, Data, Response>(
116 this.pool,
00e1bdeb
JB
117 opts
118 )
65ab8dcc 119 this.setTaskStatisticsRequirements(this.opts)
a20f0ba5 120 }
cb70b19d 121
d4ddb68b
JB
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 */
ae3ab61d 128 protected isWorkerNodeReady (workerNodeKey: number): boolean {
535fd8d5 129 return this.pool.workerNodes[workerNodeKey]?.info?.ready ?? false
19dbc45b
JB
130 }
131
a38b62f1 132 /**
8e8d9101 133 * Check the next worker node key.
a38b62f1 134 */
8e8d9101
JB
135 protected checkNextWorkerNodeKey (): void {
136 if (
137 this.nextWorkerNodeKey != null &&
138 (this.nextWorkerNodeKey < 0 ||
139 !this.isWorkerNodeReady(this.nextWorkerNodeKey))
140 ) {
a38b62f1
JB
141 delete this.nextWorkerNodeKey
142 }
143 }
144
f6b641d6 145 /**
f3a91bac 146 * Gets the worker node task runtime.
932fc8be
JB
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.
f6b641d6
JB
149 *
150 * @param workerNodeKey - The worker node key.
f3a91bac 151 * @returns The worker node task runtime.
f6b641d6 152 */
f3a91bac 153 protected getWorkerNodeTaskRunTime (workerNodeKey: number): number {
932fc8be 154 return this.taskStatisticsRequirements.runTime.median
46b0bb09
JB
155 ? this.pool.workerNodes[workerNodeKey].usage.runTime.median ?? 0
156 : this.pool.workerNodes[workerNodeKey].usage.runTime.average ?? 0
f6b641d6
JB
157 }
158
ef680bb8 159 /**
f3a91bac 160 * Gets the worker node task wait time.
932fc8be
JB
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.
ef680bb8
JB
163 *
164 * @param workerNodeKey - The worker node key.
f3a91bac 165 * @returns The worker node task wait time.
ef680bb8 166 */
f3a91bac 167 protected getWorkerNodeTaskWaitTime (workerNodeKey: number): number {
932fc8be 168 return this.taskStatisticsRequirements.waitTime.median
46b0bb09
JB
169 ? this.pool.workerNodes[workerNodeKey].usage.waitTime.median ?? 0
170 : this.pool.workerNodes[workerNodeKey].usage.waitTime.average ?? 0
ef680bb8
JB
171 }
172
5df69fab 173 /**
f3a91bac 174 * Gets the worker node task ELU.
9adcefab
JB
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.
5df69fab
JB
177 *
178 * @param workerNodeKey - The worker node key.
f3a91bac 179 * @returns The worker node task ELU.
5df69fab 180 */
f3a91bac 181 protected getWorkerNodeTaskElu (workerNodeKey: number): number {
5df69fab 182 return this.taskStatisticsRequirements.elu.median
46b0bb09
JB
183 ? this.pool.workerNodes[workerNodeKey].usage.elu.active.median ?? 0
184 : this.pool.workerNodes[workerNodeKey].usage.elu.active.average ?? 0
5df69fab
JB
185 }
186
baca80f7
JB
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 {
8e8d9101
JB
193 this.previousWorkerNodeKey =
194 workerNodeKey != null && workerNodeKey >= 0
195 ? workerNodeKey
196 : this.previousWorkerNodeKey
baca80f7 197 }
bdaf31cd 198}