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