chore: generate documentation
[poolifier.git] / src / pools / selection-strategies / abstract-worker-choice-strategy.ts
CommitLineData
0bbf65c3 1import { cpus } from 'node:os'
3c93feb9
JB
2import {
3 DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
4 DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
5} from '../../utils'
08f3f44c 6import type { IPool } from '../pool'
f06e48d8 7import type { IWorker } from '../worker'
10fcfaf4
JB
8import type {
9 IWorkerChoiceStrategy,
6c6afb84 10 StrategyPolicy,
87de9ff5 11 TaskStatisticsRequirements,
da309861 12 WorkerChoiceStrategyOptions
10fcfaf4 13} from './selection-strategies-types'
bdaf31cd
JB
14
15/**
9cd39dd4 16 * Worker choice strategy abstract base class.
bdaf31cd 17 *
38e795c1 18 * @typeParam Worker - Type of worker which manages the strategy.
e102732c
JB
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.
bdaf31cd
JB
21 */
22export abstract class AbstractWorkerChoiceStrategy<
f06e48d8 23 Worker extends IWorker,
b2b1d84e
JB
24 Data = unknown,
25 Response = unknown
17393ac8 26> implements IWorkerChoiceStrategy {
1d6d93ce
JB
27 // /**
28 // * Toggles finding the last free worker node key.
29 // */
30 // private toggleFindLastFreeWorkerNodeKey: boolean = false
6c6afb84 31
d33be430
JB
32 /**
33 * Id of the next worker node.
34 */
35 protected nextWorkerNodeId: number = 0
36
6c6afb84
JB
37 /** @inheritDoc */
38 public readonly strategyPolicy: StrategyPolicy = {
39 useDynamicWorker: false
40 }
41
afc003b2 42 /** @inheritDoc */
87de9ff5 43 public readonly taskStatisticsRequirements: TaskStatisticsRequirements = {
3c93feb9
JB
44 runTime: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
45 waitTime: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
46 elu: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS
10fcfaf4 47 }
bdaf31cd
JB
48
49 /**
6533c3e6 50 * Constructs a worker choice strategy bound to the pool.
bdaf31cd 51 *
38e795c1 52 * @param pool - The pool instance.
da309861 53 * @param opts - The worker choice strategy options.
bdaf31cd
JB
54 */
55 public constructor (
c4855468 56 protected readonly pool: IPool<Worker, Data, Response>,
a20f0ba5 57 protected opts: WorkerChoiceStrategyOptions = DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
b8f3418c 58 ) {
7254e419 59 this.choose = this.choose.bind(this)
b8f3418c 60 }
bdaf31cd 61
932fc8be
JB
62 protected setTaskStatisticsRequirements (
63 opts: WorkerChoiceStrategyOptions
64 ): void {
87de9ff5 65 if (
932fc8be
JB
66 this.taskStatisticsRequirements.runTime.average &&
67 opts.runTime?.median === true
87de9ff5 68 ) {
932fc8be
JB
69 this.taskStatisticsRequirements.runTime.average = false
70 this.taskStatisticsRequirements.runTime.median = opts.runTime
71 .median as boolean
da309861 72 }
87de9ff5 73 if (
932fc8be
JB
74 this.taskStatisticsRequirements.runTime.median &&
75 opts.runTime?.median === false
87de9ff5 76 ) {
932fc8be
JB
77 this.taskStatisticsRequirements.runTime.average = true
78 this.taskStatisticsRequirements.runTime.median = opts.runTime
79 .median as boolean
a20f0ba5 80 }
87de9ff5 81 if (
932fc8be
JB
82 this.taskStatisticsRequirements.waitTime.average &&
83 opts.waitTime?.median === true
87de9ff5 84 ) {
932fc8be
JB
85 this.taskStatisticsRequirements.waitTime.average = false
86 this.taskStatisticsRequirements.waitTime.median = opts.waitTime
87 .median as boolean
0567595a 88 }
87de9ff5 89 if (
932fc8be
JB
90 this.taskStatisticsRequirements.waitTime.median &&
91 opts.waitTime?.median === false
87de9ff5 92 ) {
932fc8be
JB
93 this.taskStatisticsRequirements.waitTime.average = true
94 this.taskStatisticsRequirements.waitTime.median = opts.waitTime
95 .median as boolean
0567595a 96 }
5df69fab
JB
97 if (
98 this.taskStatisticsRequirements.elu.average &&
99 opts.elu?.median === true
100 ) {
101 this.taskStatisticsRequirements.elu.average = false
102 this.taskStatisticsRequirements.elu.median = opts.elu.median as boolean
103 }
104 if (
105 this.taskStatisticsRequirements.elu.median &&
106 opts.elu?.median === false
107 ) {
108 this.taskStatisticsRequirements.elu.average = true
109 this.taskStatisticsRequirements.elu.median = opts.elu.median as boolean
110 }
da309861
JB
111 }
112
afc003b2 113 /** @inheritDoc */
a6f7f1b4 114 public abstract reset (): boolean
ea7a90d3 115
138d29a8 116 /** @inheritDoc */
a4958de2 117 public abstract update (workerNodeKey: number): boolean
138d29a8 118
afc003b2 119 /** @inheritDoc */
c923ce56 120 public abstract choose (): number
97a2abc3 121
afc003b2 122 /** @inheritDoc */
f06e48d8 123 public abstract remove (workerNodeKey: number): boolean
a20f0ba5
JB
124
125 /** @inheritDoc */
126 public setOptions (opts: WorkerChoiceStrategyOptions): void {
65ab8dcc
JB
127 this.opts = opts ?? DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
128 this.setTaskStatisticsRequirements(this.opts)
a20f0ba5 129 }
cb70b19d 130
1d6d93ce
JB
131 // /**
132 // * Finds a free worker node key.
133 // *
134 // * @returns The free worker node key or `-1` if there is no free worker node.
135 // */
136 // protected findFreeWorkerNodeKey (): number {
137 // if (this.toggleFindLastFreeWorkerNodeKey) {
138 // this.toggleFindLastFreeWorkerNodeKey = false
139 // return this.findLastFreeWorkerNodeKey()
140 // }
141 // this.toggleFindLastFreeWorkerNodeKey = true
142 // return this.findFirstFreeWorkerNodeKey()
143 // }
e0ae6100 144
f6b641d6 145 /**
e6606302 146 * Gets the worker 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.
e6606302 151 * @returns The worker task runtime.
f6b641d6
JB
152 */
153 protected getWorkerTaskRunTime (workerNodeKey: number): number {
932fc8be 154 return this.taskStatisticsRequirements.runTime.median
71514351
JB
155 ? this.pool.workerNodes[workerNodeKey].usage.runTime?.median ?? 0
156 : this.pool.workerNodes[workerNodeKey].usage.runTime?.average ?? 0
f6b641d6
JB
157 }
158
ef680bb8
JB
159 /**
160 * Gets the worker 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.
165 * @returns The worker task wait time.
166 */
5df69fab 167 protected getWorkerTaskWaitTime (workerNodeKey: number): number {
932fc8be 168 return this.taskStatisticsRequirements.waitTime.median
71514351
JB
169 ? this.pool.workerNodes[workerNodeKey].usage.waitTime?.median ?? 0
170 : this.pool.workerNodes[workerNodeKey].usage.waitTime?.average ?? 0
ef680bb8
JB
171 }
172
5df69fab
JB
173 /**
174 * Gets the worker 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.
179 * @returns The worker task ELU.
180 */
181 protected getWorkerTaskElu (workerNodeKey: number): number {
182 return this.taskStatisticsRequirements.elu.median
71514351
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
0bbf65c3
JB
187 protected computeDefaultWorkerWeight (): number {
188 let cpusCycleTimeWeight = 0
189 for (const cpu of cpus()) {
190 // CPU estimated cycle time
191 const numberOfDigits = cpu.speed.toString().length - 1
192 const cpuCycleTime = 1 / (cpu.speed / Math.pow(10, numberOfDigits))
193 cpusCycleTimeWeight += cpuCycleTime * Math.pow(10, numberOfDigits)
194 }
195 return Math.round(cpusCycleTimeWeight / cpus().length)
196 }
197
1d6d93ce
JB
198 // /**
199 // * Finds the first free worker node key based on the number of tasks the worker has applied.
200 // *
201 // * If a worker is found with `0` executing tasks, it is detected as free and its worker node key is returned.
202 // *
203 // * If no free worker is found, `-1` is returned.
204 // *
205 // * @returns A worker node key if there is one, `-1` otherwise.
206 // */
207 // private findFirstFreeWorkerNodeKey (): number {
208 // return this.pool.workerNodes.findIndex(workerNode => {
465b2940 209 // return workerNode.usage.tasks.executing === 0
1d6d93ce
JB
210 // })
211 // }
212
213 // /**
214 // * Finds the last free worker node key based on the number of tasks the worker has applied.
215 // *
216 // * If a worker is found with `0` executing tasks, it is detected as free and its worker node key is returned.
217 // *
218 // * If no free worker is found, `-1` is returned.
219 // *
220 // * @returns A worker node key if there is one, `-1` otherwise.
221 // */
222 // private findLastFreeWorkerNodeKey (): number {
223 // // It requires node >= 18.0.0:
465b2940
JB
224 // // return this.pool.workerNodes.findLastIndex(workerNode => {
225 // // return workerNode.usage.tasks.executing === 0
1d6d93ce
JB
226 // // })
227 // for (
228 // let workerNodeKey = this.pool.workerNodes.length - 1;
229 // workerNodeKey >= 0;
230 // workerNodeKey--
231 // ) {
465b2940 232 // if (this.pool.workerNodes[workerNodeKey].usage.tasks.executing === 0) {
1d6d93ce
JB
233 // return workerNodeKey
234 // }
235 // }
236 // return -1
237 // }
bdaf31cd 238}