feat: use monotonic high resolution timer for worker tasks statistics
[poolifier.git] / src / pools / selection-strategies / fair-share-worker-choice-strategy.ts
CommitLineData
ea7a90d3 1import type { IPoolWorker } from '../pool-worker'
23ff945a 2import { AbstractWorkerChoiceStrategy } from './abstract-worker-choice-strategy'
bf90656c
JB
3import type {
4 IWorkerChoiceStrategy,
5 RequiredStatistics
6} from './selection-strategies-types'
23ff945a
JB
7
8/**
9 * Worker virtual task timestamp.
10 */
78cea37e 11interface WorkerVirtualTaskTimestamp {
23ff945a
JB
12 start: number
13 end: number
14}
15
16/**
17 * Selects the next worker with a fair share scheduling algorithm.
18 * Loosely modeled after the fair queueing algorithm: https://en.wikipedia.org/wiki/Fair_queuing.
19 *
38e795c1
JB
20 * @typeParam Worker - Type of worker which manages the strategy.
21 * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
22 * @typeParam Response - Type of response of execution. This can only be serializable data.
23ff945a
JB
23 */
24export class FairShareWorkerChoiceStrategy<
bf90656c 25 Worker extends IPoolWorker,
b2b1d84e
JB
26 Data = unknown,
27 Response = unknown
bf90656c
JB
28 >
29 extends AbstractWorkerChoiceStrategy<Worker, Data, Response>
17393ac8 30 implements IWorkerChoiceStrategy {
afc003b2 31 /** @inheritDoc */
ea7a90d3 32 public readonly requiredStatistics: RequiredStatistics = {
c6bd2650 33 runTime: true,
78099a15
JB
34 avgRunTime: true,
35 medRunTime: false
10fcfaf4
JB
36 }
37
23ff945a
JB
38 /**
39 * Worker last virtual task execution timestamp.
40 */
ea7a90d3 41 private readonly workerLastVirtualTaskTimestamp: Map<
c923ce56 42 number,
78cea37e 43 WorkerVirtualTaskTimestamp
c923ce56 44 > = new Map<number, WorkerVirtualTaskTimestamp>()
23ff945a 45
afc003b2 46 /** @inheritDoc */
a6f7f1b4 47 public reset (): boolean {
ea7a90d3
JB
48 this.workerLastVirtualTaskTimestamp.clear()
49 return true
50 }
51
afc003b2 52 /** @inheritDoc */
c923ce56 53 public choose (): number {
23ff945a 54 let minWorkerVirtualTaskEndTimestamp = Infinity
c923ce56
JB
55 let chosenWorkerKey!: number
56 for (const [index] of this.pool.workers.entries()) {
57 this.computeWorkerLastVirtualTaskTimestamp(index)
23ff945a 58 const workerLastVirtualTaskEndTimestamp =
c923ce56 59 this.workerLastVirtualTaskTimestamp.get(index)?.end ?? 0
23ff945a
JB
60 if (
61 workerLastVirtualTaskEndTimestamp < minWorkerVirtualTaskEndTimestamp
62 ) {
63 minWorkerVirtualTaskEndTimestamp = workerLastVirtualTaskEndTimestamp
c923ce56 64 chosenWorkerKey = index
23ff945a
JB
65 }
66 }
c923ce56 67 return chosenWorkerKey
23ff945a
JB
68 }
69
afc003b2 70 /** @inheritDoc */
97a2abc3
JB
71 public remove (workerKey: number): boolean {
72 const workerDeleted = this.workerLastVirtualTaskTimestamp.delete(workerKey)
73 for (const [key, value] of this.workerLastVirtualTaskTimestamp.entries()) {
74 if (key > workerKey) {
75 this.workerLastVirtualTaskTimestamp.set(key - 1, value)
76 }
77 }
78 return workerDeleted
79 }
80
23ff945a 81 /**
11df3590
JB
82 * Computes worker last virtual task timestamp.
83 *
c923ce56 84 * @param workerKey - The worker key.
23ff945a 85 */
c923ce56 86 private computeWorkerLastVirtualTaskTimestamp (workerKey: number): void {
11df3590 87 const workerVirtualTaskStartTimestamp = Math.max(
3fafb1b2 88 performance.now(),
c923ce56 89 this.workerLastVirtualTaskTimestamp.get(workerKey)?.end ?? -Infinity
11df3590 90 )
c923ce56 91 this.workerLastVirtualTaskTimestamp.set(workerKey, {
11df3590 92 start: workerVirtualTaskStartTimestamp,
61d849ff
JB
93 end:
94 workerVirtualTaskStartTimestamp +
c923ce56 95 (this.pool.workers[workerKey].tasksUsage.avgRunTime ?? 0)
11df3590 96 })
23ff945a
JB
97 }
98}