Merge branch 'master' into waittime
[poolifier.git] / tests / pools / selection-strategies / selection-strategies.test.js
1 const { expect } = require('expect')
2 const {
3 WorkerChoiceStrategies,
4 DynamicThreadPool,
5 FixedThreadPool,
6 FixedClusterPool
7 } = require('../../../lib')
8 const { CircularArray } = require('../../../lib/circular-array')
9
10 describe('Selection strategies test suite', () => {
11 const min = 0
12 const max = 3
13
14 it('Verify that WorkerChoiceStrategies enumeration provides string values', () => {
15 expect(WorkerChoiceStrategies.ROUND_ROBIN).toBe('ROUND_ROBIN')
16 expect(WorkerChoiceStrategies.LEAST_USED).toBe('LEAST_USED')
17 expect(WorkerChoiceStrategies.LEAST_BUSY).toBe('LEAST_BUSY')
18 expect(WorkerChoiceStrategies.FAIR_SHARE).toBe('FAIR_SHARE')
19 expect(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN).toBe(
20 'WEIGHTED_ROUND_ROBIN'
21 )
22 })
23
24 it('Verify ROUND_ROBIN strategy is the default at pool creation', async () => {
25 const pool = new DynamicThreadPool(
26 min,
27 max,
28 './tests/worker-files/thread/testWorker.js'
29 )
30 expect(pool.opts.workerChoiceStrategy).toBe(
31 WorkerChoiceStrategies.ROUND_ROBIN
32 )
33 // We need to clean up the resources after our test
34 await pool.destroy()
35 })
36
37 it('Verify available strategies are taken at pool creation', async () => {
38 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
39 const pool = new FixedThreadPool(
40 max,
41 './tests/worker-files/thread/testWorker.js',
42 { workerChoiceStrategy }
43 )
44 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
45 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
46 workerChoiceStrategy
47 )
48 await pool.destroy()
49 }
50 })
51
52 it('Verify available strategies can be set after pool creation', async () => {
53 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
54 const pool = new DynamicThreadPool(
55 min,
56 max,
57 './tests/worker-files/thread/testWorker.js'
58 )
59 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
60 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
61 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
62 workerChoiceStrategy
63 )
64 await pool.destroy()
65 }
66 })
67
68 it('Verify available strategies default internals at pool creation', async () => {
69 const pool = new FixedThreadPool(
70 max,
71 './tests/worker-files/thread/testWorker.js'
72 )
73 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
74 if (workerChoiceStrategy === WorkerChoiceStrategies.ROUND_ROBIN) {
75 expect(
76 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
77 workerChoiceStrategy
78 ).nextWorkerNodeId
79 ).toBe(0)
80 } else if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) {
81 expect(
82 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
83 workerChoiceStrategy
84 ).workersVirtualTaskEndTimestamp
85 ).toBeInstanceOf(Array)
86 expect(
87 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
88 workerChoiceStrategy
89 ).workersVirtualTaskEndTimestamp.length
90 ).toBe(0)
91 } else if (
92 workerChoiceStrategy === WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
93 ) {
94 expect(
95 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
96 workerChoiceStrategy
97 ).currentWorkerNodeId
98 ).toBe(0)
99 expect(
100 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
101 workerChoiceStrategy
102 ).defaultWorkerWeight
103 ).toBeGreaterThan(0)
104 expect(
105 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
106 workerChoiceStrategy
107 ).workerVirtualTaskRunTime
108 ).toBe(0)
109 }
110 }
111 await pool.destroy()
112 })
113
114 it('Verify ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
115 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
116 let pool = new FixedThreadPool(
117 max,
118 './tests/worker-files/thread/testWorker.js',
119 { workerChoiceStrategy }
120 )
121 expect(
122 pool.workerChoiceStrategyContext.getRequiredStatistics()
123 ).toStrictEqual({
124 runTime: false,
125 avgRunTime: false,
126 medRunTime: false,
127 waitTime: false,
128 avgWaitTime: false,
129 medWaitTime: false
130 })
131 await pool.destroy()
132 pool = new DynamicThreadPool(
133 min,
134 max,
135 './tests/worker-files/thread/testWorker.js',
136 { workerChoiceStrategy }
137 )
138 expect(
139 pool.workerChoiceStrategyContext.getRequiredStatistics()
140 ).toStrictEqual({
141 runTime: false,
142 avgRunTime: false,
143 medRunTime: false,
144 waitTime: false,
145 avgWaitTime: false,
146 medWaitTime: false
147 })
148 // We need to clean up the resources after our test
149 await pool.destroy()
150 })
151
152 it('Verify ROUND_ROBIN strategy can be run in a fixed pool', async () => {
153 const pool = new FixedThreadPool(
154 max,
155 './tests/worker-files/thread/testWorker.js',
156 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
157 )
158 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
159 const maxMultiplier = 2
160 for (let i = 0; i < max * maxMultiplier; i++) {
161 await pool.execute()
162 }
163 // We need to clean up the resources after our test
164 await pool.destroy()
165 })
166
167 it('Verify ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
168 const pool = new DynamicThreadPool(
169 min,
170 max,
171 './tests/worker-files/thread/testWorker.js',
172 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
173 )
174 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
175 const maxMultiplier = 2
176 for (let i = 0; i < max * maxMultiplier; i++) {
177 await pool.execute()
178 }
179 // We need to clean up the resources after our test
180 await pool.destroy()
181 })
182
183 it('Verify ROUND_ROBIN strategy runtime behavior', async () => {
184 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
185 let pool = new FixedClusterPool(
186 max,
187 './tests/worker-files/cluster/testWorker.js',
188 { workerChoiceStrategy }
189 )
190 let results = new Set()
191 for (let i = 0; i < max; i++) {
192 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.id)
193 }
194 expect(results.size).toBe(max)
195 await pool.destroy()
196 pool = new FixedThreadPool(
197 max,
198 './tests/worker-files/thread/testWorker.js',
199 { workerChoiceStrategy }
200 )
201 results = new Set()
202 for (let i = 0; i < max; i++) {
203 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.threadId)
204 }
205 expect(results.size).toBe(max)
206 await pool.destroy()
207 })
208
209 it('Verify ROUND_ROBIN strategy internals are resets after setting it', async () => {
210 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
211 let pool = new FixedThreadPool(
212 max,
213 './tests/worker-files/thread/testWorker.js',
214 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
215 )
216 expect(
217 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
218 workerChoiceStrategy
219 ).nextWorkerNodeId
220 ).toBeDefined()
221 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
222 expect(
223 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
224 pool.workerChoiceStrategyContext.workerChoiceStrategy
225 ).nextWorkerNodeId
226 ).toBe(0)
227 await pool.destroy()
228 pool = new DynamicThreadPool(
229 min,
230 max,
231 './tests/worker-files/thread/testWorker.js',
232 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
233 )
234 expect(
235 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
236 workerChoiceStrategy
237 ).nextWorkerNodeId
238 ).toBeDefined()
239 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
240 expect(
241 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
242 pool.workerChoiceStrategyContext.workerChoiceStrategy
243 ).nextWorkerNodeId
244 ).toBe(0)
245 // We need to clean up the resources after our test
246 await pool.destroy()
247 })
248
249 it('Verify LEAST_USED strategy default tasks usage statistics requirements', async () => {
250 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
251 let pool = new FixedThreadPool(
252 max,
253 './tests/worker-files/thread/testWorker.js',
254 { workerChoiceStrategy }
255 )
256 expect(
257 pool.workerChoiceStrategyContext.getRequiredStatistics()
258 ).toStrictEqual({
259 runTime: false,
260 avgRunTime: false,
261 medRunTime: false,
262 waitTime: false,
263 avgWaitTime: false,
264 medWaitTime: false
265 })
266 await pool.destroy()
267 pool = new DynamicThreadPool(
268 min,
269 max,
270 './tests/worker-files/thread/testWorker.js',
271 { workerChoiceStrategy }
272 )
273 expect(
274 pool.workerChoiceStrategyContext.getRequiredStatistics()
275 ).toStrictEqual({
276 runTime: false,
277 avgRunTime: false,
278 medRunTime: false,
279 waitTime: false,
280 avgWaitTime: false,
281 medWaitTime: false
282 })
283 // We need to clean up the resources after our test
284 await pool.destroy()
285 })
286
287 it('Verify LEAST_USED strategy can be run in a fixed pool', async () => {
288 const pool = new FixedThreadPool(
289 max,
290 './tests/worker-files/thread/testWorker.js',
291 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
292 )
293 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
294 const maxMultiplier = 2
295 for (let i = 0; i < max * maxMultiplier; i++) {
296 await pool.execute()
297 }
298 // We need to clean up the resources after our test
299 await pool.destroy()
300 })
301
302 it('Verify LEAST_USED strategy can be run in a dynamic pool', async () => {
303 const pool = new DynamicThreadPool(
304 min,
305 max,
306 './tests/worker-files/thread/testWorker.js',
307 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
308 )
309 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
310 const maxMultiplier = 2
311 for (let i = 0; i < max * maxMultiplier; i++) {
312 await pool.execute()
313 }
314 // We need to clean up the resources after our test
315 await pool.destroy()
316 })
317
318 it('Verify LEAST_BUSY strategy default tasks usage statistics requirements', async () => {
319 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
320 let pool = new FixedThreadPool(
321 max,
322 './tests/worker-files/thread/testWorker.js',
323 { workerChoiceStrategy }
324 )
325 expect(
326 pool.workerChoiceStrategyContext.getRequiredStatistics()
327 ).toStrictEqual({
328 runTime: true,
329 avgRunTime: false,
330 medRunTime: false,
331 waitTime: false,
332 avgWaitTime: false,
333 medWaitTime: false
334 })
335 await pool.destroy()
336 pool = new DynamicThreadPool(
337 min,
338 max,
339 './tests/worker-files/thread/testWorker.js',
340 { workerChoiceStrategy }
341 )
342 expect(
343 pool.workerChoiceStrategyContext.getRequiredStatistics()
344 ).toStrictEqual({
345 runTime: true,
346 avgRunTime: false,
347 medRunTime: false,
348 waitTime: false,
349 avgWaitTime: false,
350 medWaitTime: false
351 })
352 // We need to clean up the resources after our test
353 await pool.destroy()
354 })
355
356 it('Verify LEAST_BUSY strategy can be run in a fixed pool', async () => {
357 const pool = new FixedThreadPool(
358 max,
359 './tests/worker-files/thread/testWorker.js',
360 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
361 )
362 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
363 const maxMultiplier = 2
364 for (let i = 0; i < max * maxMultiplier; i++) {
365 await pool.execute()
366 }
367 // We need to clean up the resources after our test
368 await pool.destroy()
369 })
370
371 it('Verify LEAST_BUSY strategy can be run in a dynamic pool', async () => {
372 const pool = new DynamicThreadPool(
373 min,
374 max,
375 './tests/worker-files/thread/testWorker.js',
376 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
377 )
378 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
379 const maxMultiplier = 2
380 for (let i = 0; i < max * maxMultiplier; i++) {
381 await pool.execute()
382 }
383 // We need to clean up the resources after our test
384 await pool.destroy()
385 })
386
387 it('Verify FAIR_SHARE strategy default tasks usage statistics requirements', async () => {
388 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
389 let pool = new FixedThreadPool(
390 max,
391 './tests/worker-files/thread/testWorker.js',
392 { workerChoiceStrategy }
393 )
394 expect(
395 pool.workerChoiceStrategyContext.getRequiredStatistics()
396 ).toStrictEqual({
397 runTime: true,
398 avgRunTime: true,
399 medRunTime: false,
400 waitTime: false,
401 avgWaitTime: false,
402 medWaitTime: false
403 })
404 await pool.destroy()
405 pool = new DynamicThreadPool(
406 min,
407 max,
408 './tests/worker-files/thread/testWorker.js',
409 { workerChoiceStrategy }
410 )
411 expect(
412 pool.workerChoiceStrategyContext.getRequiredStatistics()
413 ).toStrictEqual({
414 runTime: true,
415 avgRunTime: true,
416 medRunTime: false,
417 waitTime: false,
418 avgWaitTime: false,
419 medWaitTime: false
420 })
421 // We need to clean up the resources after our test
422 await pool.destroy()
423 })
424
425 it('Verify FAIR_SHARE strategy can be run in a fixed pool', async () => {
426 const pool = new FixedThreadPool(
427 max,
428 './tests/worker-files/thread/testWorker.js',
429 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
430 )
431 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
432 const maxMultiplier = 2
433 for (let i = 0; i < max * maxMultiplier; i++) {
434 await pool.execute()
435 }
436 for (const workerNode of pool.workerNodes) {
437 expect(workerNode.tasksUsage).toStrictEqual({
438 run: maxMultiplier,
439 running: 0,
440 runTime: expect.any(Number),
441 runTimeHistory: expect.any(CircularArray),
442 avgRunTime: expect.any(Number),
443 medRunTime: 0,
444 waitTime: 0,
445 waitTimeHistory: expect.any(CircularArray),
446 avgWaitTime: 0,
447 medWaitTime: 0,
448 error: 0
449 })
450 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0)
451 }
452 expect(
453 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
454 pool.workerChoiceStrategyContext.workerChoiceStrategy
455 ).workersVirtualTaskEndTimestamp.length
456 ).toBe(pool.workerNodes.length)
457 // We need to clean up the resources after our test
458 await pool.destroy()
459 })
460
461 it('Verify FAIR_SHARE strategy can be run in a dynamic pool', async () => {
462 const pool = new DynamicThreadPool(
463 min,
464 max,
465 './tests/worker-files/thread/testWorker.js',
466 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
467 )
468 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
469 const maxMultiplier = 2
470 for (let i = 0; i < max * maxMultiplier; i++) {
471 await pool.execute()
472 }
473 for (const workerNode of pool.workerNodes) {
474 expect(workerNode.tasksUsage).toStrictEqual({
475 run: max * maxMultiplier,
476 running: 0,
477 runTime: expect.any(Number),
478 runTimeHistory: expect.any(CircularArray),
479 avgRunTime: expect.any(Number),
480 medRunTime: 0,
481 waitTime: 0,
482 waitTimeHistory: expect.any(CircularArray),
483 avgWaitTime: 0,
484 medWaitTime: 0,
485 error: 0
486 })
487 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0)
488 }
489 expect(
490 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
491 pool.workerChoiceStrategyContext.workerChoiceStrategy
492 ).workersVirtualTaskEndTimestamp.length
493 ).toBe(pool.workerNodes.length)
494 // We need to clean up the resources after our test
495 await pool.destroy()
496 })
497
498 it('Verify FAIR_SHARE strategy can be run in a dynamic pool with median runtime statistic', async () => {
499 const pool = new DynamicThreadPool(
500 min,
501 max,
502 './tests/worker-files/thread/testWorker.js',
503 {
504 workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE,
505 workerChoiceStrategyOptions: {
506 medRunTime: true
507 }
508 }
509 )
510 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
511 const maxMultiplier = 2
512 for (let i = 0; i < max * maxMultiplier; i++) {
513 await pool.execute()
514 }
515 for (const workerNode of pool.workerNodes) {
516 expect(workerNode.tasksUsage).toStrictEqual({
517 run: max * maxMultiplier,
518 running: 0,
519 runTime: expect.any(Number),
520 runTimeHistory: expect.any(CircularArray),
521 avgRunTime: 0,
522 medRunTime: expect.any(Number),
523 waitTime: 0,
524 waitTimeHistory: expect.any(CircularArray),
525 avgWaitTime: 0,
526 medWaitTime: 0,
527 error: 0
528 })
529 expect(workerNode.tasksUsage.medRunTime).toBeGreaterThanOrEqual(0)
530 }
531 expect(
532 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
533 pool.workerChoiceStrategyContext.workerChoiceStrategy
534 ).workersVirtualTaskEndTimestamp.length
535 ).toBe(pool.workerNodes.length)
536 // We need to clean up the resources after our test
537 await pool.destroy()
538 })
539
540 it('Verify FAIR_SHARE strategy internals are resets after setting it', async () => {
541 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
542 let pool = new FixedThreadPool(
543 max,
544 './tests/worker-files/thread/testWorker.js'
545 )
546 expect(
547 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
548 workerChoiceStrategy
549 ).workersVirtualTaskEndTimestamp
550 ).toBeInstanceOf(Array)
551 expect(
552 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
553 workerChoiceStrategy
554 ).workersVirtualTaskEndTimestamp.length
555 ).toBe(0)
556 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
557 workerChoiceStrategy
558 ).workersVirtualTaskEndTimestamp[0] = performance.now()
559 expect(
560 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
561 workerChoiceStrategy
562 ).workersVirtualTaskEndTimestamp.length
563 ).toBe(1)
564 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
565 expect(
566 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
567 workerChoiceStrategy
568 ).workersVirtualTaskEndTimestamp
569 ).toBeInstanceOf(Array)
570 expect(
571 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
572 workerChoiceStrategy
573 ).workersVirtualTaskEndTimestamp.length
574 ).toBe(0)
575 await pool.destroy()
576 pool = new DynamicThreadPool(
577 min,
578 max,
579 './tests/worker-files/thread/testWorker.js'
580 )
581 expect(
582 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
583 workerChoiceStrategy
584 ).workersVirtualTaskEndTimestamp
585 ).toBeInstanceOf(Array)
586 expect(
587 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
588 workerChoiceStrategy
589 ).workersVirtualTaskEndTimestamp.length
590 ).toBe(0)
591 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
592 workerChoiceStrategy
593 ).workersVirtualTaskEndTimestamp[0] = performance.now()
594 expect(
595 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
596 workerChoiceStrategy
597 ).workersVirtualTaskEndTimestamp.length
598 ).toBe(1)
599 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
600 expect(
601 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
602 workerChoiceStrategy
603 ).workersVirtualTaskEndTimestamp
604 ).toBeInstanceOf(Array)
605 expect(
606 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
607 workerChoiceStrategy
608 ).workersVirtualTaskEndTimestamp.length
609 ).toBe(0)
610 // We need to clean up the resources after our test
611 await pool.destroy()
612 })
613
614 it('Verify WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
615 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
616 let pool = new FixedThreadPool(
617 max,
618 './tests/worker-files/thread/testWorker.js',
619 { workerChoiceStrategy }
620 )
621 expect(
622 pool.workerChoiceStrategyContext.getRequiredStatistics()
623 ).toStrictEqual({
624 runTime: true,
625 avgRunTime: true,
626 medRunTime: false,
627 waitTime: false,
628 avgWaitTime: false,
629 medWaitTime: false
630 })
631 await pool.destroy()
632 pool = new DynamicThreadPool(
633 min,
634 max,
635 './tests/worker-files/thread/testWorker.js',
636 { workerChoiceStrategy }
637 )
638 expect(
639 pool.workerChoiceStrategyContext.getRequiredStatistics()
640 ).toStrictEqual({
641 runTime: true,
642 avgRunTime: true,
643 medRunTime: false,
644 waitTime: false,
645 avgWaitTime: false,
646 medWaitTime: false
647 })
648 // We need to clean up the resources after our test
649 await pool.destroy()
650 })
651
652 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
653 const pool = new FixedThreadPool(
654 max,
655 './tests/worker-files/thread/testWorker.js',
656 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
657 )
658 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
659 const maxMultiplier = 2
660 for (let i = 0; i < max * maxMultiplier; i++) {
661 await pool.execute()
662 }
663 for (const workerNode of pool.workerNodes) {
664 expect(workerNode.tasksUsage).toStrictEqual({
665 // FIXME: it should be:
666 // run: max * maxMultiplier,
667 run: expect.any(Number),
668 running: 0,
669 runTime: expect.any(Number),
670 runTimeHistory: expect.any(CircularArray),
671 avgRunTime: expect.any(Number),
672 medRunTime: 0,
673 waitTime: 0,
674 waitTimeHistory: expect.any(CircularArray),
675 avgWaitTime: 0,
676 medWaitTime: 0,
677 error: 0
678 })
679 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0)
680 }
681 expect(
682 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
683 pool.workerChoiceStrategyContext.workerChoiceStrategy
684 ).defaultWorkerWeight
685 ).toBeGreaterThan(0)
686 expect(
687 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
688 pool.workerChoiceStrategyContext.workerChoiceStrategy
689 ).workerVirtualTaskRunTime
690 ).toBeGreaterThanOrEqual(0)
691 // We need to clean up the resources after our test
692 await pool.destroy()
693 })
694
695 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
696 const pool = new DynamicThreadPool(
697 min,
698 max,
699 './tests/worker-files/thread/testWorker.js',
700 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
701 )
702 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
703 const maxMultiplier = 2
704 for (let i = 0; i < max * maxMultiplier; i++) {
705 await pool.execute()
706 }
707 for (const workerNode of pool.workerNodes) {
708 expect(workerNode.tasksUsage).toStrictEqual({
709 run: max * maxMultiplier,
710 running: 0,
711 runTime: expect.any(Number),
712 runTimeHistory: expect.any(CircularArray),
713 avgRunTime: expect.any(Number),
714 medRunTime: 0,
715 waitTime: 0,
716 waitTimeHistory: expect.any(CircularArray),
717 avgWaitTime: 0,
718 medWaitTime: 0,
719 error: 0
720 })
721 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0)
722 }
723 expect(
724 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
725 pool.workerChoiceStrategyContext.workerChoiceStrategy
726 ).defaultWorkerWeight
727 ).toBeGreaterThan(0)
728 expect(
729 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
730 pool.workerChoiceStrategyContext.workerChoiceStrategy
731 ).workerVirtualTaskRunTime
732 ).toBeGreaterThanOrEqual(0)
733 // We need to clean up the resources after our test
734 await pool.destroy()
735 })
736
737 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool with median runtime statistic', async () => {
738 const pool = new DynamicThreadPool(
739 min,
740 max,
741 './tests/worker-files/thread/testWorker.js',
742 {
743 workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN,
744 workerChoiceStrategyOptions: {
745 medRunTime: true
746 }
747 }
748 )
749 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
750 const maxMultiplier = 2
751 for (let i = 0; i < max * maxMultiplier; i++) {
752 await pool.execute()
753 }
754 for (const workerNode of pool.workerNodes) {
755 expect(workerNode.tasksUsage).toStrictEqual({
756 run: max * maxMultiplier,
757 running: 0,
758 runTime: expect.any(Number),
759 runTimeHistory: expect.any(CircularArray),
760 avgRunTime: 0,
761 medRunTime: expect.any(Number),
762 waitTime: 0,
763 waitTimeHistory: expect.any(CircularArray),
764 avgWaitTime: 0,
765 medWaitTime: 0,
766 error: 0
767 })
768 expect(workerNode.tasksUsage.medRunTime).toBeGreaterThanOrEqual(0)
769 }
770 expect(
771 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
772 pool.workerChoiceStrategyContext.workerChoiceStrategy
773 ).defaultWorkerWeight
774 ).toBeGreaterThan(0)
775 expect(
776 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
777 pool.workerChoiceStrategyContext.workerChoiceStrategy
778 ).workerVirtualTaskRunTime
779 ).toBeGreaterThanOrEqual(0)
780 // We need to clean up the resources after our test
781 await pool.destroy()
782 })
783
784 it('Verify WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
785 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
786 let pool = new FixedThreadPool(
787 max,
788 './tests/worker-files/thread/testWorker.js'
789 )
790 expect(
791 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
792 workerChoiceStrategy
793 ).currentWorkerNodeId
794 ).toBeDefined()
795 expect(
796 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
797 workerChoiceStrategy
798 ).defaultWorkerWeight
799 ).toBeDefined()
800 expect(
801 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
802 workerChoiceStrategy
803 ).workerVirtualTaskRunTime
804 ).toBeDefined()
805 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
806 expect(
807 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
808 pool.workerChoiceStrategyContext.workerChoiceStrategy
809 ).currentWorkerNodeId
810 ).toBe(0)
811 expect(
812 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
813 pool.workerChoiceStrategyContext.workerChoiceStrategy
814 ).defaultWorkerWeight
815 ).toBeGreaterThan(0)
816 expect(
817 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
818 workerChoiceStrategy
819 ).workerVirtualTaskRunTime
820 ).toBe(0)
821 await pool.destroy()
822 pool = new DynamicThreadPool(
823 min,
824 max,
825 './tests/worker-files/thread/testWorker.js'
826 )
827 expect(
828 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
829 workerChoiceStrategy
830 ).currentWorkerNodeId
831 ).toBeDefined()
832 expect(
833 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
834 workerChoiceStrategy
835 ).defaultWorkerWeight
836 ).toBeDefined()
837 expect(
838 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
839 workerChoiceStrategy
840 ).workerVirtualTaskRunTime
841 ).toBeDefined()
842 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
843 expect(
844 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
845 pool.workerChoiceStrategyContext.workerChoiceStrategy
846 ).currentWorkerNodeId
847 ).toBe(0)
848 expect(
849 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
850 pool.workerChoiceStrategyContext.workerChoiceStrategy
851 ).defaultWorkerWeight
852 ).toBeGreaterThan(0)
853 expect(
854 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
855 workerChoiceStrategy
856 ).workerVirtualTaskRunTime
857 ).toBe(0)
858 // We need to clean up the resources after our test
859 await pool.destroy()
860 })
861
862 it('Verify unknown strategy throw error', () => {
863 expect(
864 () =>
865 new DynamicThreadPool(
866 min,
867 max,
868 './tests/worker-files/thread/testWorker.js',
869 { workerChoiceStrategy: 'UNKNOWN_STRATEGY' }
870 )
871 ).toThrowError("Invalid worker choice strategy 'UNKNOWN_STRATEGY'")
872 })
873 })