feat: optimize worker choice strategies 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 /**
f3a91bac 45 * Worker node virtual task runtime.
b3432a63 46 */
f3a91bac 47 private workerNodeVirtualTaskRunTime: 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()
f3a91bac 62 this.workerNodeVirtualTaskRunTime = 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) {
f3a91bac 83 this.workerNodeVirtualTaskRunTime = 0
0dd90fe1
JB
84 if (this.nextWorkerNodeKey > this.pool.workerNodes.length - 1) {
85 this.nextWorkerNodeKey = this.pool.workerNodes.length - 1
86 }
97a2abc3 87 }
226b02a3
JB
88 if (
89 this.previousWorkerNodeKey === workerNodeKey &&
90 this.previousWorkerNodeKey > this.pool.workerNodes.length - 1
91 ) {
92 this.previousWorkerNodeKey = this.pool.workerNodes.length - 1
93 }
08f3f44c 94 return true
2377984d 95 }
9b106837 96
b1aae695 97 private weightedRoundRobinNextWorkerNodeKey (): number | undefined {
9b106837 98 const workerWeight =
7c7bb289
JB
99 this.opts.weights?.[
100 this.nextWorkerNodeKey ?? this.previousWorkerNodeKey
101 ] ?? this.defaultWorkerWeight
f3a91bac
JB
102 if (this.workerNodeVirtualTaskRunTime < workerWeight) {
103 this.workerNodeVirtualTaskRunTime =
104 this.workerNodeVirtualTaskRunTime +
105 this.getWorkerNodeTaskRunTime(
7c7bb289
JB
106 this.nextWorkerNodeKey ?? this.previousWorkerNodeKey
107 )
9b106837
JB
108 } else {
109 this.nextWorkerNodeKey =
110 this.nextWorkerNodeKey === this.pool.workerNodes.length - 1
111 ? 0
7c7bb289 112 : (this.nextWorkerNodeKey ?? this.previousWorkerNodeKey) + 1
f3a91bac 113 this.workerNodeVirtualTaskRunTime = 0
9b106837 114 }
20016c79 115 return this.nextWorkerNodeKey
9b106837 116 }
b3432a63 117}