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