feat: improve IWRR implementation
[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 {
9b106837 73 const chosenWorkerNodeKey = this.nextWorkerNodeKey
b1aae695 74 this.weightedRoundRobinNextWorkerNodeKey()
48e6ef5a 75 this.checkNextWorkerNodeEligibility(chosenWorkerNodeKey)
f06e48d8 76 return chosenWorkerNodeKey
b3432a63
JB
77 }
78
afc003b2 79 /** @inheritDoc */
f06e48d8 80 public remove (workerNodeKey: number): boolean {
9b106837 81 if (this.nextWorkerNodeKey === workerNodeKey) {
f06e48d8 82 if (this.pool.workerNodes.length === 0) {
9b106837
JB
83 this.nextWorkerNodeKey = 0
84 } else if (this.nextWorkerNodeKey > this.pool.workerNodes.length - 1) {
85 this.nextWorkerNodeKey = this.pool.workerNodes.length - 1
78ab2555 86 }
08f3f44c 87 this.workerVirtualTaskRunTime = 0
97a2abc3 88 }
08f3f44c 89 return true
2377984d 90 }
9b106837 91
b1aae695 92 private weightedRoundRobinNextWorkerNodeKey (): number | undefined {
9b106837 93 const workerWeight =
7c7bb289
JB
94 this.opts.weights?.[
95 this.nextWorkerNodeKey ?? this.previousWorkerNodeKey
96 ] ?? this.defaultWorkerWeight
619f403b 97 if (this.workerVirtualTaskRunTime < workerWeight) {
9b106837 98 this.workerVirtualTaskRunTime =
619f403b 99 this.workerVirtualTaskRunTime +
7c7bb289
JB
100 this.getWorkerTaskRunTime(
101 this.nextWorkerNodeKey ?? this.previousWorkerNodeKey
102 )
9b106837
JB
103 } else {
104 this.nextWorkerNodeKey =
105 this.nextWorkerNodeKey === this.pool.workerNodes.length - 1
106 ? 0
7c7bb289 107 : (this.nextWorkerNodeKey ?? this.previousWorkerNodeKey) + 1
9b106837
JB
108 this.workerVirtualTaskRunTime = 0
109 }
20016c79 110 return this.nextWorkerNodeKey
9b106837 111 }
b3432a63 112}