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