fix: avoid out of bound at worker node removal
[poolifier.git] / src / pools / selection-strategies / weighted-round-robin-worker-choice-strategy.ts
CommitLineData
f06e48d8 1import type { IWorker } from '../worker'
65d7a1c9 2import type { IPool } from '../pool'
3c93feb9
JB
3import {
4 DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
5 DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
6} from '../../utils'
b3432a63 7import { AbstractWorkerChoiceStrategy } from './abstract-worker-choice-strategy'
bf90656c
JB
8import type {
9 IWorkerChoiceStrategy,
87de9ff5 10 TaskStatisticsRequirements,
da309861 11 WorkerChoiceStrategyOptions
bf90656c 12} from './selection-strategies-types'
b3432a63 13
b3432a63
JB
14/**
15 * Selects the next worker with a weighted round robin scheduling algorithm.
16 * Loosely modeled after the weighted round robin queueing algorithm: https://en.wikipedia.org/wiki/Weighted_round_robin.
17 *
38e795c1 18 * @typeParam Worker - Type of worker which manages the strategy.
e102732c
JB
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.
b3432a63
JB
21 */
22export class WeightedRoundRobinWorkerChoiceStrategy<
f06e48d8 23 Worker extends IWorker,
b2b1d84e
JB
24 Data = unknown,
25 Response = unknown
bf90656c
JB
26 >
27 extends AbstractWorkerChoiceStrategy<Worker, Data, Response>
17393ac8 28 implements IWorkerChoiceStrategy {
afc003b2 29 /** @inheritDoc */
87de9ff5 30 public readonly taskStatisticsRequirements: TaskStatisticsRequirements = {
932fc8be
JB
31 runTime: {
32 aggregate: true,
33 average: true,
34 median: false
35 },
3c93feb9
JB
36 waitTime: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
37 elu: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS
10fcfaf4
JB
38 }
39
b3432a63
JB
40 /**
41 * Default worker weight.
42 */
777af0ac 43 private readonly defaultWorkerWeight: number
b3432a63 44 /**
08f3f44c 45 * Worker virtual task runtime.
b3432a63 46 */
08f3f44c 47 private workerVirtualTaskRunTime: number = 0
b3432a63 48
2fc5cae3 49 /** @inheritDoc */
da309861 50 public constructor (
c4855468 51 pool: IPool<Worker, Data, Response>,
2fc5cae3 52 opts: WorkerChoiceStrategyOptions = DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
da309861
JB
53 ) {
54 super(pool, opts)
932fc8be 55 this.setTaskStatisticsRequirements(this.opts)
08f3f44c 56 this.defaultWorkerWeight = this.computeDefaultWorkerWeight()
b3432a63
JB
57 }
58
afc003b2 59 /** @inheritDoc */
a6f7f1b4 60 public reset (): boolean {
39a43af7 61 this.resetWorkerNodeKeyProperties()
08f3f44c 62 this.workerVirtualTaskRunTime = 0
ea7a90d3
JB
63 return true
64 }
65
138d29a8
JB
66 /** @inheritDoc */
67 public update (): boolean {
68 return true
69 }
70
afc003b2 71 /** @inheritDoc */
b1aae695 72 public choose (): number | undefined {
baca80f7
JB
73 this.setPreviousWorkerNodeKey(this.nextWorkerNodeKey)
74 return this.weightedRoundRobinNextWorkerNodeKey()
b3432a63
JB
75 }
76
afc003b2 77 /** @inheritDoc */
f06e48d8 78 public remove (workerNodeKey: number): boolean {
226b02a3
JB
79 if (this.pool.workerNodes.length === 0) {
80 this.reset()
81 }
9b106837 82 if (this.nextWorkerNodeKey === workerNodeKey) {
08f3f44c 83 this.workerVirtualTaskRunTime = 0
97a2abc3 84 }
226b02a3
JB
85 if (
86 this.previousWorkerNodeKey === workerNodeKey &&
87 this.previousWorkerNodeKey > this.pool.workerNodes.length - 1
88 ) {
89 this.previousWorkerNodeKey = this.pool.workerNodes.length - 1
90 }
08f3f44c 91 return true
2377984d 92 }
9b106837 93
b1aae695 94 private weightedRoundRobinNextWorkerNodeKey (): number | undefined {
9b106837 95 const workerWeight =
7c7bb289
JB
96 this.opts.weights?.[
97 this.nextWorkerNodeKey ?? this.previousWorkerNodeKey
98 ] ?? this.defaultWorkerWeight
619f403b 99 if (this.workerVirtualTaskRunTime < workerWeight) {
9b106837 100 this.workerVirtualTaskRunTime =
619f403b 101 this.workerVirtualTaskRunTime +
7c7bb289
JB
102 this.getWorkerTaskRunTime(
103 this.nextWorkerNodeKey ?? this.previousWorkerNodeKey
104 )
9b106837
JB
105 } else {
106 this.nextWorkerNodeKey =
107 this.nextWorkerNodeKey === this.pool.workerNodes.length - 1
108 ? 0
7c7bb289 109 : (this.nextWorkerNodeKey ?? this.previousWorkerNodeKey) + 1
9b106837
JB
110 this.workerVirtualTaskRunTime = 0
111 }
20016c79 112 return this.nextWorkerNodeKey
9b106837 113 }
b3432a63 114}