Commit | Line | Data |
---|---|---|
d35e5717 | 1 | import type { IPool } from '../pool.js' |
e9ed6eee | 2 | import { DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS } from '../utils.js' |
ded253e2 | 3 | import type { IWorker } from '../worker.js' |
d35e5717 | 4 | import { AbstractWorkerChoiceStrategy } from './abstract-worker-choice-strategy.js' |
feec6e8c JB |
5 | import type { |
6 | IWorkerChoiceStrategy, | |
39618ede | 7 | TaskStatisticsRequirements, |
3a502712 | 8 | WorkerChoiceStrategyOptions, |
d35e5717 | 9 | } from './selection-strategies-types.js' |
feec6e8c JB |
10 | |
11 | /** | |
12 | * Selects the next worker with an interleaved weighted round robin scheduling algorithm. | |
feec6e8c | 13 | * @typeParam Worker - Type of worker which manages the strategy. |
e102732c JB |
14 | * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data. |
15 | * @typeParam Response - Type of execution response. This can only be structured-cloneable data. | |
feec6e8c JB |
16 | */ |
17 | export class InterleavedWeightedRoundRobinWorkerChoiceStrategy< | |
18 | Worker extends IWorker, | |
19 | Data = unknown, | |
20 | Response = unknown | |
21 | > | |
22 | extends AbstractWorkerChoiceStrategy<Worker, Data, Response> | |
23 | implements IWorkerChoiceStrategy { | |
619f403b JB |
24 | /** @inheritDoc */ |
25 | public readonly taskStatisticsRequirements: TaskStatisticsRequirements = { | |
26 | runTime: { | |
27 | aggregate: true, | |
28 | average: true, | |
3a502712 | 29 | median: false, |
619f403b | 30 | }, |
e0843544 JB |
31 | waitTime: { |
32 | aggregate: true, | |
33 | average: true, | |
3a502712 | 34 | median: false, |
e0843544 | 35 | }, |
3a502712 | 36 | elu: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS, |
619f403b JB |
37 | } |
38 | ||
feec6e8c | 39 | /** |
d33be430 | 40 | * Round id. |
feec6e8c | 41 | */ |
c63a35a0 | 42 | private roundId = 0 |
feec6e8c JB |
43 | /** |
44 | * Round weights. | |
45 | */ | |
b2365b8a | 46 | private roundWeights: number[] |
feec6e8c | 47 | /** |
619f403b | 48 | * Worker node id. |
feec6e8c | 49 | */ |
c63a35a0 | 50 | private workerNodeId = 0 |
619f403b | 51 | /** |
e0843544 | 52 | * Worker node virtual execution time. |
619f403b | 53 | */ |
e0843544 | 54 | private workerNodeVirtualTaskExecutionTime = 0 |
feec6e8c JB |
55 | |
56 | /** @inheritDoc */ | |
57 | public constructor ( | |
58 | pool: IPool<Worker, Data, Response>, | |
39618ede | 59 | opts?: WorkerChoiceStrategyOptions |
feec6e8c JB |
60 | ) { |
61 | super(pool, opts) | |
3d6f0f73 JB |
62 | this.setTaskStatisticsRequirements(this.opts) |
63 | this.roundWeights = this.getRoundWeights() | |
feec6e8c JB |
64 | } |
65 | ||
66 | /** @inheritDoc */ | |
67 | public reset (): boolean { | |
39a43af7 | 68 | this.resetWorkerNodeKeyProperties() |
d33be430 | 69 | this.roundId = 0 |
619f403b | 70 | this.workerNodeId = 0 |
e0843544 | 71 | this.workerNodeVirtualTaskExecutionTime = 0 |
feec6e8c JB |
72 | return true |
73 | } | |
74 | ||
75 | /** @inheritDoc */ | |
76 | public update (): boolean { | |
77 | return true | |
78 | } | |
79 | ||
80 | /** @inheritDoc */ | |
b1aae695 | 81 | public choose (): number | undefined { |
297f3bbe | 82 | for ( |
d33be430 | 83 | let roundIndex = this.roundId; |
d3127e84 JB |
84 | roundIndex < this.roundWeights.length; |
85 | roundIndex++ | |
297f3bbe | 86 | ) { |
619f403b | 87 | this.roundId = roundIndex |
feec6e8c | 88 | for ( |
619f403b | 89 | let workerNodeKey = this.workerNodeId; |
297f3bbe JB |
90 | workerNodeKey < this.pool.workerNodes.length; |
91 | workerNodeKey++ | |
feec6e8c | 92 | ) { |
619f403b | 93 | this.workerNodeId = workerNodeKey |
619f403b JB |
94 | if ( |
95 | this.workerNodeId !== this.nextWorkerNodeKey && | |
e0843544 | 96 | this.workerNodeVirtualTaskExecutionTime !== 0 |
619f403b | 97 | ) { |
e0843544 | 98 | this.workerNodeVirtualTaskExecutionTime = 0 |
619f403b | 99 | } |
67f3f2d6 | 100 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
7f0e1334 | 101 | const workerWeight = this.opts!.weights![workerNodeKey] |
619f403b | 102 | if ( |
97256a85 | 103 | this.isWorkerNodeReady(workerNodeKey) && |
619f403b | 104 | workerWeight >= this.roundWeights[roundIndex] && |
e0843544 | 105 | this.workerNodeVirtualTaskExecutionTime < workerWeight |
619f403b | 106 | ) { |
e0843544 JB |
107 | this.workerNodeVirtualTaskExecutionTime += |
108 | this.getWorkerNodeTaskWaitTime(workerNodeKey) + | |
f3a91bac | 109 | this.getWorkerNodeTaskRunTime(workerNodeKey) |
baca80f7 | 110 | this.setPreviousWorkerNodeKey(this.nextWorkerNodeKey) |
619f403b JB |
111 | this.nextWorkerNodeKey = workerNodeKey |
112 | return this.nextWorkerNodeKey | |
feec6e8c JB |
113 | } |
114 | } | |
297f3bbe | 115 | } |
619f403b JB |
116 | this.interleavedWeightedRoundRobinNextWorkerNodeId() |
117 | } | |
118 | ||
119 | private interleavedWeightedRoundRobinNextWorkerNodeId (): void { | |
8e8d9101 JB |
120 | if (this.pool.workerNodes.length === 0) { |
121 | this.workerNodeId = 0 | |
122 | } else if ( | |
a38b62f1 JB |
123 | this.roundId === this.roundWeights.length - 1 && |
124 | this.workerNodeId === this.pool.workerNodes.length - 1 | |
125 | ) { | |
126 | this.roundId = 0 | |
127 | this.workerNodeId = 0 | |
128 | } else if (this.workerNodeId === this.pool.workerNodes.length - 1) { | |
129 | this.roundId = this.roundId + 1 | |
130 | this.workerNodeId = 0 | |
131 | } else { | |
132 | this.workerNodeId = this.workerNodeId + 1 | |
133 | } | |
feec6e8c JB |
134 | } |
135 | ||
136 | /** @inheritDoc */ | |
137 | public remove (workerNodeKey: number): boolean { | |
226b02a3 | 138 | if (this.pool.workerNodes.length === 0) { |
153179f2 JB |
139 | this.resetWorkerNodeKeyProperties() |
140 | this.workerNodeId = 0 | |
e0843544 | 141 | this.workerNodeVirtualTaskExecutionTime = 0 |
153179f2 | 142 | return true |
226b02a3 JB |
143 | } |
144 | if ( | |
145 | this.workerNodeId === workerNodeKey && | |
146 | this.workerNodeId > this.pool.workerNodes.length - 1 | |
147 | ) { | |
148 | this.workerNodeId = this.pool.workerNodes.length - 1 | |
149 | } | |
150 | if ( | |
151 | this.previousWorkerNodeKey === workerNodeKey && | |
152 | this.previousWorkerNodeKey > this.pool.workerNodes.length - 1 | |
153 | ) { | |
154 | this.previousWorkerNodeKey = this.pool.workerNodes.length - 1 | |
feec6e8c JB |
155 | } |
156 | return true | |
157 | } | |
158 | ||
e4854a4e | 159 | /** @inheritDoc */ |
39618ede | 160 | public setOptions (opts: WorkerChoiceStrategyOptions | undefined): void { |
e4854a4e JB |
161 | super.setOptions(opts) |
162 | this.roundWeights = this.getRoundWeights() | |
163 | } | |
164 | ||
feec6e8c | 165 | private getRoundWeights (): number[] { |
feec6e8c JB |
166 | return [ |
167 | ...new Set( | |
67f3f2d6 | 168 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
39618ede | 169 | Object.values(this.opts!.weights!) |
feec6e8c | 170 | .slice() |
b808b625 | 171 | .sort((a, b) => a - b) |
3a502712 | 172 | ), |
feec6e8c JB |
173 | ] |
174 | } | |
175 | } |