build(deps-dev): apply updates
[poolifier.git] / tests / pools / selection-strategies / worker-choice-strategy-context.test.mjs
1 import { expect } from 'expect'
2 import { createStubInstance, restore, stub } from 'sinon'
3
4 import {
5 DynamicThreadPool,
6 FixedThreadPool,
7 WorkerChoiceStrategies
8 } from '../../../lib/index.cjs'
9 import { FairShareWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/fair-share-worker-choice-strategy.cjs'
10 import { InterleavedWeightedRoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/interleaved-weighted-round-robin-worker-choice-strategy.cjs'
11 import { LeastBusyWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-busy-worker-choice-strategy.cjs'
12 import { LeastEluWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-elu-worker-choice-strategy.cjs'
13 import { LeastUsedWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-used-worker-choice-strategy.cjs'
14 import { RoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/round-robin-worker-choice-strategy.cjs'
15 import { WeightedRoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/weighted-round-robin-worker-choice-strategy.cjs'
16 import { WorkerChoiceStrategyContext } from '../../../lib/pools/selection-strategies/worker-choice-strategy-context.cjs'
17
18 describe('Worker choice strategy context test suite', () => {
19 const min = 1
20 const max = 3
21 let fixedPool, dynamicPool
22
23 before(() => {
24 fixedPool = new FixedThreadPool(
25 max,
26 './tests/worker-files/thread/testWorker.mjs'
27 )
28 dynamicPool = new DynamicThreadPool(
29 min,
30 max,
31 './tests/worker-files/thread/testWorker.mjs'
32 )
33 })
34
35 afterEach(() => {
36 restore()
37 })
38
39 after(async () => {
40 await fixedPool.destroy()
41 await dynamicPool.destroy()
42 })
43
44 it('Verify that constructor() initializes the context with all the available worker choice strategies', () => {
45 let workerChoiceStrategyContext = new WorkerChoiceStrategyContext(fixedPool)
46 expect(workerChoiceStrategyContext.workerChoiceStrategies.size).toBe(
47 Object.keys(WorkerChoiceStrategies).length
48 )
49 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(dynamicPool)
50 expect(workerChoiceStrategyContext.workerChoiceStrategies.size).toBe(
51 Object.keys(WorkerChoiceStrategies).length
52 )
53 })
54
55 it('Verify that constructor() initializes the context with retries attribute properly set', () => {
56 let workerChoiceStrategyContext = new WorkerChoiceStrategyContext(fixedPool)
57 expect(workerChoiceStrategyContext.retries).toBe(fixedPool.info.maxSize * 2)
58 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(dynamicPool)
59 expect(workerChoiceStrategyContext.retries).toBe(
60 dynamicPool.info.maxSize * 2
61 )
62 })
63
64 it('Verify that execute() throws error if null or undefined is returned after retries', () => {
65 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
66 fixedPool
67 )
68 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
69 WorkerChoiceStrategies.ROUND_ROBIN
70 )
71 const workerChoiceStrategyUndefinedStub = createStubInstance(
72 RoundRobinWorkerChoiceStrategy,
73 {
74 choose: stub().returns(undefined)
75 }
76 )
77 workerChoiceStrategyContext.workerChoiceStrategies.set(
78 workerChoiceStrategyContext.workerChoiceStrategy,
79 workerChoiceStrategyUndefinedStub
80 )
81 expect(() => workerChoiceStrategyContext.execute()).toThrow(
82 new Error(
83 `Worker node key chosen is null or undefined after ${workerChoiceStrategyContext.retries} retries`
84 )
85 )
86 const workerChoiceStrategyNullStub = createStubInstance(
87 RoundRobinWorkerChoiceStrategy,
88 {
89 choose: stub().returns(null)
90 }
91 )
92 workerChoiceStrategyContext.workerChoiceStrategies.set(
93 workerChoiceStrategyContext.workerChoiceStrategy,
94 workerChoiceStrategyNullStub
95 )
96 expect(() => workerChoiceStrategyContext.execute()).toThrow(
97 new Error(
98 `Worker node key chosen is null or undefined after ${workerChoiceStrategyContext.retries} retries`
99 )
100 )
101 })
102
103 it('Verify that execute() retry until a worker node is chosen', () => {
104 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
105 fixedPool
106 )
107 const workerChoiceStrategyStub = createStubInstance(
108 RoundRobinWorkerChoiceStrategy,
109 {
110 choose: stub()
111 .onCall(0)
112 .returns(undefined)
113 .onCall(1)
114 .returns(undefined)
115 .onCall(2)
116 .returns(undefined)
117 .onCall(3)
118 .returns(undefined)
119 .onCall(4)
120 .returns(undefined)
121 .returns(1)
122 }
123 )
124 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
125 WorkerChoiceStrategies.ROUND_ROBIN
126 )
127 workerChoiceStrategyContext.workerChoiceStrategies.set(
128 workerChoiceStrategyContext.workerChoiceStrategy,
129 workerChoiceStrategyStub
130 )
131 const chosenWorkerKey = workerChoiceStrategyContext.execute()
132 expect(
133 workerChoiceStrategyContext.workerChoiceStrategies.get(
134 workerChoiceStrategyContext.workerChoiceStrategy
135 ).choose.callCount
136 ).toBe(6)
137 expect(chosenWorkerKey).toBe(1)
138 })
139
140 it('Verify that execute() return the worker node key chosen by the strategy with fixed pool', () => {
141 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
142 fixedPool
143 )
144 const workerChoiceStrategyStub = createStubInstance(
145 RoundRobinWorkerChoiceStrategy,
146 {
147 choose: stub().returns(0)
148 }
149 )
150 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
151 WorkerChoiceStrategies.ROUND_ROBIN
152 )
153 workerChoiceStrategyContext.workerChoiceStrategies.set(
154 workerChoiceStrategyContext.workerChoiceStrategy,
155 workerChoiceStrategyStub
156 )
157 const chosenWorkerKey = workerChoiceStrategyContext.execute()
158 expect(
159 workerChoiceStrategyContext.workerChoiceStrategies.get(
160 workerChoiceStrategyContext.workerChoiceStrategy
161 ).choose.calledOnce
162 ).toBe(true)
163 expect(chosenWorkerKey).toBe(0)
164 })
165
166 it('Verify that execute() return the worker node key chosen by the strategy with dynamic pool', () => {
167 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
168 dynamicPool
169 )
170 const workerChoiceStrategyStub = createStubInstance(
171 RoundRobinWorkerChoiceStrategy,
172 {
173 choose: stub().returns(0)
174 }
175 )
176 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
177 WorkerChoiceStrategies.ROUND_ROBIN
178 )
179 workerChoiceStrategyContext.workerChoiceStrategies.set(
180 workerChoiceStrategyContext.workerChoiceStrategy,
181 workerChoiceStrategyStub
182 )
183 const chosenWorkerKey = workerChoiceStrategyContext.execute()
184 expect(
185 workerChoiceStrategyContext.workerChoiceStrategies.get(
186 workerChoiceStrategyContext.workerChoiceStrategy
187 ).choose.calledOnce
188 ).toBe(true)
189 expect(chosenWorkerKey).toBe(0)
190 })
191
192 it('Verify that setWorkerChoiceStrategy() works with ROUND_ROBIN and fixed pool', () => {
193 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
194 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
195 fixedPool
196 )
197 expect(
198 workerChoiceStrategyContext.workerChoiceStrategies.get(
199 workerChoiceStrategy
200 )
201 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
202 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
203 workerChoiceStrategy
204 )
205 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
206 expect(
207 workerChoiceStrategyContext.workerChoiceStrategies.get(
208 workerChoiceStrategy
209 )
210 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
211 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
212 workerChoiceStrategy
213 )
214 })
215
216 it('Verify that setWorkerChoiceStrategy() works with ROUND_ROBIN and dynamic pool', () => {
217 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
218 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
219 dynamicPool
220 )
221 expect(
222 workerChoiceStrategyContext.workerChoiceStrategies.get(
223 workerChoiceStrategy
224 )
225 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
226 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
227 workerChoiceStrategy
228 )
229 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
230 expect(
231 workerChoiceStrategyContext.workerChoiceStrategies.get(
232 workerChoiceStrategy
233 )
234 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
235 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
236 workerChoiceStrategy
237 )
238 })
239
240 it('Verify that setWorkerChoiceStrategy() works with LEAST_USED and fixed pool', () => {
241 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
242 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
243 fixedPool
244 )
245 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
246 expect(
247 workerChoiceStrategyContext.workerChoiceStrategies.get(
248 workerChoiceStrategy
249 )
250 ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
251 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
252 workerChoiceStrategy
253 )
254 })
255
256 it('Verify that setWorkerChoiceStrategy() works with LEAST_USED and dynamic pool', () => {
257 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
258 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
259 dynamicPool
260 )
261 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
262 expect(
263 workerChoiceStrategyContext.workerChoiceStrategies.get(
264 workerChoiceStrategy
265 )
266 ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
267 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
268 workerChoiceStrategy
269 )
270 })
271
272 it('Verify that setWorkerChoiceStrategy() works with LEAST_BUSY and fixed pool', () => {
273 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
274 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
275 fixedPool
276 )
277 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
278 expect(
279 workerChoiceStrategyContext.workerChoiceStrategies.get(
280 workerChoiceStrategy
281 )
282 ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
283 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
284 workerChoiceStrategy
285 )
286 })
287
288 it('Verify that setWorkerChoiceStrategy() works with LEAST_BUSY and dynamic pool', () => {
289 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
290 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
291 dynamicPool
292 )
293 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
294 expect(
295 workerChoiceStrategyContext.workerChoiceStrategies.get(
296 workerChoiceStrategy
297 )
298 ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
299 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
300 workerChoiceStrategy
301 )
302 })
303
304 it('Verify that setWorkerChoiceStrategy() works with LEAST_ELU and fixed pool', () => {
305 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
306 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
307 fixedPool
308 )
309 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
310 expect(
311 workerChoiceStrategyContext.workerChoiceStrategies.get(
312 workerChoiceStrategy
313 )
314 ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
315 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
316 workerChoiceStrategy
317 )
318 })
319
320 it('Verify that setWorkerChoiceStrategy() works with LEAST_ELU and dynamic pool', () => {
321 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
322 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
323 dynamicPool
324 )
325 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
326 expect(
327 workerChoiceStrategyContext.workerChoiceStrategies.get(
328 workerChoiceStrategy
329 )
330 ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
331 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
332 workerChoiceStrategy
333 )
334 })
335
336 it('Verify that setWorkerChoiceStrategy() works with FAIR_SHARE and fixed pool', () => {
337 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
338 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
339 fixedPool
340 )
341 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
342 expect(
343 workerChoiceStrategyContext.workerChoiceStrategies.get(
344 workerChoiceStrategy
345 )
346 ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
347 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
348 workerChoiceStrategy
349 )
350 })
351
352 it('Verify that setWorkerChoiceStrategy() works with FAIR_SHARE and dynamic pool', () => {
353 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
354 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
355 dynamicPool
356 )
357 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
358 expect(
359 workerChoiceStrategyContext.workerChoiceStrategies.get(
360 workerChoiceStrategy
361 )
362 ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
363 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
364 workerChoiceStrategy
365 )
366 })
367
368 it('Verify that setWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and fixed pool', () => {
369 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
370 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
371 fixedPool
372 )
373 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
374 expect(
375 workerChoiceStrategyContext.workerChoiceStrategies.get(
376 workerChoiceStrategy
377 )
378 ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
379 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
380 workerChoiceStrategy
381 )
382 })
383
384 it('Verify that setWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
385 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
386 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
387 dynamicPool
388 )
389 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
390 expect(
391 workerChoiceStrategyContext.workerChoiceStrategies.get(
392 workerChoiceStrategy
393 )
394 ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
395 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
396 workerChoiceStrategy
397 )
398 })
399
400 it('Verify that setWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and fixed pool', () => {
401 const workerChoiceStrategy =
402 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
403 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
404 fixedPool
405 )
406 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
407 expect(
408 workerChoiceStrategyContext.workerChoiceStrategies.get(
409 workerChoiceStrategy
410 )
411 ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
412 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
413 workerChoiceStrategy
414 )
415 })
416
417 it('Verify that setWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
418 const workerChoiceStrategy =
419 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
420 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
421 dynamicPool
422 )
423 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
424 expect(
425 workerChoiceStrategyContext.workerChoiceStrategies.get(
426 workerChoiceStrategy
427 )
428 ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
429 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
430 workerChoiceStrategy
431 )
432 })
433
434 it('Verify that worker choice strategy options enable median runtime pool statistics', () => {
435 const wwrWorkerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
436 let workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
437 fixedPool,
438 wwrWorkerChoiceStrategy,
439 {
440 runTime: { median: true }
441 }
442 )
443 expect(
444 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
445 .average
446 ).toBe(false)
447 expect(
448 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
449 ).toBe(true)
450 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
451 dynamicPool,
452 wwrWorkerChoiceStrategy,
453 {
454 runTime: { median: true }
455 }
456 )
457 expect(
458 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
459 .average
460 ).toBe(false)
461 expect(
462 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
463 ).toBe(true)
464 const fsWorkerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
465 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
466 fixedPool,
467 fsWorkerChoiceStrategy,
468 {
469 runTime: { median: true }
470 }
471 )
472 expect(
473 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
474 .average
475 ).toBe(false)
476 expect(
477 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
478 ).toBe(true)
479 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
480 dynamicPool,
481 fsWorkerChoiceStrategy,
482 {
483 runTime: { median: true }
484 }
485 )
486 expect(
487 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
488 .average
489 ).toBe(false)
490 expect(
491 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
492 ).toBe(true)
493 })
494 })