refactor: align worker choice strategy options namespace
[poolifier.git] / src / pools / selection-strategies / fair-share-worker-choice-strategy.ts
1 import { DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS } from '../../utils'
2 import type { IPool } from '../pool'
3 import type { IWorker } from '../worker'
4 import { AbstractWorkerChoiceStrategy } from './abstract-worker-choice-strategy'
5 import type {
6 IWorkerChoiceStrategy,
7 TaskStatisticsRequirements,
8 WorkerChoiceStrategyOptions
9 } from './selection-strategies-types'
10
11 /**
12 * Selects the next worker with a fair share scheduling algorithm.
13 * Loosely modeled after the fair queueing algorithm: https://en.wikipedia.org/wiki/Fair_queuing.
14 *
15 * @typeParam Worker - Type of worker which manages the strategy.
16 * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
17 * @typeParam Response - Type of execution response. This can only be serializable data.
18 */
19 export class FairShareWorkerChoiceStrategy<
20 Worker extends IWorker,
21 Data = unknown,
22 Response = unknown
23 >
24 extends AbstractWorkerChoiceStrategy<Worker, Data, Response>
25 implements IWorkerChoiceStrategy {
26 /** @inheritDoc */
27 public readonly taskStatisticsRequirements: TaskStatisticsRequirements = {
28 runTime: {
29 aggregate: true,
30 average: true,
31 median: false
32 },
33 waitTime: {
34 aggregate: false,
35 average: false,
36 median: false
37 },
38 elu: false
39 }
40
41 /**
42 * Workers' virtual task end execution timestamp.
43 */
44 private workersVirtualTaskEndTimestamp: number[] = []
45
46 /** @inheritDoc */
47 public constructor (
48 pool: IPool<Worker, Data, Response>,
49 opts: WorkerChoiceStrategyOptions = DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS
50 ) {
51 super(pool, opts)
52 this.setTaskStatisticsRequirements(this.opts)
53 }
54
55 /** @inheritDoc */
56 public reset (): boolean {
57 this.workersVirtualTaskEndTimestamp = []
58 return true
59 }
60
61 /** @inheritDoc */
62 public update (workerNodeKey: number): boolean {
63 this.computeWorkerVirtualTaskEndTimestamp(workerNodeKey)
64 return true
65 }
66
67 /** @inheritDoc */
68 public choose (): number {
69 let minWorkerVirtualTaskEndTimestamp = Infinity
70 let chosenWorkerNodeKey!: number
71 for (const [workerNodeKey] of this.pool.workerNodes.entries()) {
72 if (this.workersVirtualTaskEndTimestamp[workerNodeKey] == null) {
73 this.computeWorkerVirtualTaskEndTimestamp(workerNodeKey)
74 }
75 const workerVirtualTaskEndTimestamp =
76 this.workersVirtualTaskEndTimestamp[workerNodeKey]
77 if (workerVirtualTaskEndTimestamp < minWorkerVirtualTaskEndTimestamp) {
78 minWorkerVirtualTaskEndTimestamp = workerVirtualTaskEndTimestamp
79 chosenWorkerNodeKey = workerNodeKey
80 }
81 }
82 return chosenWorkerNodeKey
83 }
84
85 /** @inheritDoc */
86 public remove (workerNodeKey: number): boolean {
87 this.workersVirtualTaskEndTimestamp.splice(workerNodeKey, 1)
88 return true
89 }
90
91 /**
92 * Computes the worker node key virtual task end timestamp.
93 *
94 * @param workerNodeKey - The worker node key.
95 */
96 private computeWorkerVirtualTaskEndTimestamp (workerNodeKey: number): void {
97 this.workersVirtualTaskEndTimestamp[workerNodeKey] =
98 this.getWorkerVirtualTaskEndTimestamp(
99 workerNodeKey,
100 this.getWorkerVirtualTaskStartTimestamp(workerNodeKey)
101 )
102 }
103
104 private getWorkerVirtualTaskEndTimestamp (
105 workerNodeKey: number,
106 workerVirtualTaskStartTimestamp: number
107 ): number {
108 return (
109 workerVirtualTaskStartTimestamp + this.getWorkerTaskRunTime(workerNodeKey)
110 )
111 }
112
113 private getWorkerVirtualTaskStartTimestamp (workerNodeKey: number): number {
114 return Math.max(
115 performance.now(),
116 this.workersVirtualTaskEndTimestamp[workerNodeKey] ?? -Infinity
117 )
118 }
119 }