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 structured-cloneable data.
17 * @typeParam Response - Type of execution response. This can only be structured-cloneable 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 this.opts
= opts
?? DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
137 this.setTaskStatisticsRequirements(this.opts
)
141 // * Finds a free worker node key.
143 // * @returns The free worker node key or `-1` if there is no free worker node.
145 // protected findFreeWorkerNodeKey (): number {
146 // if (this.toggleFindLastFreeWorkerNodeKey) {
147 // this.toggleFindLastFreeWorkerNodeKey = false
148 // return this.findLastFreeWorkerNodeKey()
150 // this.toggleFindLastFreeWorkerNodeKey = true
151 // return this.findFirstFreeWorkerNodeKey()
155 * Gets the worker task runtime.
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.
159 * @param workerNodeKey - The worker node key.
160 * @returns The worker task runtime.
162 protected getWorkerTaskRunTime (workerNodeKey
: number): number {
163 return this.taskStatisticsRequirements
.runTime
.median
164 ? this.pool
.workerNodes
[workerNodeKey
].workerUsage
.runTime
.median
165 : this.pool
.workerNodes
[workerNodeKey
].workerUsage
.runTime
.average
169 * Gets the worker task wait time.
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.
173 * @param workerNodeKey - The worker node key.
174 * @returns The worker task wait time.
176 protected getWorkerTaskWaitTime (workerNodeKey
: number): number {
177 return this.taskStatisticsRequirements
.waitTime
.median
178 ? this.pool
.workerNodes
[workerNodeKey
].workerUsage
.waitTime
.median
179 : this.pool
.workerNodes
[workerNodeKey
].workerUsage
.waitTime
.average
183 * Gets the worker task ELU.
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.
187 * @param workerNodeKey - The worker node key.
188 * @returns The worker task ELU.
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
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
)
204 return Math.round(cpusCycleTimeWeight
/ cpus().length
)
208 // * Finds the first free worker node key based on the number of tasks the worker has applied.
210 // * If a worker is found with `0` executing tasks, it is detected as free and its worker node key is returned.
212 // * If no free worker is found, `-1` is returned.
214 // * @returns A worker node key if there is one, `-1` otherwise.
216 // private findFirstFreeWorkerNodeKey (): number {
217 // return this.pool.workerNodes.findIndex(workerNode => {
218 // return workerNode.workerUsage.tasks.executing === 0
223 // * Finds the last free worker node key based on the number of tasks the worker has applied.
225 // * If a worker is found with `0` executing tasks, it is detected as free and its worker node key is returned.
227 // * If no free worker is found, `-1` is returned.
229 // * @returns A worker node key if there is one, `-1` otherwise.
231 // private findLastFreeWorkerNodeKey (): number {
232 // // It requires node >= 18.0.0:
233 // // return this.workerNodes.findLastIndex(workerNode => {
234 // // return workerNode.workerUsage.tasks.executing === 0
237 // let workerNodeKey = this.pool.workerNodes.length - 1;
238 // workerNodeKey >= 0;
242 // this.pool.workerNodes[workerNodeKey].workerUsage.tasks.executing === 0
244 // return workerNodeKey