test: improve worker choice strategies options coverage
[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.LEAST_ELU).toBe('LEAST_ELU')
19 expect(WorkerChoiceStrategies.FAIR_SHARE).toBe('FAIR_SHARE')
20 expect(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN).toBe(
21 'WEIGHTED_ROUND_ROBIN'
22 )
23 expect(WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN).toBe(
24 'INTERLEAVED_WEIGHTED_ROUND_ROBIN'
25 )
26 })
27
28 it('Verify ROUND_ROBIN strategy is the default at pool creation', async () => {
29 const pool = new DynamicThreadPool(
30 min,
31 max,
32 './tests/worker-files/thread/testWorker.js'
33 )
34 expect(pool.opts.workerChoiceStrategy).toBe(
35 WorkerChoiceStrategies.ROUND_ROBIN
36 )
37 // We need to clean up the resources after our test
38 await pool.destroy()
39 })
40
41 it('Verify available strategies are taken at pool creation', async () => {
42 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
43 const pool = new FixedThreadPool(
44 max,
45 './tests/worker-files/thread/testWorker.js',
46 { workerChoiceStrategy }
47 )
48 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
49 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
50 workerChoiceStrategy
51 )
52 await pool.destroy()
53 }
54 })
55
56 it('Verify available strategies can be set after pool creation', async () => {
57 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
58 const pool = new DynamicThreadPool(
59 min,
60 max,
61 './tests/worker-files/thread/testWorker.js'
62 )
63 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
64 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
65 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
66 workerChoiceStrategy
67 )
68 await pool.destroy()
69 }
70 })
71
72 it('Verify available strategies default internals at pool creation', async () => {
73 const pool = new FixedThreadPool(
74 max,
75 './tests/worker-files/thread/testWorker.js'
76 )
77 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
78 if (workerChoiceStrategy === WorkerChoiceStrategies.ROUND_ROBIN) {
79 expect(
80 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
81 workerChoiceStrategy
82 ).nextWorkerNodeId
83 ).toBe(0)
84 } else if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) {
85 expect(
86 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
87 workerChoiceStrategy
88 ).workersVirtualTaskEndTimestamp
89 ).toBeInstanceOf(Array)
90 expect(
91 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
92 workerChoiceStrategy
93 ).workersVirtualTaskEndTimestamp.length
94 ).toBe(0)
95 } else if (
96 workerChoiceStrategy === WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
97 ) {
98 expect(
99 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
100 workerChoiceStrategy
101 ).nextWorkerNodeId
102 ).toBe(0)
103 expect(
104 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
105 workerChoiceStrategy
106 ).defaultWorkerWeight
107 ).toBeGreaterThan(0)
108 expect(
109 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
110 workerChoiceStrategy
111 ).workerVirtualTaskRunTime
112 ).toBe(0)
113 }
114 }
115 await pool.destroy()
116 })
117
118 it('Verify ROUND_ROBIN strategy default policy', async () => {
119 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
120 let pool = new FixedThreadPool(
121 max,
122 './tests/worker-files/thread/testWorker.js',
123 { workerChoiceStrategy }
124 )
125 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
126 useDynamicWorker: true
127 })
128 await pool.destroy()
129 pool = new DynamicThreadPool(
130 min,
131 max,
132 './tests/worker-files/thread/testWorker.js',
133 { workerChoiceStrategy }
134 )
135 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
136 useDynamicWorker: true
137 })
138 // We need to clean up the resources after our test
139 await pool.destroy()
140 })
141
142 it('Verify ROUND_ROBIN strategy default tasks statistics requirements', async () => {
143 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
144 let pool = new FixedThreadPool(
145 max,
146 './tests/worker-files/thread/testWorker.js',
147 { workerChoiceStrategy }
148 )
149 expect(
150 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
151 ).toStrictEqual({
152 runTime: {
153 aggregate: false,
154 average: false,
155 median: false
156 },
157 waitTime: {
158 aggregate: false,
159 average: false,
160 median: false
161 },
162 elu: {
163 aggregate: false,
164 average: false,
165 median: false
166 }
167 })
168 await pool.destroy()
169 pool = new DynamicThreadPool(
170 min,
171 max,
172 './tests/worker-files/thread/testWorker.js',
173 { workerChoiceStrategy }
174 )
175 expect(
176 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
177 ).toStrictEqual({
178 runTime: {
179 aggregate: false,
180 average: false,
181 median: false
182 },
183 waitTime: {
184 aggregate: false,
185 average: false,
186 median: false
187 },
188 elu: {
189 aggregate: false,
190 average: false,
191 median: false
192 }
193 })
194 // We need to clean up the resources after our test
195 await pool.destroy()
196 })
197
198 it('Verify ROUND_ROBIN strategy can be run in a fixed pool', async () => {
199 const pool = new FixedThreadPool(
200 max,
201 './tests/worker-files/thread/testWorker.js',
202 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
203 )
204 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
205 const promises = new Set()
206 const maxMultiplier = 2
207 for (let i = 0; i < max * maxMultiplier; i++) {
208 promises.add(pool.execute())
209 }
210 await Promise.all(promises)
211 for (const workerNode of pool.workerNodes) {
212 expect(workerNode.workerUsage).toStrictEqual({
213 tasks: {
214 executed: maxMultiplier,
215 executing: 0,
216 queued: 0,
217 failed: 0
218 },
219 runTime: {
220 aggregate: 0,
221 average: 0,
222 median: 0,
223 history: expect.any(CircularArray)
224 },
225 waitTime: {
226 aggregate: 0,
227 average: 0,
228 median: 0,
229 history: expect.any(CircularArray)
230 },
231 elu: {
232 idle: {
233 aggregate: 0,
234 average: 0,
235 median: 0,
236 history: expect.any(CircularArray)
237 },
238 active: {
239 aggregate: 0,
240 average: 0,
241 median: 0,
242 history: expect.any(CircularArray)
243 },
244 utilization: 0
245 }
246 })
247 }
248 expect(
249 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
250 WorkerChoiceStrategies.ROUND_ROBIN
251 ).nextWorkerNodeId
252 ).toBe(0)
253 // We need to clean up the resources after our test
254 await pool.destroy()
255 })
256
257 it('Verify ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
258 const pool = new DynamicThreadPool(
259 min,
260 max,
261 './tests/worker-files/thread/testWorker.js',
262 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
263 )
264 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
265 const promises = new Set()
266 const maxMultiplier = 2
267 for (let i = 0; i < max * maxMultiplier; i++) {
268 promises.add(pool.execute())
269 }
270 await Promise.all(promises)
271 for (const workerNode of pool.workerNodes) {
272 expect(workerNode.workerUsage).toStrictEqual({
273 tasks: {
274 executed: maxMultiplier,
275 executing: 0,
276 queued: 0,
277 failed: 0
278 },
279 runTime: {
280 aggregate: 0,
281 average: 0,
282 median: 0,
283 history: expect.any(CircularArray)
284 },
285 waitTime: {
286 aggregate: 0,
287 average: 0,
288 median: 0,
289 history: expect.any(CircularArray)
290 },
291 elu: {
292 idle: {
293 aggregate: 0,
294 average: 0,
295 median: 0,
296 history: expect.any(CircularArray)
297 },
298 active: {
299 aggregate: 0,
300 average: 0,
301 median: 0,
302 history: expect.any(CircularArray)
303 },
304 utilization: 0
305 }
306 })
307 }
308 expect(
309 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
310 WorkerChoiceStrategies.ROUND_ROBIN
311 ).nextWorkerNodeId
312 ).toBe(0)
313 // We need to clean up the resources after our test
314 await pool.destroy()
315 })
316
317 it('Verify ROUND_ROBIN strategy runtime behavior', async () => {
318 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
319 let pool = new FixedClusterPool(
320 max,
321 './tests/worker-files/cluster/testWorker.js',
322 { workerChoiceStrategy }
323 )
324 let results = new Set()
325 for (let i = 0; i < max; i++) {
326 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.id)
327 }
328 expect(results.size).toBe(max)
329 await pool.destroy()
330 pool = new FixedThreadPool(
331 max,
332 './tests/worker-files/thread/testWorker.js',
333 { workerChoiceStrategy }
334 )
335 results = new Set()
336 for (let i = 0; i < max; i++) {
337 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.threadId)
338 }
339 expect(results.size).toBe(max)
340 await pool.destroy()
341 })
342
343 it('Verify ROUND_ROBIN strategy internals are resets after setting it', async () => {
344 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
345 let pool = new FixedThreadPool(
346 max,
347 './tests/worker-files/thread/testWorker.js',
348 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
349 )
350 expect(
351 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
352 workerChoiceStrategy
353 ).nextWorkerNodeId
354 ).toBeDefined()
355 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
356 expect(
357 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
358 pool.workerChoiceStrategyContext.workerChoiceStrategy
359 ).nextWorkerNodeId
360 ).toBe(0)
361 await pool.destroy()
362 pool = new DynamicThreadPool(
363 min,
364 max,
365 './tests/worker-files/thread/testWorker.js',
366 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
367 )
368 expect(
369 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
370 workerChoiceStrategy
371 ).nextWorkerNodeId
372 ).toBeDefined()
373 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
374 expect(
375 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
376 pool.workerChoiceStrategyContext.workerChoiceStrategy
377 ).nextWorkerNodeId
378 ).toBe(0)
379 // We need to clean up the resources after our test
380 await pool.destroy()
381 })
382
383 it('Verify LEAST_USED strategy default policy', async () => {
384 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
385 let pool = new FixedThreadPool(
386 max,
387 './tests/worker-files/thread/testWorker.js',
388 { workerChoiceStrategy }
389 )
390 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
391 useDynamicWorker: false
392 })
393 await pool.destroy()
394 pool = new DynamicThreadPool(
395 min,
396 max,
397 './tests/worker-files/thread/testWorker.js',
398 { workerChoiceStrategy }
399 )
400 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
401 useDynamicWorker: false
402 })
403 // We need to clean up the resources after our test
404 await pool.destroy()
405 })
406
407 it('Verify LEAST_USED strategy default tasks statistics requirements', async () => {
408 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
409 let pool = new FixedThreadPool(
410 max,
411 './tests/worker-files/thread/testWorker.js',
412 { workerChoiceStrategy }
413 )
414 expect(
415 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
416 ).toStrictEqual({
417 runTime: {
418 aggregate: false,
419 average: false,
420 median: false
421 },
422 waitTime: {
423 aggregate: false,
424 average: false,
425 median: false
426 },
427 elu: {
428 aggregate: false,
429 average: false,
430 median: false
431 }
432 })
433 await pool.destroy()
434 pool = new DynamicThreadPool(
435 min,
436 max,
437 './tests/worker-files/thread/testWorker.js',
438 { workerChoiceStrategy }
439 )
440 expect(
441 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
442 ).toStrictEqual({
443 runTime: {
444 aggregate: false,
445 average: false,
446 median: false
447 },
448 waitTime: {
449 aggregate: false,
450 average: false,
451 median: false
452 },
453 elu: {
454 aggregate: false,
455 average: false,
456 median: false
457 }
458 })
459 // We need to clean up the resources after our test
460 await pool.destroy()
461 })
462
463 it('Verify LEAST_USED strategy can be run in a fixed pool', async () => {
464 const pool = new FixedThreadPool(
465 max,
466 './tests/worker-files/thread/testWorker.js',
467 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
468 )
469 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
470 const promises = new Set()
471 const maxMultiplier = 2
472 for (let i = 0; i < max * maxMultiplier; i++) {
473 promises.add(pool.execute())
474 }
475 await Promise.all(promises)
476 for (const workerNode of pool.workerNodes) {
477 expect(workerNode.workerUsage).toStrictEqual({
478 tasks: {
479 executed: expect.any(Number),
480 executing: 0,
481 queued: 0,
482 failed: 0
483 },
484 runTime: {
485 aggregate: 0,
486 average: 0,
487 median: 0,
488 history: expect.any(CircularArray)
489 },
490 waitTime: {
491 aggregate: 0,
492 average: 0,
493 median: 0,
494 history: expect.any(CircularArray)
495 },
496 elu: {
497 idle: {
498 aggregate: 0,
499 average: 0,
500 median: 0,
501 history: expect.any(CircularArray)
502 },
503 active: {
504 aggregate: 0,
505 average: 0,
506 median: 0,
507 history: expect.any(CircularArray)
508 },
509 utilization: 0
510 }
511 })
512 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
513 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
514 max * maxMultiplier
515 )
516 }
517 // We need to clean up the resources after our test
518 await pool.destroy()
519 })
520
521 it('Verify LEAST_USED strategy can be run in a dynamic pool', async () => {
522 const pool = new DynamicThreadPool(
523 min,
524 max,
525 './tests/worker-files/thread/testWorker.js',
526 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
527 )
528 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
529 const promises = new Set()
530 const maxMultiplier = 2
531 for (let i = 0; i < max * maxMultiplier; i++) {
532 promises.add(pool.execute())
533 }
534 await Promise.all(promises)
535 for (const workerNode of pool.workerNodes) {
536 expect(workerNode.workerUsage).toStrictEqual({
537 tasks: {
538 executed: expect.any(Number),
539 executing: 0,
540 queued: 0,
541 failed: 0
542 },
543 runTime: {
544 aggregate: 0,
545 average: 0,
546 median: 0,
547 history: expect.any(CircularArray)
548 },
549 waitTime: {
550 aggregate: 0,
551 average: 0,
552 median: 0,
553 history: expect.any(CircularArray)
554 },
555 elu: {
556 idle: {
557 aggregate: 0,
558 average: 0,
559 median: 0,
560 history: expect.any(CircularArray)
561 },
562 active: {
563 aggregate: 0,
564 average: 0,
565 median: 0,
566 history: expect.any(CircularArray)
567 },
568 utilization: 0
569 }
570 })
571 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
572 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
573 max * maxMultiplier
574 )
575 }
576 // We need to clean up the resources after our test
577 await pool.destroy()
578 })
579
580 it('Verify LEAST_BUSY strategy default policy', async () => {
581 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
582 let pool = new FixedThreadPool(
583 max,
584 './tests/worker-files/thread/testWorker.js',
585 { workerChoiceStrategy }
586 )
587 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
588 useDynamicWorker: false
589 })
590 await pool.destroy()
591 pool = new DynamicThreadPool(
592 min,
593 max,
594 './tests/worker-files/thread/testWorker.js',
595 { workerChoiceStrategy }
596 )
597 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
598 useDynamicWorker: false
599 })
600 // We need to clean up the resources after our test
601 await pool.destroy()
602 })
603
604 it('Verify LEAST_BUSY strategy default tasks statistics requirements', async () => {
605 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
606 let pool = new FixedThreadPool(
607 max,
608 './tests/worker-files/thread/testWorker.js',
609 { workerChoiceStrategy }
610 )
611 expect(
612 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
613 ).toStrictEqual({
614 runTime: {
615 aggregate: true,
616 average: false,
617 median: false
618 },
619 waitTime: {
620 aggregate: true,
621 average: false,
622 median: false
623 },
624 elu: {
625 aggregate: false,
626 average: false,
627 median: false
628 }
629 })
630 await pool.destroy()
631 pool = new DynamicThreadPool(
632 min,
633 max,
634 './tests/worker-files/thread/testWorker.js',
635 { workerChoiceStrategy }
636 )
637 expect(
638 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
639 ).toStrictEqual({
640 runTime: {
641 aggregate: true,
642 average: false,
643 median: false
644 },
645 waitTime: {
646 aggregate: true,
647 average: false,
648 median: false
649 },
650 elu: {
651 aggregate: false,
652 average: false,
653 median: false
654 }
655 })
656 // We need to clean up the resources after our test
657 await pool.destroy()
658 })
659
660 it('Verify LEAST_BUSY strategy can be run in a fixed pool', async () => {
661 const pool = new FixedThreadPool(
662 max,
663 './tests/worker-files/thread/testWorker.js',
664 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
665 )
666 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
667 const promises = new Set()
668 const maxMultiplier = 2
669 for (let i = 0; i < max * maxMultiplier; i++) {
670 promises.add(pool.execute())
671 }
672 await Promise.all(promises)
673 for (const workerNode of pool.workerNodes) {
674 expect(workerNode.workerUsage).toStrictEqual({
675 tasks: {
676 executed: expect.any(Number),
677 executing: 0,
678 queued: 0,
679 failed: 0
680 },
681 runTime: {
682 aggregate: expect.any(Number),
683 average: 0,
684 median: 0,
685 history: expect.any(CircularArray)
686 },
687 waitTime: {
688 aggregate: expect.any(Number),
689 average: 0,
690 median: 0,
691 history: expect.any(CircularArray)
692 },
693 elu: {
694 idle: {
695 aggregate: 0,
696 average: 0,
697 median: 0,
698 history: expect.any(CircularArray)
699 },
700 active: {
701 aggregate: 0,
702 average: 0,
703 median: 0,
704 history: expect.any(CircularArray)
705 },
706 utilization: 0
707 }
708 })
709 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
710 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
711 max * maxMultiplier
712 )
713 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThanOrEqual(0)
714 expect(workerNode.workerUsage.waitTime.aggregate).toBeGreaterThanOrEqual(
715 0
716 )
717 }
718 // We need to clean up the resources after our test
719 await pool.destroy()
720 })
721
722 it('Verify LEAST_BUSY strategy can be run in a dynamic pool', async () => {
723 const pool = new DynamicThreadPool(
724 min,
725 max,
726 './tests/worker-files/thread/testWorker.js',
727 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
728 )
729 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
730 const promises = new Set()
731 const maxMultiplier = 2
732 for (let i = 0; i < max * maxMultiplier; i++) {
733 promises.add(pool.execute())
734 }
735 await Promise.all(promises)
736 for (const workerNode of pool.workerNodes) {
737 expect(workerNode.workerUsage).toStrictEqual({
738 tasks: {
739 executed: expect.any(Number),
740 executing: 0,
741 queued: 0,
742 failed: 0
743 },
744 runTime: {
745 aggregate: expect.any(Number),
746 average: 0,
747 median: 0,
748 history: expect.any(CircularArray)
749 },
750 waitTime: {
751 aggregate: expect.any(Number),
752 average: 0,
753 median: 0,
754 history: expect.any(CircularArray)
755 },
756 elu: {
757 idle: {
758 aggregate: 0,
759 average: 0,
760 median: 0,
761 history: expect.any(CircularArray)
762 },
763 active: {
764 aggregate: 0,
765 average: 0,
766 median: 0,
767 history: expect.any(CircularArray)
768 },
769 utilization: 0
770 }
771 })
772 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
773 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
774 max * maxMultiplier
775 )
776 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThanOrEqual(0)
777 expect(workerNode.workerUsage.waitTime.aggregate).toBeGreaterThanOrEqual(
778 0
779 )
780 }
781 // We need to clean up the resources after our test
782 await pool.destroy()
783 })
784
785 it('Verify LEAST_ELU strategy default policy', async () => {
786 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
787 let pool = new FixedThreadPool(
788 max,
789 './tests/worker-files/thread/testWorker.js',
790 { workerChoiceStrategy }
791 )
792 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
793 useDynamicWorker: false
794 })
795 await pool.destroy()
796 pool = new DynamicThreadPool(
797 min,
798 max,
799 './tests/worker-files/thread/testWorker.js',
800 { workerChoiceStrategy }
801 )
802 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
803 useDynamicWorker: false
804 })
805 // We need to clean up the resources after our test
806 await pool.destroy()
807 })
808
809 it('Verify LEAST_ELU strategy default tasks statistics requirements', async () => {
810 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
811 let pool = new FixedThreadPool(
812 max,
813 './tests/worker-files/thread/testWorker.js',
814 { workerChoiceStrategy }
815 )
816 expect(
817 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
818 ).toStrictEqual({
819 runTime: {
820 aggregate: false,
821 average: false,
822 median: false
823 },
824 waitTime: {
825 aggregate: false,
826 average: false,
827 median: false
828 },
829 elu: {
830 aggregate: true,
831 average: false,
832 median: false
833 }
834 })
835 await pool.destroy()
836 pool = new DynamicThreadPool(
837 min,
838 max,
839 './tests/worker-files/thread/testWorker.js',
840 { workerChoiceStrategy }
841 )
842 expect(
843 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
844 ).toStrictEqual({
845 runTime: {
846 aggregate: false,
847 average: false,
848 median: false
849 },
850 waitTime: {
851 aggregate: false,
852 average: false,
853 median: false
854 },
855 elu: {
856 aggregate: true,
857 average: false,
858 median: false
859 }
860 })
861 // We need to clean up the resources after our test
862 await pool.destroy()
863 })
864
865 it('Verify LEAST_ELU strategy can be run in a fixed pool', async () => {
866 const pool = new FixedThreadPool(
867 max,
868 './tests/worker-files/thread/testWorker.js',
869 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_ELU }
870 )
871 // TODO: Create a better test to cover `LeastEluWorkerChoiceStrategy#choose`
872 const promises = new Set()
873 const maxMultiplier = 2
874 for (let i = 0; i < max * maxMultiplier; i++) {
875 promises.add(pool.execute())
876 }
877 await Promise.all(promises)
878 for (const workerNode of pool.workerNodes) {
879 expect(workerNode.workerUsage).toStrictEqual({
880 tasks: {
881 executed: expect.any(Number),
882 executing: 0,
883 queued: 0,
884 failed: 0
885 },
886 runTime: {
887 aggregate: 0,
888 average: 0,
889 median: 0,
890 history: expect.any(CircularArray)
891 },
892 waitTime: {
893 aggregate: 0,
894 average: 0,
895 median: 0,
896 history: expect.any(CircularArray)
897 },
898 elu: {
899 idle: {
900 aggregate: 0,
901 average: 0,
902 median: 0,
903 history: expect.any(CircularArray)
904 },
905 active: {
906 aggregate: expect.any(Number),
907 average: 0,
908 median: 0,
909 history: expect.any(CircularArray)
910 },
911 utilization: expect.any(Number)
912 }
913 })
914 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
915 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
916 max * maxMultiplier
917 )
918 expect(workerNode.workerUsage.elu.utilization).toBeGreaterThanOrEqual(0)
919 expect(workerNode.workerUsage.elu.utilization).toBeLessThanOrEqual(1)
920 }
921 // We need to clean up the resources after our test
922 await pool.destroy()
923 })
924
925 it('Verify LEAST_ELU strategy can be run in a dynamic pool', async () => {
926 const pool = new DynamicThreadPool(
927 min,
928 max,
929 './tests/worker-files/thread/testWorker.js',
930 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_ELU }
931 )
932 // TODO: Create a better test to cover `LeastEluWorkerChoiceStrategy#choose`
933 const promises = new Set()
934 const maxMultiplier = 2
935 for (let i = 0; i < max * maxMultiplier; i++) {
936 promises.add(pool.execute())
937 }
938 await Promise.all(promises)
939 for (const workerNode of pool.workerNodes) {
940 expect(workerNode.workerUsage).toStrictEqual({
941 tasks: {
942 executed: expect.any(Number),
943 executing: 0,
944 queued: 0,
945 failed: 0
946 },
947 runTime: {
948 aggregate: 0,
949 average: 0,
950 median: 0,
951 history: expect.any(CircularArray)
952 },
953 waitTime: {
954 aggregate: 0,
955 average: 0,
956 median: 0,
957 history: expect.any(CircularArray)
958 },
959 elu: {
960 idle: {
961 aggregate: 0,
962 average: 0,
963 median: 0,
964 history: expect.any(CircularArray)
965 },
966 active: {
967 aggregate: expect.any(Number),
968 average: 0,
969 median: 0,
970 history: expect.any(CircularArray)
971 },
972 utilization: expect.any(Number)
973 }
974 })
975 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
976 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
977 max * maxMultiplier
978 )
979 expect(workerNode.workerUsage.elu.utilization).toBeGreaterThanOrEqual(0)
980 expect(workerNode.workerUsage.elu.utilization).toBeLessThanOrEqual(1)
981 }
982 // We need to clean up the resources after our test
983 await pool.destroy()
984 })
985
986 it('Verify FAIR_SHARE strategy default policy', async () => {
987 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
988 let pool = new FixedThreadPool(
989 max,
990 './tests/worker-files/thread/testWorker.js',
991 { workerChoiceStrategy }
992 )
993 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
994 useDynamicWorker: false
995 })
996 await pool.destroy()
997 pool = new DynamicThreadPool(
998 min,
999 max,
1000 './tests/worker-files/thread/testWorker.js',
1001 { workerChoiceStrategy }
1002 )
1003 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
1004 useDynamicWorker: false
1005 })
1006 // We need to clean up the resources after our test
1007 await pool.destroy()
1008 })
1009
1010 it('Verify FAIR_SHARE strategy default tasks statistics requirements', async () => {
1011 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
1012 let pool = new FixedThreadPool(
1013 max,
1014 './tests/worker-files/thread/testWorker.js',
1015 { workerChoiceStrategy }
1016 )
1017 expect(
1018 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1019 ).toStrictEqual({
1020 runTime: {
1021 aggregate: true,
1022 average: true,
1023 median: false
1024 },
1025 waitTime: {
1026 aggregate: false,
1027 average: false,
1028 median: false
1029 },
1030 elu: {
1031 aggregate: true,
1032 average: true,
1033 median: false
1034 }
1035 })
1036 await pool.destroy()
1037 pool = new DynamicThreadPool(
1038 min,
1039 max,
1040 './tests/worker-files/thread/testWorker.js',
1041 { workerChoiceStrategy }
1042 )
1043 expect(
1044 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1045 ).toStrictEqual({
1046 runTime: {
1047 aggregate: true,
1048 average: true,
1049 median: false
1050 },
1051 waitTime: {
1052 aggregate: false,
1053 average: false,
1054 median: false
1055 },
1056 elu: {
1057 aggregate: true,
1058 average: true,
1059 median: false
1060 }
1061 })
1062 // We need to clean up the resources after our test
1063 await pool.destroy()
1064 })
1065
1066 it('Verify FAIR_SHARE strategy can be run in a fixed pool', async () => {
1067 const pool = new FixedThreadPool(
1068 max,
1069 './tests/worker-files/thread/testWorker.js',
1070 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
1071 )
1072 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
1073 const promises = new Set()
1074 const maxMultiplier = 2
1075 for (let i = 0; i < max * maxMultiplier; i++) {
1076 promises.add(pool.execute())
1077 }
1078 await Promise.all(promises)
1079 for (const workerNode of pool.workerNodes) {
1080 expect(workerNode.workerUsage).toStrictEqual({
1081 tasks: {
1082 executed: expect.any(Number),
1083 executing: 0,
1084 queued: 0,
1085 failed: 0
1086 },
1087 runTime: {
1088 aggregate: expect.any(Number),
1089 average: expect.any(Number),
1090 median: 0,
1091 history: expect.any(CircularArray)
1092 },
1093 waitTime: {
1094 aggregate: 0,
1095 average: 0,
1096 median: 0,
1097 history: expect.any(CircularArray)
1098 },
1099 elu: {
1100 idle: {
1101 aggregate: 0,
1102 average: 0,
1103 median: 0,
1104 history: expect.any(CircularArray)
1105 },
1106 active: {
1107 aggregate: expect.any(Number),
1108 average: expect.any(Number),
1109 median: 0,
1110 history: expect.any(CircularArray)
1111 },
1112 utilization: expect.any(Number)
1113 }
1114 })
1115 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
1116 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
1117 max * maxMultiplier
1118 )
1119 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThanOrEqual(0)
1120 expect(workerNode.workerUsage.runTime.average).toBeGreaterThanOrEqual(0)
1121 expect(workerNode.workerUsage.elu.utilization).toBeGreaterThanOrEqual(0)
1122 expect(workerNode.workerUsage.elu.utilization).toBeLessThanOrEqual(1)
1123 }
1124 expect(
1125 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1126 pool.workerChoiceStrategyContext.workerChoiceStrategy
1127 ).workersVirtualTaskEndTimestamp.length
1128 ).toBe(pool.workerNodes.length)
1129 // We need to clean up the resources after our test
1130 await pool.destroy()
1131 })
1132
1133 it('Verify FAIR_SHARE strategy can be run in a dynamic pool', async () => {
1134 const pool = new DynamicThreadPool(
1135 min,
1136 max,
1137 './tests/worker-files/thread/testWorker.js',
1138 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
1139 )
1140 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
1141 const promises = new Set()
1142 const maxMultiplier = 2
1143 for (let i = 0; i < max * maxMultiplier; i++) {
1144 promises.add(pool.execute())
1145 }
1146 await Promise.all(promises)
1147 for (const workerNode of pool.workerNodes) {
1148 expect(workerNode.workerUsage).toStrictEqual({
1149 tasks: {
1150 executed: expect.any(Number),
1151 executing: 0,
1152 queued: 0,
1153 failed: 0
1154 },
1155 runTime: {
1156 aggregate: expect.any(Number),
1157 average: expect.any(Number),
1158 median: 0,
1159 history: expect.any(CircularArray)
1160 },
1161 waitTime: {
1162 aggregate: 0,
1163 average: 0,
1164 median: 0,
1165 history: expect.any(CircularArray)
1166 },
1167 elu: {
1168 idle: {
1169 aggregate: 0,
1170 average: 0,
1171 median: 0,
1172 history: expect.any(CircularArray)
1173 },
1174 active: {
1175 aggregate: expect.any(Number),
1176 average: expect.any(Number),
1177 median: 0,
1178 history: expect.any(CircularArray)
1179 },
1180 utilization: expect.any(Number)
1181 }
1182 })
1183 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
1184 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
1185 max * maxMultiplier
1186 )
1187 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThanOrEqual(0)
1188 expect(workerNode.workerUsage.runTime.average).toBeGreaterThanOrEqual(0)
1189 expect(workerNode.workerUsage.elu.utilization).toBeGreaterThanOrEqual(0)
1190 expect(workerNode.workerUsage.elu.utilization).toBeLessThanOrEqual(1)
1191 }
1192 expect(
1193 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1194 pool.workerChoiceStrategyContext.workerChoiceStrategy
1195 ).workersVirtualTaskEndTimestamp.length
1196 ).toBe(pool.workerNodes.length)
1197 // We need to clean up the resources after our test
1198 await pool.destroy()
1199 })
1200
1201 it('Verify FAIR_SHARE strategy can be run in a dynamic pool with median runtime statistic', async () => {
1202 const pool = new DynamicThreadPool(
1203 min,
1204 max,
1205 './tests/worker-files/thread/testWorker.js',
1206 {
1207 workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE,
1208 workerChoiceStrategyOptions: {
1209 runTime: { median: true }
1210 }
1211 }
1212 )
1213 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
1214 const promises = new Set()
1215 const maxMultiplier = 2
1216 for (let i = 0; i < max * maxMultiplier; i++) {
1217 promises.add(pool.execute())
1218 }
1219 await Promise.all(promises)
1220 for (const workerNode of pool.workerNodes) {
1221 expect(workerNode.workerUsage).toStrictEqual({
1222 tasks: {
1223 executed: expect.any(Number),
1224 executing: 0,
1225 queued: 0,
1226 failed: 0
1227 },
1228 runTime: {
1229 aggregate: expect.any(Number),
1230 average: 0,
1231 median: expect.any(Number),
1232 history: expect.any(CircularArray)
1233 },
1234 waitTime: {
1235 aggregate: 0,
1236 average: 0,
1237 median: 0,
1238 history: expect.any(CircularArray)
1239 },
1240 elu: {
1241 idle: {
1242 aggregate: 0,
1243 average: 0,
1244 median: 0,
1245 history: expect.any(CircularArray)
1246 },
1247 active: {
1248 aggregate: expect.any(Number),
1249 average: expect.any(Number),
1250 median: 0,
1251 history: expect.any(CircularArray)
1252 },
1253 utilization: expect.any(Number)
1254 }
1255 })
1256 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
1257 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
1258 max * maxMultiplier
1259 )
1260 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThanOrEqual(0)
1261 expect(workerNode.workerUsage.runTime.median).toBeGreaterThanOrEqual(0)
1262 expect(workerNode.workerUsage.elu.utilization).toBeGreaterThanOrEqual(0)
1263 expect(workerNode.workerUsage.elu.utilization).toBeLessThanOrEqual(1)
1264 }
1265 expect(
1266 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1267 pool.workerChoiceStrategyContext.workerChoiceStrategy
1268 ).workersVirtualTaskEndTimestamp.length
1269 ).toBe(pool.workerNodes.length)
1270 // We need to clean up the resources after our test
1271 await pool.destroy()
1272 })
1273
1274 it('Verify FAIR_SHARE strategy internals are resets after setting it', async () => {
1275 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
1276 let pool = new FixedThreadPool(
1277 max,
1278 './tests/worker-files/thread/testWorker.js'
1279 )
1280 expect(
1281 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1282 workerChoiceStrategy
1283 ).workersVirtualTaskEndTimestamp
1284 ).toBeInstanceOf(Array)
1285 expect(
1286 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1287 workerChoiceStrategy
1288 ).workersVirtualTaskEndTimestamp.length
1289 ).toBe(0)
1290 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1291 workerChoiceStrategy
1292 ).workersVirtualTaskEndTimestamp[0] = performance.now()
1293 expect(
1294 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1295 workerChoiceStrategy
1296 ).workersVirtualTaskEndTimestamp.length
1297 ).toBe(1)
1298 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1299 expect(
1300 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1301 workerChoiceStrategy
1302 ).workersVirtualTaskEndTimestamp
1303 ).toBeInstanceOf(Array)
1304 expect(
1305 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1306 workerChoiceStrategy
1307 ).workersVirtualTaskEndTimestamp.length
1308 ).toBe(0)
1309 await pool.destroy()
1310 pool = new DynamicThreadPool(
1311 min,
1312 max,
1313 './tests/worker-files/thread/testWorker.js'
1314 )
1315 expect(
1316 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1317 workerChoiceStrategy
1318 ).workersVirtualTaskEndTimestamp
1319 ).toBeInstanceOf(Array)
1320 expect(
1321 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1322 workerChoiceStrategy
1323 ).workersVirtualTaskEndTimestamp.length
1324 ).toBe(0)
1325 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1326 workerChoiceStrategy
1327 ).workersVirtualTaskEndTimestamp[0] = performance.now()
1328 expect(
1329 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1330 workerChoiceStrategy
1331 ).workersVirtualTaskEndTimestamp.length
1332 ).toBe(1)
1333 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1334 expect(
1335 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1336 workerChoiceStrategy
1337 ).workersVirtualTaskEndTimestamp
1338 ).toBeInstanceOf(Array)
1339 expect(
1340 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1341 workerChoiceStrategy
1342 ).workersVirtualTaskEndTimestamp.length
1343 ).toBe(0)
1344 // We need to clean up the resources after our test
1345 await pool.destroy()
1346 })
1347
1348 it('Verify WEIGHTED_ROUND_ROBIN strategy default policy', async () => {
1349 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
1350 let pool = new FixedThreadPool(
1351 max,
1352 './tests/worker-files/thread/testWorker.js',
1353 { workerChoiceStrategy }
1354 )
1355 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
1356 useDynamicWorker: true
1357 })
1358 await pool.destroy()
1359 pool = new DynamicThreadPool(
1360 min,
1361 max,
1362 './tests/worker-files/thread/testWorker.js',
1363 { workerChoiceStrategy }
1364 )
1365 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
1366 useDynamicWorker: true
1367 })
1368 // We need to clean up the resources after our test
1369 await pool.destroy()
1370 })
1371
1372 it('Verify WEIGHTED_ROUND_ROBIN strategy default tasks statistics requirements', async () => {
1373 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
1374 let pool = new FixedThreadPool(
1375 max,
1376 './tests/worker-files/thread/testWorker.js',
1377 { workerChoiceStrategy }
1378 )
1379 expect(
1380 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1381 ).toStrictEqual({
1382 runTime: {
1383 aggregate: true,
1384 average: true,
1385 median: false
1386 },
1387 waitTime: {
1388 aggregate: false,
1389 average: false,
1390 median: false
1391 },
1392 elu: {
1393 aggregate: false,
1394 average: false,
1395 median: false
1396 }
1397 })
1398 await pool.destroy()
1399 pool = new DynamicThreadPool(
1400 min,
1401 max,
1402 './tests/worker-files/thread/testWorker.js',
1403 { workerChoiceStrategy }
1404 )
1405 expect(
1406 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1407 ).toStrictEqual({
1408 runTime: {
1409 aggregate: true,
1410 average: true,
1411 median: false
1412 },
1413 waitTime: {
1414 aggregate: false,
1415 average: false,
1416 median: false
1417 },
1418 elu: {
1419 aggregate: false,
1420 average: false,
1421 median: false
1422 }
1423 })
1424 // We need to clean up the resources after our test
1425 await pool.destroy()
1426 })
1427
1428 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
1429 const pool = new FixedThreadPool(
1430 max,
1431 './tests/worker-files/thread/testWorker.js',
1432 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
1433 )
1434 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
1435 const promises = new Set()
1436 const maxMultiplier = 2
1437 for (let i = 0; i < max * maxMultiplier; i++) {
1438 promises.add(pool.execute())
1439 }
1440 await Promise.all(promises)
1441 for (const workerNode of pool.workerNodes) {
1442 expect(workerNode.workerUsage).toStrictEqual({
1443 tasks: {
1444 executed: expect.any(Number),
1445 executing: 0,
1446 queued: 0,
1447 failed: 0
1448 },
1449 runTime: {
1450 aggregate: expect.any(Number),
1451 average: expect.any(Number),
1452 median: 0,
1453 history: expect.any(CircularArray)
1454 },
1455 waitTime: {
1456 aggregate: 0,
1457 average: 0,
1458 median: 0,
1459 history: expect.any(CircularArray)
1460 },
1461 elu: {
1462 idle: {
1463 aggregate: 0,
1464 average: 0,
1465 median: 0,
1466 history: expect.any(CircularArray)
1467 },
1468 active: {
1469 aggregate: 0,
1470 average: 0,
1471 median: 0,
1472 history: expect.any(CircularArray)
1473 },
1474 utilization: 0
1475 }
1476 })
1477 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
1478 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
1479 max * maxMultiplier
1480 )
1481 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThanOrEqual(0)
1482 expect(workerNode.workerUsage.runTime.average).toBeGreaterThanOrEqual(0)
1483 }
1484 expect(
1485 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1486 pool.workerChoiceStrategyContext.workerChoiceStrategy
1487 ).defaultWorkerWeight
1488 ).toBeGreaterThan(0)
1489 expect(
1490 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1491 pool.workerChoiceStrategyContext.workerChoiceStrategy
1492 ).workerVirtualTaskRunTime
1493 ).toBeGreaterThanOrEqual(0)
1494 // We need to clean up the resources after our test
1495 await pool.destroy()
1496 })
1497
1498 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
1499 const pool = new DynamicThreadPool(
1500 min,
1501 max,
1502 './tests/worker-files/thread/testWorker.js',
1503 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
1504 )
1505 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
1506 const promises = new Set()
1507 const maxMultiplier = 2
1508 for (let i = 0; i < max * maxMultiplier; i++) {
1509 promises.add(pool.execute())
1510 }
1511 await Promise.all(promises)
1512 for (const workerNode of pool.workerNodes) {
1513 expect(workerNode.workerUsage).toStrictEqual({
1514 tasks: {
1515 executed: expect.any(Number),
1516 executing: 0,
1517 queued: 0,
1518 failed: 0
1519 },
1520 runTime: {
1521 aggregate: expect.any(Number),
1522 average: expect.any(Number),
1523 median: 0,
1524 history: expect.any(CircularArray)
1525 },
1526 waitTime: {
1527 aggregate: 0,
1528 average: 0,
1529 median: 0,
1530 history: expect.any(CircularArray)
1531 },
1532 elu: {
1533 idle: {
1534 aggregate: 0,
1535 average: 0,
1536 median: 0,
1537 history: expect.any(CircularArray)
1538 },
1539 active: {
1540 aggregate: 0,
1541 average: 0,
1542 median: 0,
1543 history: expect.any(CircularArray)
1544 },
1545 utilization: 0
1546 }
1547 })
1548 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
1549 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
1550 max * maxMultiplier
1551 )
1552 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0)
1553 expect(workerNode.workerUsage.runTime.average).toBeGreaterThan(0)
1554 }
1555 expect(
1556 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1557 pool.workerChoiceStrategyContext.workerChoiceStrategy
1558 ).defaultWorkerWeight
1559 ).toBeGreaterThan(0)
1560 expect(
1561 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1562 pool.workerChoiceStrategyContext.workerChoiceStrategy
1563 ).workerVirtualTaskRunTime
1564 ).toBeGreaterThanOrEqual(0)
1565 // We need to clean up the resources after our test
1566 await pool.destroy()
1567 })
1568
1569 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool with median runtime statistic', async () => {
1570 const pool = new DynamicThreadPool(
1571 min,
1572 max,
1573 './tests/worker-files/thread/testWorker.js',
1574 {
1575 workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN,
1576 workerChoiceStrategyOptions: {
1577 runTime: { median: true }
1578 }
1579 }
1580 )
1581 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
1582 const promises = new Set()
1583 const maxMultiplier = 2
1584 for (let i = 0; i < max * maxMultiplier; i++) {
1585 promises.add(pool.execute())
1586 }
1587 await Promise.all(promises)
1588 for (const workerNode of pool.workerNodes) {
1589 expect(workerNode.workerUsage).toStrictEqual({
1590 tasks: {
1591 executed: expect.any(Number),
1592 executing: 0,
1593 queued: 0,
1594 failed: 0
1595 },
1596 runTime: {
1597 aggregate: expect.any(Number),
1598 average: 0,
1599 median: expect.any(Number),
1600 history: expect.any(CircularArray)
1601 },
1602 waitTime: {
1603 aggregate: 0,
1604 average: 0,
1605 median: 0,
1606 history: expect.any(CircularArray)
1607 },
1608 elu: {
1609 idle: {
1610 aggregate: 0,
1611 average: 0,
1612 median: 0,
1613 history: expect.any(CircularArray)
1614 },
1615 active: {
1616 aggregate: 0,
1617 average: 0,
1618 median: 0,
1619 history: expect.any(CircularArray)
1620 },
1621 utilization: 0
1622 }
1623 })
1624 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
1625 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
1626 max * maxMultiplier
1627 )
1628 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0)
1629 expect(workerNode.workerUsage.runTime.median).toBeGreaterThan(0)
1630 }
1631 expect(
1632 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1633 pool.workerChoiceStrategyContext.workerChoiceStrategy
1634 ).defaultWorkerWeight
1635 ).toBeGreaterThan(0)
1636 expect(
1637 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1638 pool.workerChoiceStrategyContext.workerChoiceStrategy
1639 ).workerVirtualTaskRunTime
1640 ).toBeGreaterThanOrEqual(0)
1641 // We need to clean up the resources after our test
1642 await pool.destroy()
1643 })
1644
1645 it('Verify WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
1646 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
1647 let pool = new FixedThreadPool(
1648 max,
1649 './tests/worker-files/thread/testWorker.js'
1650 )
1651 expect(
1652 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1653 workerChoiceStrategy
1654 ).nextWorkerNodeId
1655 ).toBeDefined()
1656 expect(
1657 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1658 workerChoiceStrategy
1659 ).defaultWorkerWeight
1660 ).toBeDefined()
1661 expect(
1662 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1663 workerChoiceStrategy
1664 ).workerVirtualTaskRunTime
1665 ).toBeDefined()
1666 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1667 expect(
1668 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1669 pool.workerChoiceStrategyContext.workerChoiceStrategy
1670 ).nextWorkerNodeId
1671 ).toBe(0)
1672 expect(
1673 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1674 pool.workerChoiceStrategyContext.workerChoiceStrategy
1675 ).defaultWorkerWeight
1676 ).toBeGreaterThan(0)
1677 expect(
1678 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1679 workerChoiceStrategy
1680 ).workerVirtualTaskRunTime
1681 ).toBe(0)
1682 await pool.destroy()
1683 pool = new DynamicThreadPool(
1684 min,
1685 max,
1686 './tests/worker-files/thread/testWorker.js'
1687 )
1688 expect(
1689 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1690 workerChoiceStrategy
1691 ).nextWorkerNodeId
1692 ).toBeDefined()
1693 expect(
1694 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1695 workerChoiceStrategy
1696 ).defaultWorkerWeight
1697 ).toBeDefined()
1698 expect(
1699 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1700 workerChoiceStrategy
1701 ).workerVirtualTaskRunTime
1702 ).toBeDefined()
1703 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1704 expect(
1705 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1706 pool.workerChoiceStrategyContext.workerChoiceStrategy
1707 ).nextWorkerNodeId
1708 ).toBe(0)
1709 expect(
1710 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1711 pool.workerChoiceStrategyContext.workerChoiceStrategy
1712 ).defaultWorkerWeight
1713 ).toBeGreaterThan(0)
1714 expect(
1715 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1716 workerChoiceStrategy
1717 ).workerVirtualTaskRunTime
1718 ).toBe(0)
1719 // We need to clean up the resources after our test
1720 await pool.destroy()
1721 })
1722
1723 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy default policy', async () => {
1724 const workerChoiceStrategy =
1725 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1726 let pool = new FixedThreadPool(
1727 max,
1728 './tests/worker-files/thread/testWorker.js',
1729 { workerChoiceStrategy }
1730 )
1731 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
1732 useDynamicWorker: true
1733 })
1734 await pool.destroy()
1735 pool = new DynamicThreadPool(
1736 min,
1737 max,
1738 './tests/worker-files/thread/testWorker.js',
1739 { workerChoiceStrategy }
1740 )
1741 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
1742 useDynamicWorker: true
1743 })
1744 // We need to clean up the resources after our test
1745 await pool.destroy()
1746 })
1747
1748 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy default tasks statistics requirements', async () => {
1749 const workerChoiceStrategy =
1750 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1751 let pool = new FixedThreadPool(
1752 max,
1753 './tests/worker-files/thread/testWorker.js',
1754 { workerChoiceStrategy }
1755 )
1756 expect(
1757 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1758 ).toStrictEqual({
1759 runTime: {
1760 aggregate: false,
1761 average: false,
1762 median: false
1763 },
1764 waitTime: {
1765 aggregate: false,
1766 average: false,
1767 median: false
1768 },
1769 elu: {
1770 aggregate: false,
1771 average: false,
1772 median: false
1773 }
1774 })
1775 await pool.destroy()
1776 pool = new DynamicThreadPool(
1777 min,
1778 max,
1779 './tests/worker-files/thread/testWorker.js',
1780 { workerChoiceStrategy }
1781 )
1782 expect(
1783 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1784 ).toStrictEqual({
1785 runTime: {
1786 aggregate: false,
1787 average: false,
1788 median: false
1789 },
1790 waitTime: {
1791 aggregate: false,
1792 average: false,
1793 median: false
1794 },
1795 elu: {
1796 aggregate: false,
1797 average: false,
1798 median: false
1799 }
1800 })
1801 // We need to clean up the resources after our test
1802 await pool.destroy()
1803 })
1804
1805 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
1806 const pool = new FixedThreadPool(
1807 max,
1808 './tests/worker-files/thread/testWorker.js',
1809 {
1810 workerChoiceStrategy:
1811 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1812 }
1813 )
1814 // TODO: Create a better test to cover `InterleavedWeightedRoundRobinWorkerChoiceStrategy#choose`
1815 const promises = new Set()
1816 const maxMultiplier = 2
1817 for (let i = 0; i < max * maxMultiplier; i++) {
1818 promises.add(pool.execute())
1819 }
1820 await Promise.all(promises)
1821 for (const workerNode of pool.workerNodes) {
1822 expect(workerNode.workerUsage).toStrictEqual({
1823 tasks: {
1824 executed: maxMultiplier,
1825 executing: 0,
1826 queued: 0,
1827 failed: 0
1828 },
1829 runTime: {
1830 aggregate: 0,
1831 average: 0,
1832 median: 0,
1833 history: expect.any(CircularArray)
1834 },
1835 waitTime: {
1836 aggregate: 0,
1837 average: 0,
1838 median: 0,
1839 history: expect.any(CircularArray)
1840 },
1841 elu: {
1842 idle: {
1843 aggregate: 0,
1844 average: 0,
1845 median: 0,
1846 history: expect.any(CircularArray)
1847 },
1848 active: {
1849 aggregate: 0,
1850 average: 0,
1851 median: 0,
1852 history: expect.any(CircularArray)
1853 },
1854 utilization: 0
1855 }
1856 })
1857 }
1858 expect(
1859 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1860 pool.workerChoiceStrategyContext.workerChoiceStrategy
1861 ).defaultWorkerWeight
1862 ).toBeGreaterThan(0)
1863 expect(
1864 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1865 pool.workerChoiceStrategyContext.workerChoiceStrategy
1866 ).roundId
1867 ).toBe(0)
1868 expect(
1869 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1870 pool.workerChoiceStrategyContext.workerChoiceStrategy
1871 ).nextWorkerNodeId
1872 ).toBe(0)
1873 expect(
1874 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1875 pool.workerChoiceStrategyContext.workerChoiceStrategy
1876 ).roundWeights
1877 ).toStrictEqual([
1878 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1879 pool.workerChoiceStrategyContext.workerChoiceStrategy
1880 ).defaultWorkerWeight
1881 ])
1882 // We need to clean up the resources after our test
1883 await pool.destroy()
1884 })
1885
1886 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
1887 const pool = new DynamicThreadPool(
1888 min,
1889 max,
1890 './tests/worker-files/thread/testWorker.js',
1891 {
1892 workerChoiceStrategy:
1893 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1894 }
1895 )
1896 // TODO: Create a better test to cover `InterleavedWeightedRoundRobinWorkerChoiceStrategy#choose`
1897 const promises = new Set()
1898 const maxMultiplier = 2
1899 for (let i = 0; i < max * maxMultiplier; i++) {
1900 promises.add(pool.execute())
1901 }
1902 await Promise.all(promises)
1903 for (const workerNode of pool.workerNodes) {
1904 expect(workerNode.workerUsage).toStrictEqual({
1905 tasks: {
1906 executed: maxMultiplier,
1907 executing: 0,
1908 queued: 0,
1909 failed: 0
1910 },
1911 runTime: {
1912 aggregate: 0,
1913 average: 0,
1914 median: 0,
1915 history: expect.any(CircularArray)
1916 },
1917 waitTime: {
1918 aggregate: 0,
1919 average: 0,
1920 median: 0,
1921 history: expect.any(CircularArray)
1922 },
1923 elu: {
1924 idle: {
1925 aggregate: 0,
1926 average: 0,
1927 median: 0,
1928 history: expect.any(CircularArray)
1929 },
1930 active: {
1931 aggregate: 0,
1932 average: 0,
1933 median: 0,
1934 history: expect.any(CircularArray)
1935 },
1936 utilization: 0
1937 }
1938 })
1939 }
1940 expect(
1941 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1942 pool.workerChoiceStrategyContext.workerChoiceStrategy
1943 ).defaultWorkerWeight
1944 ).toBeGreaterThan(0)
1945 expect(
1946 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1947 pool.workerChoiceStrategyContext.workerChoiceStrategy
1948 ).roundId
1949 ).toBe(0)
1950 expect(
1951 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1952 pool.workerChoiceStrategyContext.workerChoiceStrategy
1953 ).nextWorkerNodeId
1954 ).toBe(0)
1955 expect(
1956 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1957 pool.workerChoiceStrategyContext.workerChoiceStrategy
1958 ).roundWeights
1959 ).toStrictEqual([
1960 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1961 pool.workerChoiceStrategyContext.workerChoiceStrategy
1962 ).defaultWorkerWeight
1963 ])
1964 // We need to clean up the resources after our test
1965 await pool.destroy()
1966 })
1967
1968 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
1969 const workerChoiceStrategy =
1970 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1971 let pool = new FixedThreadPool(
1972 max,
1973 './tests/worker-files/thread/testWorker.js'
1974 )
1975 expect(
1976 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1977 workerChoiceStrategy
1978 ).roundId
1979 ).toBeDefined()
1980 expect(
1981 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1982 workerChoiceStrategy
1983 ).nextWorkerNodeId
1984 ).toBeDefined()
1985 expect(
1986 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1987 workerChoiceStrategy
1988 ).defaultWorkerWeight
1989 ).toBeDefined()
1990 expect(
1991 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1992 workerChoiceStrategy
1993 ).roundWeights
1994 ).toBeDefined()
1995 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1996 expect(
1997 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1998 workerChoiceStrategy
1999 ).roundId
2000 ).toBe(0)
2001 expect(
2002 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2003 pool.workerChoiceStrategyContext.workerChoiceStrategy
2004 ).nextWorkerNodeId
2005 ).toBe(0)
2006 expect(
2007 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2008 pool.workerChoiceStrategyContext.workerChoiceStrategy
2009 ).defaultWorkerWeight
2010 ).toBeGreaterThan(0)
2011 expect(
2012 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2013 workerChoiceStrategy
2014 ).roundWeights
2015 ).toStrictEqual([
2016 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2017 pool.workerChoiceStrategyContext.workerChoiceStrategy
2018 ).defaultWorkerWeight
2019 ])
2020 await pool.destroy()
2021 pool = new DynamicThreadPool(
2022 min,
2023 max,
2024 './tests/worker-files/thread/testWorker.js'
2025 )
2026 expect(
2027 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2028 workerChoiceStrategy
2029 ).roundId
2030 ).toBeDefined()
2031 expect(
2032 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2033 workerChoiceStrategy
2034 ).nextWorkerNodeId
2035 ).toBeDefined()
2036 expect(
2037 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2038 workerChoiceStrategy
2039 ).defaultWorkerWeight
2040 ).toBeDefined()
2041 expect(
2042 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2043 workerChoiceStrategy
2044 ).roundWeights
2045 ).toBeDefined()
2046 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
2047 expect(
2048 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2049 pool.workerChoiceStrategyContext.workerChoiceStrategy
2050 ).nextWorkerNodeId
2051 ).toBe(0)
2052 expect(
2053 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2054 pool.workerChoiceStrategyContext.workerChoiceStrategy
2055 ).defaultWorkerWeight
2056 ).toBeGreaterThan(0)
2057 expect(
2058 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2059 workerChoiceStrategy
2060 ).roundWeights
2061 ).toStrictEqual([
2062 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2063 pool.workerChoiceStrategyContext.workerChoiceStrategy
2064 ).defaultWorkerWeight
2065 ])
2066 // We need to clean up the resources after our test
2067 await pool.destroy()
2068 })
2069
2070 it('Verify unknown strategy throw error', () => {
2071 expect(
2072 () =>
2073 new DynamicThreadPool(
2074 min,
2075 max,
2076 './tests/worker-files/thread/testWorker.js',
2077 { workerChoiceStrategy: 'UNKNOWN_STRATEGY' }
2078 )
2079 ).toThrowError("Invalid worker choice strategy 'UNKNOWN_STRATEGY'")
2080 })
2081 })