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