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