]>
Commit | Line | Data |
---|---|---|
1 | import type { IPool } from '../pool.js' | |
2 | import type { IWorker } from '../worker.js' | |
3 | import type { | |
4 | IWorkerChoiceStrategy, | |
5 | StrategyPolicy, | |
6 | TaskStatisticsRequirements, | |
7 | WorkerChoiceStrategyOptions, | |
8 | } from './selection-strategies-types.js' | |
9 | ||
10 | import { DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS } from '../utils.js' | |
11 | import { | |
12 | buildWorkerChoiceStrategyOptions, | |
13 | toggleMedianMeasurementStatisticsRequirements, | |
14 | } from './selection-strategies-utils.js' | |
15 | ||
16 | /** | |
17 | * Worker choice strategy abstract base class. | |
18 | * @typeParam Worker - Type of worker which manages the strategy. | |
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. | |
21 | */ | |
22 | export abstract class AbstractWorkerChoiceStrategy< | |
23 | Worker extends IWorker, | |
24 | Data = unknown, | |
25 | Response = unknown | |
26 | > implements IWorkerChoiceStrategy { | |
27 | /** @inheritDoc */ | |
28 | public readonly strategyPolicy: StrategyPolicy = Object.freeze({ | |
29 | dynamicWorkerReady: true, | |
30 | dynamicWorkerUsage: false, | |
31 | }) | |
32 | ||
33 | /** @inheritDoc */ | |
34 | public readonly taskStatisticsRequirements: TaskStatisticsRequirements = | |
35 | Object.freeze({ | |
36 | elu: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS, | |
37 | runTime: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS, | |
38 | waitTime: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS, | |
39 | }) | |
40 | ||
41 | /** | |
42 | * The next worker node key. | |
43 | */ | |
44 | protected nextWorkerNodeKey: number | undefined = 0 | |
45 | ||
46 | /** | |
47 | * The previous worker node key. | |
48 | */ | |
49 | protected previousWorkerNodeKey = 0 | |
50 | ||
51 | /** | |
52 | * Constructs a worker choice strategy bound to the pool. | |
53 | * @param pool - The pool instance. | |
54 | * @param opts - The worker choice strategy options. | |
55 | */ | |
56 | public constructor ( | |
57 | protected readonly pool: IPool<Worker, Data, Response>, | |
58 | protected opts?: WorkerChoiceStrategyOptions | |
59 | ) { | |
60 | this.choose = this.choose.bind(this) | |
61 | this.setOptions(this.opts) | |
62 | } | |
63 | ||
64 | /** @inheritDoc */ | |
65 | public abstract choose (): number | undefined | |
66 | ||
67 | /** @inheritDoc */ | |
68 | public abstract remove (workerNodeKey: number): boolean | |
69 | ||
70 | /** @inheritDoc */ | |
71 | public abstract reset (): boolean | |
72 | ||
73 | /** @inheritDoc */ | |
74 | public setOptions (opts: undefined | WorkerChoiceStrategyOptions): void { | |
75 | this.opts = buildWorkerChoiceStrategyOptions<Worker, Data, Response>( | |
76 | this.pool, | |
77 | opts | |
78 | ) | |
79 | this.setTaskStatisticsRequirements(this.opts) | |
80 | } | |
81 | ||
82 | /** @inheritDoc */ | |
83 | public abstract update (workerNodeKey: number): boolean | |
84 | ||
85 | /** | |
86 | * Check the next worker node key. | |
87 | */ | |
88 | protected checkNextWorkerNodeKey (): void { | |
89 | if ( | |
90 | this.nextWorkerNodeKey != null && | |
91 | (this.nextWorkerNodeKey < 0 || | |
92 | !this.isWorkerNodeReady(this.nextWorkerNodeKey)) | |
93 | ) { | |
94 | delete this.nextWorkerNodeKey | |
95 | } | |
96 | } | |
97 | ||
98 | /** | |
99 | * Gets the worker node task ELU. | |
100 | * If the task statistics require the average ELU, the average ELU is returned. | |
101 | * If the task statistics require the median ELU, the median ELU is returned. | |
102 | * @param workerNodeKey - The worker node key. | |
103 | * @returns The worker node task ELU. | |
104 | */ | |
105 | protected getWorkerNodeTaskElu (workerNodeKey: number): number { | |
106 | return this.taskStatisticsRequirements.elu.median | |
107 | ? (this.pool.workerNodes[workerNodeKey]?.usage.elu.active.median ?? 0) | |
108 | : (this.pool.workerNodes[workerNodeKey]?.usage.elu.active.average ?? 0) | |
109 | } | |
110 | ||
111 | /** | |
112 | * Gets the worker node task runtime. | |
113 | * If the task statistics require the average runtime, the average runtime is returned. | |
114 | * If the task statistics require the median runtime, the median runtime is returned. | |
115 | * @param workerNodeKey - The worker node key. | |
116 | * @returns The worker node task runtime. | |
117 | */ | |
118 | protected getWorkerNodeTaskRunTime (workerNodeKey: number): number { | |
119 | return this.taskStatisticsRequirements.runTime.median | |
120 | ? (this.pool.workerNodes[workerNodeKey]?.usage.runTime.median ?? 0) | |
121 | : (this.pool.workerNodes[workerNodeKey]?.usage.runTime.average ?? 0) | |
122 | } | |
123 | ||
124 | /** | |
125 | * Gets the worker node task wait time. | |
126 | * If the task statistics require the average wait time, the average wait time is returned. | |
127 | * If the task statistics require the median wait time, the median wait time is returned. | |
128 | * @param workerNodeKey - The worker node key. | |
129 | * @returns The worker node task wait time. | |
130 | */ | |
131 | protected getWorkerNodeTaskWaitTime (workerNodeKey: number): number { | |
132 | return this.taskStatisticsRequirements.waitTime.median | |
133 | ? (this.pool.workerNodes[workerNodeKey]?.usage.waitTime.median ?? 0) | |
134 | : (this.pool.workerNodes[workerNodeKey]?.usage.waitTime.average ?? 0) | |
135 | } | |
136 | ||
137 | /** | |
138 | * Whether the worker node is ready or not. | |
139 | * @param workerNodeKey - The worker node key. | |
140 | * @returns Whether the worker node is ready or not. | |
141 | */ | |
142 | protected isWorkerNodeReady (workerNodeKey: number): boolean { | |
143 | return this.pool.workerNodes[workerNodeKey]?.info.ready ?? false | |
144 | } | |
145 | ||
146 | protected resetWorkerNodeKeyProperties (): void { | |
147 | this.nextWorkerNodeKey = 0 | |
148 | this.previousWorkerNodeKey = 0 | |
149 | } | |
150 | ||
151 | /** | |
152 | * Sets safely the previous worker node key. | |
153 | * @param workerNodeKey - The worker node key. | |
154 | */ | |
155 | protected setPreviousWorkerNodeKey (workerNodeKey: number | undefined): void { | |
156 | this.previousWorkerNodeKey = | |
157 | workerNodeKey != null && workerNodeKey >= 0 | |
158 | ? workerNodeKey | |
159 | : this.previousWorkerNodeKey | |
160 | } | |
161 | ||
162 | protected setTaskStatisticsRequirements ( | |
163 | opts: undefined | WorkerChoiceStrategyOptions | |
164 | ): void { | |
165 | toggleMedianMeasurementStatisticsRequirements( | |
166 | this.taskStatisticsRequirements.runTime, | |
167 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
168 | opts!.runTime!.median | |
169 | ) | |
170 | toggleMedianMeasurementStatisticsRequirements( | |
171 | this.taskStatisticsRequirements.waitTime, | |
172 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
173 | opts!.waitTime!.median | |
174 | ) | |
175 | toggleMedianMeasurementStatisticsRequirements( | |
176 | this.taskStatisticsRequirements.elu, | |
177 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
178 | opts!.elu!.median | |
179 | ) | |
180 | } | |
181 | } |