docs: add scope on pool public API
[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,
57441b79 10 MeasurementStatisticsRequirements,
6c6afb84 11 StrategyPolicy,
87de9ff5 12 TaskStatisticsRequirements,
da309861 13 WorkerChoiceStrategyOptions
10fcfaf4 14} from './selection-strategies-types'
bdaf31cd
JB
15
16/**
9cd39dd4 17 * Worker choice strategy abstract base class.
bdaf31cd 18 *
38e795c1 19 * @typeParam Worker - Type of worker which manages the strategy.
e102732c
JB
20 * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
21 * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
bdaf31cd
JB
22 */
23export abstract class AbstractWorkerChoiceStrategy<
f06e48d8 24 Worker extends IWorker,
b2b1d84e
JB
25 Data = unknown,
26 Response = unknown
17393ac8 27> implements IWorkerChoiceStrategy {
d33be430 28 /**
9b106837 29 * The next worker node key.
d33be430 30 */
9b106837 31 protected nextWorkerNodeKey: number = 0
d33be430 32
6c6afb84
JB
33 /** @inheritDoc */
34 public readonly strategyPolicy: StrategyPolicy = {
35 useDynamicWorker: false
36 }
37
afc003b2 38 /** @inheritDoc */
87de9ff5 39 public readonly taskStatisticsRequirements: TaskStatisticsRequirements = {
3c93feb9
JB
40 runTime: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
41 waitTime: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
42 elu: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS
10fcfaf4 43 }
bdaf31cd
JB
44
45 /**
6533c3e6 46 * Constructs a worker choice strategy bound to the pool.
bdaf31cd 47 *
38e795c1 48 * @param pool - The pool instance.
da309861 49 * @param opts - The worker choice strategy options.
bdaf31cd
JB
50 */
51 public constructor (
c4855468 52 protected readonly pool: IPool<Worker, Data, Response>,
a20f0ba5 53 protected opts: WorkerChoiceStrategyOptions = DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
b8f3418c 54 ) {
7254e419 55 this.choose = this.choose.bind(this)
b8f3418c 56 }
bdaf31cd 57
932fc8be
JB
58 protected setTaskStatisticsRequirements (
59 opts: WorkerChoiceStrategyOptions
60 ): void {
57441b79
JB
61 this.toggleMedianMeasurementStatisticsRequirements(
62 this.taskStatisticsRequirements.runTime,
63 opts.runTime?.median as boolean
64 )
65 this.toggleMedianMeasurementStatisticsRequirements(
66 this.taskStatisticsRequirements.waitTime,
67 opts.waitTime?.median as boolean
68 )
69 this.toggleMedianMeasurementStatisticsRequirements(
70 this.taskStatisticsRequirements.elu,
71 opts.elu?.median as boolean
72 )
73 }
74
75 private toggleMedianMeasurementStatisticsRequirements (
76 measurementStatisticsRequirements: MeasurementStatisticsRequirements,
77 toggleMedian: boolean
78 ): void {
79 if (measurementStatisticsRequirements.average && toggleMedian) {
80 measurementStatisticsRequirements.average = false
81 measurementStatisticsRequirements.median = toggleMedian
5df69fab 82 }
57441b79
JB
83 if (measurementStatisticsRequirements.median && !toggleMedian) {
84 measurementStatisticsRequirements.average = true
85 measurementStatisticsRequirements.median = toggleMedian
5df69fab 86 }
da309861
JB
87 }
88
afc003b2 89 /** @inheritDoc */
a6f7f1b4 90 public abstract reset (): boolean
ea7a90d3 91
138d29a8 92 /** @inheritDoc */
a4958de2 93 public abstract update (workerNodeKey: number): boolean
138d29a8 94
afc003b2 95 /** @inheritDoc */
c923ce56 96 public abstract choose (): number
97a2abc3 97
afc003b2 98 /** @inheritDoc */
f06e48d8 99 public abstract remove (workerNodeKey: number): boolean
a20f0ba5
JB
100
101 /** @inheritDoc */
102 public setOptions (opts: WorkerChoiceStrategyOptions): void {
65ab8dcc
JB
103 this.opts = opts ?? DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
104 this.setTaskStatisticsRequirements(this.opts)
a20f0ba5 105 }
cb70b19d 106
d4ddb68b
JB
107 /**
108 * Whether the worker node is ready or not.
109 *
110 * @param workerNodeKey - The worker node key.
111 * @returns Whether the worker node is ready or not.
112 */
9b106837 113 protected isWorkerNodeReady (workerNodeKey: number): boolean {
19dbc45b
JB
114 return this.pool.workerNodes[workerNodeKey].info.ready
115 }
116
e2b31e32
JB
117 /**
118 * Whether the worker node has back pressure or not (i.e. its tasks queue is full).
119 *
120 * @param workerNodeKey - The worker node key.
121 * @returns `true` if the worker node has back pressure, `false` otherwise.
122 */
123 protected hasWorkerNodeBackPressure (workerNodeKey: number): boolean {
124 return this.pool.hasWorkerNodeBackPressure(workerNodeKey)
125 }
126
f6b641d6 127 /**
e6606302 128 * Gets the worker task runtime.
932fc8be
JB
129 * If the task statistics require the average runtime, the average runtime is returned.
130 * If the task statistics require the median runtime , the median runtime is returned.
f6b641d6
JB
131 *
132 * @param workerNodeKey - The worker node key.
e6606302 133 * @returns The worker task runtime.
f6b641d6
JB
134 */
135 protected getWorkerTaskRunTime (workerNodeKey: number): number {
932fc8be 136 return this.taskStatisticsRequirements.runTime.median
71514351
JB
137 ? this.pool.workerNodes[workerNodeKey].usage.runTime?.median ?? 0
138 : this.pool.workerNodes[workerNodeKey].usage.runTime?.average ?? 0
f6b641d6
JB
139 }
140
ef680bb8
JB
141 /**
142 * Gets the worker task wait time.
932fc8be
JB
143 * If the task statistics require the average wait time, the average wait time is returned.
144 * If the task statistics require the median wait time, the median wait time is returned.
ef680bb8
JB
145 *
146 * @param workerNodeKey - The worker node key.
147 * @returns The worker task wait time.
148 */
5df69fab 149 protected getWorkerTaskWaitTime (workerNodeKey: number): number {
932fc8be 150 return this.taskStatisticsRequirements.waitTime.median
71514351
JB
151 ? this.pool.workerNodes[workerNodeKey].usage.waitTime?.median ?? 0
152 : this.pool.workerNodes[workerNodeKey].usage.waitTime?.average ?? 0
ef680bb8
JB
153 }
154
5df69fab
JB
155 /**
156 * Gets the worker task ELU.
9adcefab
JB
157 * If the task statistics require the average ELU, the average ELU is returned.
158 * If the task statistics require the median ELU, the median ELU is returned.
5df69fab
JB
159 *
160 * @param workerNodeKey - The worker node key.
161 * @returns The worker task ELU.
162 */
163 protected getWorkerTaskElu (workerNodeKey: number): number {
164 return this.taskStatisticsRequirements.elu.median
71514351
JB
165 ? this.pool.workerNodes[workerNodeKey].usage.elu.active?.median ?? 0
166 : this.pool.workerNodes[workerNodeKey].usage.elu.active?.average ?? 0
5df69fab
JB
167 }
168
0bbf65c3
JB
169 protected computeDefaultWorkerWeight (): number {
170 let cpusCycleTimeWeight = 0
171 for (const cpu of cpus()) {
172 // CPU estimated cycle time
173 const numberOfDigits = cpu.speed.toString().length - 1
174 const cpuCycleTime = 1 / (cpu.speed / Math.pow(10, numberOfDigits))
175 cpusCycleTimeWeight += cpuCycleTime * Math.pow(10, numberOfDigits)
176 }
177 return Math.round(cpusCycleTimeWeight / cpus().length)
178 }
bdaf31cd 179}