1 import type { IWorker
} from
'./abstract-pool'
2 import type { IPoolInternal
} from
'./pool-internal'
5 * Enumeration of worker choice strategies.
7 export const WorkerChoiceStrategies
= Object.freeze({
9 * Round robin worker selection strategy.
11 ROUND_ROBIN
: 'ROUND_ROBIN',
13 * Less recently used worker selection strategy.
15 LESS_RECENTLY_USED
: 'LESS_RECENTLY_USED'
19 * Worker choice strategy.
21 export type WorkerChoiceStrategy
= keyof
typeof WorkerChoiceStrategies
24 * Worker choice strategy interface.
26 * @template Worker Type of worker which manages the strategy.
28 interface IWorkerChoiceStrategy
<Worker
extends IWorker
> {
30 * Choose a worker in the pool.
36 * Selects the next worker in a round robin fashion.
38 * @template Worker Type of worker which manages the strategy.
39 * @template Data Type of data sent to the worker. This can only be serializable data.
40 * @template Response Type of response of execution. This can only be serializable data.
42 class RoundRobinWorkerChoiceStrategy
<Worker
extends IWorker
, Data
, Response
>
43 implements IWorkerChoiceStrategy
<Worker
> {
45 * Index for the next worker.
47 private nextWorkerIndex
: number = 0
50 * Constructs a worker choice strategy that selects in a round robin fashion.
52 * @param pool The pool instance.
55 private readonly pool
: IPoolInternal
<Worker
, Data
, Response
>
59 public choose (): Worker
{
60 const chosenWorker
= this.pool
.workers
[this.nextWorkerIndex
]
61 this.nextWorkerIndex
=
62 this.pool
.workers
.length
- 1 === this.nextWorkerIndex
64 : this.nextWorkerIndex
+ 1
70 * Selects the less recently used worker.
72 * @template Worker Type of worker which manages the strategy.
73 * @template Data Type of data sent to the worker. This can only be serializable data.
74 * @template Response Type of response of execution. This can only be serializable data.
76 class LessRecentlyUsedWorkerChoiceStrategy
<
77 Worker
extends IWorker
,
80 > implements IWorkerChoiceStrategy
<Worker
> {
82 * Constructs a worker choice strategy that selects based on less recently used.
84 * @param pool The pool instance.
87 private readonly pool
: IPoolInternal
<Worker
, Data
, Response
>
91 public choose (): Worker
{
92 const isPoolDynamic
= this.pool
.dynamic
93 let minNumberOfTasks
= Infinity
94 // A worker is always found because it picks the one with fewer tasks
95 let lessRecentlyUsedWorker
!: Worker
96 for (const [worker
, numberOfTasks
] of this.pool
.tasks
) {
97 if (!isPoolDynamic
&& numberOfTasks
=== 0) {
99 } else if (numberOfTasks
< minNumberOfTasks
) {
100 minNumberOfTasks
= numberOfTasks
101 lessRecentlyUsedWorker
= worker
104 return lessRecentlyUsedWorker
109 * Dynamically choose a worker.
111 * @template Worker Type of worker which manages the strategy.
112 * @template Data Type of data sent to the worker. This can only be serializable data.
113 * @template Response Type of response of execution. This can only be serializable data.
115 class DynamicPoolWorkerChoiceStrategy
<Worker
extends IWorker
, Data
, Response
>
116 implements IWorkerChoiceStrategy
<Worker
> {
117 private workerChoiceStrategy
: IWorkerChoiceStrategy
<Worker
>
120 * Constructs a worker choice strategy for dynamical pools.
122 * @param pool The pool instance.
123 * @param createDynamicallyWorkerCallback The worker creation callback for dynamic pool.
124 * @param workerChoiceStrategy The worker choice strategy when the pull is full.
127 private readonly pool
: IPoolInternal
<Worker
, Data
, Response
>,
128 private createDynamicallyWorkerCallback
: () => Worker
,
129 workerChoiceStrategy
: WorkerChoiceStrategy
= WorkerChoiceStrategies
.ROUND_ROBIN
131 this.workerChoiceStrategy
= SelectionStrategiesUtils
.getWorkerChoiceStrategy(
138 public choose (): Worker
{
139 const freeWorker
= SelectionStrategiesUtils
.findFreeWorkerBasedOnTasks(
146 if (this.pool
.workers
.length
=== this.pool
.max
) {
147 this.pool
.emitter
.emit('busy')
148 return this.workerChoiceStrategy
.choose()
151 // All workers are busy, create a new worker
152 return this.createDynamicallyWorkerCallback()
157 * The worker choice strategy context.
159 * @template Worker Type of worker.
160 * @template Data Type of data sent to the worker. This can only be serializable data.
161 * @template Response Type of response of execution. This can only be serializable data.
163 export class WorkerChoiceStrategyContext
<
164 Worker
extends IWorker
,
168 // Will be set by setter in constructor
169 private workerChoiceStrategy
!: IWorkerChoiceStrategy
<Worker
>
172 * Worker choice strategy context constructor.
174 * @param pool The pool instance.
175 * @param createDynamicallyWorkerCallback The worker creation callback for dynamic pool.
176 * @param workerChoiceStrategy The worker choice strategy.
179 private readonly pool
: IPoolInternal
<Worker
, Data
, Response
>,
180 private createDynamicallyWorkerCallback
: () => Worker
,
181 workerChoiceStrategy
: WorkerChoiceStrategy
= WorkerChoiceStrategies
.ROUND_ROBIN
183 this.setWorkerChoiceStrategy(workerChoiceStrategy
)
187 * Get the worker choice strategy instance specific to the pool type.
189 * @param workerChoiceStrategy The worker choice strategy.
190 * @returns The worker choice strategy instance for the pool type.
192 private getPoolWorkerChoiceStrategy (
193 workerChoiceStrategy
: WorkerChoiceStrategy
= WorkerChoiceStrategies
.ROUND_ROBIN
194 ): IWorkerChoiceStrategy
<Worker
> {
195 if (this.pool
.dynamic
) {
196 return new DynamicPoolWorkerChoiceStrategy(
198 this.createDynamicallyWorkerCallback
,
202 return SelectionStrategiesUtils
.getWorkerChoiceStrategy(
209 * Set the worker choice strategy to use in the context.
211 * @param workerChoiceStrategy The worker choice strategy to set.
213 public setWorkerChoiceStrategy (
214 workerChoiceStrategy
: WorkerChoiceStrategy
216 this.workerChoiceStrategy
= this.getPoolWorkerChoiceStrategy(
222 * Choose a worker with the underlying selection strategy.
224 * @returns The chosen one.
226 public execute (): Worker
{
227 return this.workerChoiceStrategy
.choose()
232 * Worker selection strategies helpers.
234 class SelectionStrategiesUtils
{
236 * Find a free worker based on number of tasks the worker has applied.
238 * If a worker was found that has `0` tasks, it is detected as free and will be returned.
240 * If no free worker was found, `null` will be returned.
242 * @param workerTasksMap The pool worker tasks map.
243 * @returns A free worker if there was one, otherwise `null`.
245 public static findFreeWorkerBasedOnTasks
<Worker
extends IWorker
> (
246 workerTasksMap
: Map
<Worker
, number>
248 for (const [worker
, numberOfTasks
] of workerTasksMap
) {
249 if (numberOfTasks
=== 0) {
250 // A worker is free, use it
258 * Get the worker choice strategy instance.
260 * @param pool The pool instance.
261 * @param workerChoiceStrategy The worker choice strategy.
262 * @returns The worker choice strategy instance.
264 public static getWorkerChoiceStrategy
<
265 Worker
extends IWorker
,
269 pool
: IPoolInternal
<Worker
, Data
, Response
>,
270 workerChoiceStrategy
: WorkerChoiceStrategy
= WorkerChoiceStrategies
.ROUND_ROBIN
271 ): IWorkerChoiceStrategy
<Worker
> {
272 switch (workerChoiceStrategy
) {
273 case WorkerChoiceStrategies
.ROUND_ROBIN
:
274 return new RoundRobinWorkerChoiceStrategy(pool
)
275 case WorkerChoiceStrategies
.LESS_RECENTLY_USED
:
276 return new LessRecentlyUsedWorkerChoiceStrategy(pool
)
279 `Worker choice strategy '${workerChoiceStrategy}' not found`