1 import { cpus
} from
'node:os'
2 import { DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
} from
'../../utils'
3 import type { IPool
} from
'../pool'
4 import type { IWorker
} from
'../worker'
8 TaskStatisticsRequirements
,
9 WorkerChoiceStrategyOptions
10 } from
'./selection-strategies-types'
13 * Worker choice strategy abstract base class.
15 * @typeParam Worker - Type of worker which manages the strategy.
16 * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
17 * @typeParam Response - Type of execution response. This can only be serializable data.
19 export abstract class AbstractWorkerChoiceStrategy
<
20 Worker
extends IWorker
,
23 > implements IWorkerChoiceStrategy
{
25 // * Toggles finding the last free worker node key.
27 // private toggleFindLastFreeWorkerNodeKey: boolean = false
30 * Id of the next worker node.
32 protected nextWorkerNodeId
: number = 0
35 public readonly strategyPolicy
: StrategyPolicy
= {
36 useDynamicWorker
: false
40 public readonly taskStatisticsRequirements
: TaskStatisticsRequirements
= {
59 * Constructs a worker choice strategy bound to the pool.
61 * @param pool - The pool instance.
62 * @param opts - The worker choice strategy options.
65 protected readonly pool
: IPool
<Worker
, Data
, Response
>,
66 protected opts
: WorkerChoiceStrategyOptions
= DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
68 this.choose
= this.choose
.bind(this)
71 protected setTaskStatisticsRequirements (
72 opts
: WorkerChoiceStrategyOptions
75 this.taskStatisticsRequirements
.runTime
.average
&&
76 opts
.runTime
?.median
=== true
78 this.taskStatisticsRequirements
.runTime
.average
= false
79 this.taskStatisticsRequirements
.runTime
.median
= opts
.runTime
83 this.taskStatisticsRequirements
.runTime
.median
&&
84 opts
.runTime
?.median
=== false
86 this.taskStatisticsRequirements
.runTime
.average
= true
87 this.taskStatisticsRequirements
.runTime
.median
= opts
.runTime
91 this.taskStatisticsRequirements
.waitTime
.average
&&
92 opts
.waitTime
?.median
=== true
94 this.taskStatisticsRequirements
.waitTime
.average
= false
95 this.taskStatisticsRequirements
.waitTime
.median
= opts
.waitTime
99 this.taskStatisticsRequirements
.waitTime
.median
&&
100 opts
.waitTime
?.median
=== false
102 this.taskStatisticsRequirements
.waitTime
.average
= true
103 this.taskStatisticsRequirements
.waitTime
.median
= opts
.waitTime
107 this.taskStatisticsRequirements
.elu
.average
&&
108 opts
.elu
?.median
=== true
110 this.taskStatisticsRequirements
.elu
.average
= false
111 this.taskStatisticsRequirements
.elu
.median
= opts
.elu
.median
as boolean
114 this.taskStatisticsRequirements
.elu
.median
&&
115 opts
.elu
?.median
=== false
117 this.taskStatisticsRequirements
.elu
.average
= true
118 this.taskStatisticsRequirements
.elu
.median
= opts
.elu
.median
as boolean
123 public abstract reset (): boolean
126 public abstract update (workerNodeKey
: number): boolean
129 public abstract choose (): number
132 public abstract remove (workerNodeKey
: number): boolean
135 public setOptions (opts
: WorkerChoiceStrategyOptions
): void {
136 opts
= opts
?? DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
137 this.setTaskStatisticsRequirements(opts
)
142 // * Finds a free worker node key.
144 // * @returns The free worker node key or `-1` if there is no free worker node.
146 // protected findFreeWorkerNodeKey (): number {
147 // if (this.toggleFindLastFreeWorkerNodeKey) {
148 // this.toggleFindLastFreeWorkerNodeKey = false
149 // return this.findLastFreeWorkerNodeKey()
151 // this.toggleFindLastFreeWorkerNodeKey = true
152 // return this.findFirstFreeWorkerNodeKey()
156 * Gets the worker task runtime.
157 * If the task statistics require the average runtime, the average runtime is returned.
158 * If the task statistics require the median runtime , the median runtime is returned.
160 * @param workerNodeKey - The worker node key.
161 * @returns The worker task runtime.
163 protected getWorkerTaskRunTime (workerNodeKey
: number): number {
164 return this.taskStatisticsRequirements
.runTime
.median
165 ? this.pool
.workerNodes
[workerNodeKey
].workerUsage
.runTime
.median
166 : this.pool
.workerNodes
[workerNodeKey
].workerUsage
.runTime
.average
170 * Gets the worker task wait time.
171 * If the task statistics require the average wait time, the average wait time is returned.
172 * If the task statistics require the median wait time, the median wait time is returned.
174 * @param workerNodeKey - The worker node key.
175 * @returns The worker task wait time.
177 protected getWorkerTaskWaitTime (workerNodeKey
: number): number {
178 return this.taskStatisticsRequirements
.waitTime
.median
179 ? this.pool
.workerNodes
[workerNodeKey
].workerUsage
.waitTime
.median
180 : this.pool
.workerNodes
[workerNodeKey
].workerUsage
.waitTime
.average
184 * Gets the worker task ELU.
185 * If the task statistics require the average ELU, the average ELU is returned.
186 * If the task statistics require the median ELU, the median ELU is returned.
188 * @param workerNodeKey - The worker node key.
189 * @returns The worker task ELU.
191 protected getWorkerTaskElu (workerNodeKey
: number): number {
192 return this.taskStatisticsRequirements
.elu
.median
193 ? this.pool
.workerNodes
[workerNodeKey
].workerUsage
.elu
.active
.median
194 : this.pool
.workerNodes
[workerNodeKey
].workerUsage
.elu
.active
.average
197 protected computeDefaultWorkerWeight (): number {
198 let cpusCycleTimeWeight
= 0
199 for (const cpu
of cpus()) {
200 // CPU estimated cycle time
201 const numberOfDigits
= cpu
.speed
.toString().length
- 1
202 const cpuCycleTime
= 1 / (cpu
.speed
/ Math.pow(10, numberOfDigits
))
203 cpusCycleTimeWeight
+= cpuCycleTime
* Math.pow(10, numberOfDigits
)
205 return Math.round(cpusCycleTimeWeight
/ cpus().length
)
209 // * Finds the first free worker node key based on the number of tasks the worker has applied.
211 // * If a worker is found with `0` executing tasks, it is detected as free and its worker node key is returned.
213 // * If no free worker is found, `-1` is returned.
215 // * @returns A worker node key if there is one, `-1` otherwise.
217 // private findFirstFreeWorkerNodeKey (): number {
218 // return this.pool.workerNodes.findIndex(workerNode => {
219 // return workerNode.workerUsage.tasks.executing === 0
224 // * Finds the last free worker node key based on the number of tasks the worker has applied.
226 // * If a worker is found with `0` executing tasks, it is detected as free and its worker node key is returned.
228 // * If no free worker is found, `-1` is returned.
230 // * @returns A worker node key if there is one, `-1` otherwise.
232 // private findLastFreeWorkerNodeKey (): number {
233 // // It requires node >= 18.0.0:
234 // // return this.workerNodes.findLastIndex(workerNode => {
235 // // return workerNode.workerUsage.tasks.executing === 0
238 // let workerNodeKey = this.pool.workerNodes.length - 1;
239 // workerNodeKey >= 0;
243 // this.pool.workerNodes[workerNodeKey].workerUsage.tasks.executing === 0
245 // return workerNodeKey