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