Commit | Line | Data |
---|---|---|
feec6e8c JB |
1 | import type { IWorker } from '../worker' |
2 | import type { IPool } from '../pool' | |
3 | import { DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS } from '../../utils' | |
4 | import { AbstractWorkerChoiceStrategy } from './abstract-worker-choice-strategy' | |
5 | import type { | |
6 | IWorkerChoiceStrategy, | |
feec6e8c JB |
7 | WorkerChoiceStrategyOptions |
8 | } from './selection-strategies-types' | |
9 | ||
10 | /** | |
11 | * Selects the next worker with an interleaved weighted round robin scheduling algorithm. | |
12 | * | |
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 { | |
feec6e8c | 24 | /** |
d33be430 | 25 | * Round id. |
feec6e8c JB |
26 | * This is used to determine the current round weight. |
27 | */ | |
d33be430 | 28 | private roundId: number = 0 |
feec6e8c JB |
29 | /** |
30 | * Round weights. | |
31 | */ | |
e4854a4e | 32 | private roundWeights: number[] |
feec6e8c JB |
33 | /** |
34 | * Default worker weight. | |
35 | */ | |
36 | private readonly defaultWorkerWeight: number | |
37 | ||
38 | /** @inheritDoc */ | |
39 | public constructor ( | |
40 | pool: IPool<Worker, Data, Response>, | |
41 | opts: WorkerChoiceStrategyOptions = DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS | |
42 | ) { | |
43 | super(pool, opts) | |
932fc8be | 44 | this.setTaskStatisticsRequirements(this.opts) |
feec6e8c JB |
45 | this.defaultWorkerWeight = this.computeDefaultWorkerWeight() |
46 | this.roundWeights = this.getRoundWeights() | |
47 | } | |
48 | ||
49 | /** @inheritDoc */ | |
50 | public reset (): boolean { | |
39a43af7 | 51 | this.resetWorkerNodeKeyProperties() |
d33be430 | 52 | this.roundId = 0 |
feec6e8c JB |
53 | return true |
54 | } | |
55 | ||
56 | /** @inheritDoc */ | |
57 | public update (): boolean { | |
58 | return true | |
59 | } | |
60 | ||
61 | /** @inheritDoc */ | |
b1aae695 | 62 | public choose (): number | undefined { |
0bc68267 | 63 | let roundId!: number |
297f3bbe JB |
64 | let workerNodeId: number | undefined |
65 | for ( | |
d33be430 | 66 | let roundIndex = this.roundId; |
d3127e84 JB |
67 | roundIndex < this.roundWeights.length; |
68 | roundIndex++ | |
297f3bbe | 69 | ) { |
7c7bb289 | 70 | roundId = roundIndex |
feec6e8c | 71 | for ( |
7c7bb289 JB |
72 | let workerNodeKey = |
73 | this.nextWorkerNodeKey ?? this.previousWorkerNodeKey; | |
297f3bbe JB |
74 | workerNodeKey < this.pool.workerNodes.length; |
75 | workerNodeKey++ | |
feec6e8c | 76 | ) { |
297f3bbe JB |
77 | const workerWeight = |
78 | this.opts.weights?.[workerNodeKey] ?? this.defaultWorkerWeight | |
3f7d99f5 | 79 | if ( |
8990357d | 80 | this.isWorkerNodeEligible(workerNodeKey) && |
3f7d99f5 JB |
81 | workerWeight >= this.roundWeights[roundIndex] |
82 | ) { | |
297f3bbe JB |
83 | workerNodeId = workerNodeKey |
84 | break | |
feec6e8c JB |
85 | } |
86 | } | |
297f3bbe | 87 | } |
7c7bb289 JB |
88 | this.roundId = roundId |
89 | if (workerNodeId == null) { | |
90 | this.previousWorkerNodeKey = | |
91 | this.nextWorkerNodeKey ?? this.previousWorkerNodeKey | |
92 | } | |
b1aae695 | 93 | this.nextWorkerNodeKey = workerNodeId |
9b106837 JB |
94 | const chosenWorkerNodeKey = this.nextWorkerNodeKey |
95 | if (this.nextWorkerNodeKey === this.pool.workerNodes.length - 1) { | |
96 | this.nextWorkerNodeKey = 0 | |
d33be430 JB |
97 | this.roundId = |
98 | this.roundId === this.roundWeights.length - 1 ? 0 : this.roundId + 1 | |
297f3bbe | 99 | } else { |
7c7bb289 JB |
100 | this.nextWorkerNodeKey = |
101 | (this.nextWorkerNodeKey ?? this.previousWorkerNodeKey) + 1 | |
feec6e8c JB |
102 | } |
103 | return chosenWorkerNodeKey | |
104 | } | |
105 | ||
106 | /** @inheritDoc */ | |
107 | public remove (workerNodeKey: number): boolean { | |
9b106837 | 108 | if (this.nextWorkerNodeKey === workerNodeKey) { |
feec6e8c | 109 | if (this.pool.workerNodes.length === 0) { |
9b106837 JB |
110 | this.nextWorkerNodeKey = 0 |
111 | } else if (this.nextWorkerNodeKey > this.pool.workerNodes.length - 1) { | |
112 | this.nextWorkerNodeKey = this.pool.workerNodes.length - 1 | |
d33be430 JB |
113 | this.roundId = |
114 | this.roundId === this.roundWeights.length - 1 ? 0 : this.roundId + 1 | |
feec6e8c JB |
115 | } |
116 | } | |
117 | return true | |
118 | } | |
119 | ||
e4854a4e JB |
120 | /** @inheritDoc */ |
121 | public setOptions (opts: WorkerChoiceStrategyOptions): void { | |
122 | super.setOptions(opts) | |
123 | this.roundWeights = this.getRoundWeights() | |
124 | } | |
125 | ||
feec6e8c JB |
126 | private getRoundWeights (): number[] { |
127 | if (this.opts.weights == null) { | |
128 | return [this.defaultWorkerWeight] | |
129 | } | |
130 | return [ | |
131 | ...new Set( | |
132 | Object.values(this.opts.weights) | |
133 | .slice() | |
134 | .sort((a, b) => a - b) | |
135 | ) | |
136 | ] | |
137 | } | |
138 | } |