chore(deps-dev): bump tatami-ng to 0.6.0
[poolifier.git] / src / pools / selection-strategies / fair-share-worker-choice-strategy.ts
CommitLineData
d35e5717 1import type { IPool } from '../pool.js'
67f3f2d6 2import type { IWorker } from '../worker.js'
97231086 3
d35e5717 4import { AbstractWorkerChoiceStrategy } from './abstract-worker-choice-strategy.js'
9adcefab
JB
5import {
6 type IWorkerChoiceStrategy,
7 Measurements,
39618ede 8 type TaskStatisticsRequirements,
3a502712 9 type WorkerChoiceStrategyOptions,
d35e5717 10} from './selection-strategies-types.js'
23ff945a 11
23ff945a
JB
12/**
13 * Selects the next worker with a fair share scheduling algorithm.
14 * Loosely modeled after the fair queueing algorithm: https://en.wikipedia.org/wiki/Fair_queuing.
38e795c1 15 * @typeParam Worker - Type of worker which manages the strategy.
e102732c
JB
16 * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
17 * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
23ff945a
JB
18 */
19export class FairShareWorkerChoiceStrategy<
f06e48d8 20 Worker extends IWorker,
b2b1d84e
JB
21 Data = unknown,
22 Response = unknown
bf90656c
JB
23 >
24 extends AbstractWorkerChoiceStrategy<Worker, Data, Response>
17393ac8 25 implements IWorkerChoiceStrategy {
afc003b2 26 /** @inheritDoc */
ae85b351
JB
27 public override readonly taskStatisticsRequirements: TaskStatisticsRequirements =
28 {
29 elu: {
30 aggregate: true,
31 average: true,
32 median: false,
33 },
34 runTime: {
35 aggregate: true,
36 average: true,
37 median: false,
38 },
39 waitTime: {
40 aggregate: true,
41 average: true,
42 median: false,
43 },
44 }
10fcfaf4 45
2fc5cae3
JB
46 /** @inheritDoc */
47 public constructor (
48 pool: IPool<Worker, Data, Response>,
39618ede 49 opts?: WorkerChoiceStrategyOptions
2fc5cae3
JB
50 ) {
51 super(pool, opts)
932fc8be 52 this.setTaskStatisticsRequirements(this.opts)
2fc5cae3
JB
53 }
54
97231086
JB
55 /**
56 * Computes the worker node key virtual task end timestamp.
57 * @param workerNodeKey - The worker node key.
58 * @returns The worker node key virtual task end timestamp.
59 */
60 private computeWorkerNodeVirtualTaskEndTimestamp (
61 workerNodeKey: number
62 ): number {
63 return this.getWorkerNodeVirtualTaskEndTimestamp(
64 workerNodeKey,
65 this.getWorkerNodeVirtualTaskStartTimestamp(workerNodeKey)
66 )
9b106837
JB
67 }
68
b1aae695 69 private fairShareNextWorkerNodeKey (): number | undefined {
f3a91bac
JB
70 return this.pool.workerNodes.reduce(
71 (minWorkerNodeKey, workerNode, workerNodeKey, workerNodes) => {
72 if (workerNode.strategyData?.virtualTaskEndTimestamp == null) {
73 workerNode.strategyData = {
74 virtualTaskEndTimestamp:
3a502712 75 this.computeWorkerNodeVirtualTaskEndTimestamp(workerNodeKey),
f3a91bac
JB
76 }
77 }
ae3ab61d 78 return this.isWorkerNodeReady(workerNodeKey) &&
67f3f2d6
JB
79 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
80 workerNode.strategyData.virtualTaskEndTimestamp! <
81 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
82 workerNodes[minWorkerNodeKey].strategyData!.virtualTaskEndTimestamp!
f3a91bac
JB
83 ? workerNodeKey
84 : minWorkerNodeKey
85 },
86 0
87 )
97a2abc3
JB
88 }
89
f3a91bac 90 private getWorkerNodeVirtualTaskEndTimestamp (
b0d6ed8f 91 workerNodeKey: number,
f3a91bac 92 workerNodeVirtualTaskStartTimestamp: number
b0d6ed8f 93 ): number {
e0843544
JB
94 const workerNodeTaskExecutionTime =
95 this.getWorkerNodeTaskWaitTime(workerNodeKey) +
39618ede 96 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
e0843544 97 (this.opts!.measurement === Measurements.elu
f3a91bac 98 ? this.getWorkerNodeTaskElu(workerNodeKey)
e0843544
JB
99 : this.getWorkerNodeTaskRunTime(workerNodeKey))
100 return workerNodeVirtualTaskStartTimestamp + workerNodeTaskExecutionTime
b0d6ed8f
JB
101 }
102
f3a91bac
JB
103 private getWorkerNodeVirtualTaskStartTimestamp (
104 workerNodeKey: number
105 ): number {
f201a0cd
JB
106 const virtualTaskEndTimestamp =
107 this.pool.workerNodes[workerNodeKey]?.strategyData
108 ?.virtualTaskEndTimestamp
f3a91bac 109 const now = performance.now()
80115618 110 return now < (virtualTaskEndTimestamp ?? Number.NEGATIVE_INFINITY)
67f3f2d6
JB
111 ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
112 virtualTaskEndTimestamp!
f3a91bac 113 : now
23ff945a 114 }
97231086
JB
115
116 /** @inheritDoc */
117 public choose (): number | undefined {
118 this.setPreviousWorkerNodeKey(this.nextWorkerNodeKey)
119 this.nextWorkerNodeKey = this.fairShareNextWorkerNodeKey()
120 return this.nextWorkerNodeKey
121 }
122
123 /** @inheritDoc */
124 public remove (): boolean {
125 return true
126 }
127
128 /** @inheritDoc */
129 public reset (): boolean {
130 for (const workerNode of this.pool.workerNodes) {
131 delete workerNode.strategyData?.virtualTaskEndTimestamp
132 }
133 return true
134 }
135
136 /** @inheritDoc */
137 public update (workerNodeKey: number): boolean {
138 this.pool.workerNodes[workerNodeKey].strategyData = {
139 virtualTaskEndTimestamp:
140 this.computeWorkerNodeVirtualTaskEndTimestamp(workerNodeKey),
141 }
142 return true
143 }
23ff945a 144}