feat: add statistics accounting to ELU fields
[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: false,
902 average: false,
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: false,
928 average: false,
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: 0,
978 average: 0,
979 median: 0,
980 history: expect.any(CircularArray)
981 },
982 utilization: 0
983 }
984 })
985 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0)
986 expect(workerNode.workerUsage.runTime.average).toBeGreaterThan(0)
987 }
988 expect(
989 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
990 pool.workerChoiceStrategyContext.workerChoiceStrategy
991 ).workersVirtualTaskEndTimestamp.length
992 ).toBe(pool.workerNodes.length)
993 // We need to clean up the resources after our test
994 await pool.destroy()
995 })
996
997 it('Verify FAIR_SHARE strategy can be run in a dynamic pool', async () => {
998 const pool = new DynamicThreadPool(
999 min,
1000 max,
1001 './tests/worker-files/thread/testWorker.js',
1002 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
1003 )
1004 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
1005 const promises = new Set()
1006 const maxMultiplier = 2
1007 for (let i = 0; i < max * maxMultiplier; i++) {
1008 promises.add(pool.execute())
1009 }
1010 await Promise.all(promises)
1011 for (const workerNode of pool.workerNodes) {
1012 expect(workerNode.workerUsage).toStrictEqual({
1013 tasks: {
1014 executed: maxMultiplier,
1015 executing: 0,
1016 queued: 0,
1017 failed: 0
1018 },
1019 runTime: {
1020 aggregate: expect.any(Number),
1021 average: expect.any(Number),
1022 median: 0,
1023 history: expect.any(CircularArray)
1024 },
1025 waitTime: {
1026 aggregate: 0,
1027 average: 0,
1028 median: 0,
1029 history: expect.any(CircularArray)
1030 },
1031 elu: {
1032 idle: {
1033 aggregate: 0,
1034 average: 0,
1035 median: 0,
1036 history: expect.any(CircularArray)
1037 },
1038 active: {
1039 aggregate: 0,
1040 average: 0,
1041 median: 0,
1042 history: expect.any(CircularArray)
1043 },
1044 utilization: 0
1045 }
1046 })
1047 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0)
1048 expect(workerNode.workerUsage.runTime.average).toBeGreaterThan(0)
1049 }
1050 expect(
1051 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1052 pool.workerChoiceStrategyContext.workerChoiceStrategy
1053 ).workersVirtualTaskEndTimestamp.length
1054 ).toBe(pool.workerNodes.length)
1055 // We need to clean up the resources after our test
1056 await pool.destroy()
1057 })
1058
1059 it('Verify FAIR_SHARE strategy can be run in a dynamic pool with median runtime statistic', async () => {
1060 const pool = new DynamicThreadPool(
1061 min,
1062 max,
1063 './tests/worker-files/thread/testWorker.js',
1064 {
1065 workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE,
1066 workerChoiceStrategyOptions: {
1067 runTime: { median: true }
1068 }
1069 }
1070 )
1071 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
1072 const promises = new Set()
1073 const maxMultiplier = 2
1074 for (let i = 0; i < max * maxMultiplier; i++) {
1075 promises.add(pool.execute())
1076 }
1077 await Promise.all(promises)
1078 for (const workerNode of pool.workerNodes) {
1079 expect(workerNode.workerUsage).toStrictEqual({
1080 tasks: {
1081 executed: maxMultiplier,
1082 executing: 0,
1083 queued: 0,
1084 failed: 0
1085 },
1086 runTime: {
1087 aggregate: expect.any(Number),
1088 average: 0,
1089 median: expect.any(Number),
1090 history: expect.any(CircularArray)
1091 },
1092 waitTime: {
1093 aggregate: 0,
1094 average: 0,
1095 median: 0,
1096 history: expect.any(CircularArray)
1097 },
1098 elu: {
1099 idle: {
1100 aggregate: 0,
1101 average: 0,
1102 median: 0,
1103 history: expect.any(CircularArray)
1104 },
1105 active: {
1106 aggregate: 0,
1107 average: 0,
1108 median: 0,
1109 history: expect.any(CircularArray)
1110 },
1111 utilization: 0
1112 }
1113 })
1114 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0)
1115 expect(workerNode.workerUsage.runTime.median).toBeGreaterThan(0)
1116 }
1117 expect(
1118 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1119 pool.workerChoiceStrategyContext.workerChoiceStrategy
1120 ).workersVirtualTaskEndTimestamp.length
1121 ).toBe(pool.workerNodes.length)
1122 // We need to clean up the resources after our test
1123 await pool.destroy()
1124 })
1125
1126 it('Verify FAIR_SHARE strategy internals are resets after setting it', async () => {
1127 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
1128 let pool = new FixedThreadPool(
1129 max,
1130 './tests/worker-files/thread/testWorker.js'
1131 )
1132 expect(
1133 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1134 workerChoiceStrategy
1135 ).workersVirtualTaskEndTimestamp
1136 ).toBeInstanceOf(Array)
1137 expect(
1138 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1139 workerChoiceStrategy
1140 ).workersVirtualTaskEndTimestamp.length
1141 ).toBe(0)
1142 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1143 workerChoiceStrategy
1144 ).workersVirtualTaskEndTimestamp[0] = performance.now()
1145 expect(
1146 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1147 workerChoiceStrategy
1148 ).workersVirtualTaskEndTimestamp.length
1149 ).toBe(1)
1150 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1151 expect(
1152 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1153 workerChoiceStrategy
1154 ).workersVirtualTaskEndTimestamp
1155 ).toBeInstanceOf(Array)
1156 expect(
1157 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1158 workerChoiceStrategy
1159 ).workersVirtualTaskEndTimestamp.length
1160 ).toBe(0)
1161 await pool.destroy()
1162 pool = new DynamicThreadPool(
1163 min,
1164 max,
1165 './tests/worker-files/thread/testWorker.js'
1166 )
1167 expect(
1168 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1169 workerChoiceStrategy
1170 ).workersVirtualTaskEndTimestamp
1171 ).toBeInstanceOf(Array)
1172 expect(
1173 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1174 workerChoiceStrategy
1175 ).workersVirtualTaskEndTimestamp.length
1176 ).toBe(0)
1177 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1178 workerChoiceStrategy
1179 ).workersVirtualTaskEndTimestamp[0] = performance.now()
1180 expect(
1181 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1182 workerChoiceStrategy
1183 ).workersVirtualTaskEndTimestamp.length
1184 ).toBe(1)
1185 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1186 expect(
1187 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1188 workerChoiceStrategy
1189 ).workersVirtualTaskEndTimestamp
1190 ).toBeInstanceOf(Array)
1191 expect(
1192 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1193 workerChoiceStrategy
1194 ).workersVirtualTaskEndTimestamp.length
1195 ).toBe(0)
1196 // We need to clean up the resources after our test
1197 await pool.destroy()
1198 })
1199
1200 it('Verify WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
1201 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
1202 let pool = new FixedThreadPool(
1203 max,
1204 './tests/worker-files/thread/testWorker.js',
1205 { workerChoiceStrategy }
1206 )
1207 expect(
1208 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1209 ).toStrictEqual({
1210 runTime: {
1211 aggregate: true,
1212 average: true,
1213 median: false
1214 },
1215 waitTime: {
1216 aggregate: false,
1217 average: false,
1218 median: false
1219 },
1220 elu: {
1221 aggregate: false,
1222 average: false,
1223 median: false
1224 }
1225 })
1226 await pool.destroy()
1227 pool = new DynamicThreadPool(
1228 min,
1229 max,
1230 './tests/worker-files/thread/testWorker.js',
1231 { workerChoiceStrategy }
1232 )
1233 expect(
1234 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1235 ).toStrictEqual({
1236 runTime: {
1237 aggregate: true,
1238 average: true,
1239 median: false
1240 },
1241 waitTime: {
1242 aggregate: false,
1243 average: false,
1244 median: false
1245 },
1246 elu: {
1247 aggregate: false,
1248 average: false,
1249 median: false
1250 }
1251 })
1252 // We need to clean up the resources after our test
1253 await pool.destroy()
1254 })
1255
1256 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
1257 const pool = new FixedThreadPool(
1258 max,
1259 './tests/worker-files/thread/testWorker.js',
1260 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
1261 )
1262 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
1263 const promises = new Set()
1264 const maxMultiplier = 2
1265 for (let i = 0; i < max * maxMultiplier; i++) {
1266 promises.add(pool.execute())
1267 }
1268 await Promise.all(promises)
1269 for (const workerNode of pool.workerNodes) {
1270 expect(workerNode.workerUsage).toStrictEqual({
1271 tasks: {
1272 executed: expect.any(Number),
1273 executing: 0,
1274 queued: 0,
1275 failed: 0
1276 },
1277 runTime: {
1278 aggregate: expect.any(Number),
1279 average: expect.any(Number),
1280 median: 0,
1281 history: expect.any(CircularArray)
1282 },
1283 waitTime: {
1284 aggregate: 0,
1285 average: 0,
1286 median: 0,
1287 history: expect.any(CircularArray)
1288 },
1289 elu: {
1290 idle: {
1291 aggregate: 0,
1292 average: 0,
1293 median: 0,
1294 history: expect.any(CircularArray)
1295 },
1296 active: {
1297 aggregate: 0,
1298 average: 0,
1299 median: 0,
1300 history: expect.any(CircularArray)
1301 },
1302 utilization: 0
1303 }
1304 })
1305 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
1306 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
1307 max * maxMultiplier
1308 )
1309 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThanOrEqual(0)
1310 expect(workerNode.workerUsage.runTime.average).toBeGreaterThanOrEqual(0)
1311 }
1312 expect(
1313 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1314 pool.workerChoiceStrategyContext.workerChoiceStrategy
1315 ).defaultWorkerWeight
1316 ).toBeGreaterThan(0)
1317 expect(
1318 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1319 pool.workerChoiceStrategyContext.workerChoiceStrategy
1320 ).workerVirtualTaskRunTime
1321 ).toBeGreaterThanOrEqual(0)
1322 // We need to clean up the resources after our test
1323 await pool.destroy()
1324 })
1325
1326 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
1327 const pool = new DynamicThreadPool(
1328 min,
1329 max,
1330 './tests/worker-files/thread/testWorker.js',
1331 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
1332 )
1333 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
1334 const promises = new Set()
1335 const maxMultiplier = 2
1336 for (let i = 0; i < max * maxMultiplier; i++) {
1337 promises.add(pool.execute())
1338 }
1339 await Promise.all(promises)
1340 for (const workerNode of pool.workerNodes) {
1341 expect(workerNode.workerUsage).toStrictEqual({
1342 tasks: {
1343 executed: expect.any(Number),
1344 executing: 0,
1345 queued: 0,
1346 failed: 0
1347 },
1348 runTime: {
1349 aggregate: expect.any(Number),
1350 average: expect.any(Number),
1351 median: 0,
1352 history: expect.any(CircularArray)
1353 },
1354 waitTime: {
1355 aggregate: 0,
1356 average: 0,
1357 median: 0,
1358 history: expect.any(CircularArray)
1359 },
1360 elu: {
1361 idle: {
1362 aggregate: 0,
1363 average: 0,
1364 median: 0,
1365 history: expect.any(CircularArray)
1366 },
1367 active: {
1368 aggregate: 0,
1369 average: 0,
1370 median: 0,
1371 history: expect.any(CircularArray)
1372 },
1373 utilization: 0
1374 }
1375 })
1376 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThan(0)
1377 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
1378 max * maxMultiplier
1379 )
1380 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0)
1381 expect(workerNode.workerUsage.runTime.average).toBeGreaterThan(0)
1382 }
1383 expect(
1384 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1385 pool.workerChoiceStrategyContext.workerChoiceStrategy
1386 ).defaultWorkerWeight
1387 ).toBeGreaterThan(0)
1388 expect(
1389 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1390 pool.workerChoiceStrategyContext.workerChoiceStrategy
1391 ).workerVirtualTaskRunTime
1392 ).toBeGreaterThanOrEqual(0)
1393 // We need to clean up the resources after our test
1394 await pool.destroy()
1395 })
1396
1397 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool with median runtime statistic', async () => {
1398 const pool = new DynamicThreadPool(
1399 min,
1400 max,
1401 './tests/worker-files/thread/testWorker.js',
1402 {
1403 workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN,
1404 workerChoiceStrategyOptions: {
1405 runTime: { median: true }
1406 }
1407 }
1408 )
1409 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
1410 const promises = new Set()
1411 const maxMultiplier = 2
1412 for (let i = 0; i < max * maxMultiplier; i++) {
1413 promises.add(pool.execute())
1414 }
1415 await Promise.all(promises)
1416 for (const workerNode of pool.workerNodes) {
1417 expect(workerNode.workerUsage).toStrictEqual({
1418 tasks: {
1419 executed: expect.any(Number),
1420 executing: 0,
1421 queued: 0,
1422 failed: 0
1423 },
1424 runTime: {
1425 aggregate: expect.any(Number),
1426 average: 0,
1427 median: expect.any(Number),
1428 history: expect.any(CircularArray)
1429 },
1430 waitTime: {
1431 aggregate: 0,
1432 average: 0,
1433 median: 0,
1434 history: expect.any(CircularArray)
1435 },
1436 elu: {
1437 idle: {
1438 aggregate: 0,
1439 average: 0,
1440 median: 0,
1441 history: expect.any(CircularArray)
1442 },
1443 active: {
1444 aggregate: 0,
1445 average: 0,
1446 median: 0,
1447 history: expect.any(CircularArray)
1448 },
1449 utilization: 0
1450 }
1451 })
1452 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThan(0)
1453 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
1454 max * maxMultiplier
1455 )
1456 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0)
1457 expect(workerNode.workerUsage.runTime.median).toBeGreaterThan(0)
1458 }
1459 expect(
1460 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1461 pool.workerChoiceStrategyContext.workerChoiceStrategy
1462 ).defaultWorkerWeight
1463 ).toBeGreaterThan(0)
1464 expect(
1465 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1466 pool.workerChoiceStrategyContext.workerChoiceStrategy
1467 ).workerVirtualTaskRunTime
1468 ).toBeGreaterThanOrEqual(0)
1469 // We need to clean up the resources after our test
1470 await pool.destroy()
1471 })
1472
1473 it('Verify WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
1474 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
1475 let pool = new FixedThreadPool(
1476 max,
1477 './tests/worker-files/thread/testWorker.js'
1478 )
1479 expect(
1480 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1481 workerChoiceStrategy
1482 ).currentWorkerNodeId
1483 ).toBeDefined()
1484 expect(
1485 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1486 workerChoiceStrategy
1487 ).defaultWorkerWeight
1488 ).toBeDefined()
1489 expect(
1490 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1491 workerChoiceStrategy
1492 ).workerVirtualTaskRunTime
1493 ).toBeDefined()
1494 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1495 expect(
1496 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1497 pool.workerChoiceStrategyContext.workerChoiceStrategy
1498 ).currentWorkerNodeId
1499 ).toBe(0)
1500 expect(
1501 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1502 pool.workerChoiceStrategyContext.workerChoiceStrategy
1503 ).defaultWorkerWeight
1504 ).toBeGreaterThan(0)
1505 expect(
1506 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1507 workerChoiceStrategy
1508 ).workerVirtualTaskRunTime
1509 ).toBe(0)
1510 await pool.destroy()
1511 pool = new DynamicThreadPool(
1512 min,
1513 max,
1514 './tests/worker-files/thread/testWorker.js'
1515 )
1516 expect(
1517 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1518 workerChoiceStrategy
1519 ).currentWorkerNodeId
1520 ).toBeDefined()
1521 expect(
1522 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1523 workerChoiceStrategy
1524 ).defaultWorkerWeight
1525 ).toBeDefined()
1526 expect(
1527 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1528 workerChoiceStrategy
1529 ).workerVirtualTaskRunTime
1530 ).toBeDefined()
1531 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1532 expect(
1533 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1534 pool.workerChoiceStrategyContext.workerChoiceStrategy
1535 ).currentWorkerNodeId
1536 ).toBe(0)
1537 expect(
1538 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1539 pool.workerChoiceStrategyContext.workerChoiceStrategy
1540 ).defaultWorkerWeight
1541 ).toBeGreaterThan(0)
1542 expect(
1543 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1544 workerChoiceStrategy
1545 ).workerVirtualTaskRunTime
1546 ).toBe(0)
1547 // We need to clean up the resources after our test
1548 await pool.destroy()
1549 })
1550
1551 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
1552 const workerChoiceStrategy =
1553 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1554 let pool = new FixedThreadPool(
1555 max,
1556 './tests/worker-files/thread/testWorker.js',
1557 { workerChoiceStrategy }
1558 )
1559 expect(
1560 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1561 ).toStrictEqual({
1562 runTime: {
1563 aggregate: false,
1564 average: false,
1565 median: false
1566 },
1567 waitTime: {
1568 aggregate: false,
1569 average: false,
1570 median: false
1571 },
1572 elu: {
1573 aggregate: false,
1574 average: false,
1575 median: false
1576 }
1577 })
1578 await pool.destroy()
1579 pool = new DynamicThreadPool(
1580 min,
1581 max,
1582 './tests/worker-files/thread/testWorker.js',
1583 { workerChoiceStrategy }
1584 )
1585 expect(
1586 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1587 ).toStrictEqual({
1588 runTime: {
1589 aggregate: false,
1590 average: false,
1591 median: false
1592 },
1593 waitTime: {
1594 aggregate: false,
1595 average: false,
1596 median: false
1597 },
1598 elu: {
1599 aggregate: false,
1600 average: false,
1601 median: false
1602 }
1603 })
1604 // We need to clean up the resources after our test
1605 await pool.destroy()
1606 })
1607
1608 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
1609 const pool = new FixedThreadPool(
1610 max,
1611 './tests/worker-files/thread/testWorker.js',
1612 {
1613 workerChoiceStrategy:
1614 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1615 }
1616 )
1617 // TODO: Create a better test to cover `InterleavedWeightedRoundRobinWorkerChoiceStrategy#choose`
1618 const promises = new Set()
1619 const maxMultiplier = 2
1620 for (let i = 0; i < max * maxMultiplier; i++) {
1621 promises.add(pool.execute())
1622 }
1623 await Promise.all(promises)
1624 for (const workerNode of pool.workerNodes) {
1625 expect(workerNode.workerUsage).toStrictEqual({
1626 tasks: {
1627 executed: maxMultiplier,
1628 executing: 0,
1629 queued: 0,
1630 failed: 0
1631 },
1632 runTime: {
1633 aggregate: 0,
1634 average: 0,
1635 median: 0,
1636 history: expect.any(CircularArray)
1637 },
1638 waitTime: {
1639 aggregate: 0,
1640 average: 0,
1641 median: 0,
1642 history: expect.any(CircularArray)
1643 },
1644 elu: {
1645 idle: {
1646 aggregate: 0,
1647 average: 0,
1648 median: 0,
1649 history: expect.any(CircularArray)
1650 },
1651 active: {
1652 aggregate: 0,
1653 average: 0,
1654 median: 0,
1655 history: expect.any(CircularArray)
1656 },
1657 utilization: 0
1658 }
1659 })
1660 }
1661 expect(
1662 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1663 pool.workerChoiceStrategyContext.workerChoiceStrategy
1664 ).defaultWorkerWeight
1665 ).toBeGreaterThan(0)
1666 expect(
1667 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1668 pool.workerChoiceStrategyContext.workerChoiceStrategy
1669 ).currentRoundId
1670 ).toBe(0)
1671 expect(
1672 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1673 pool.workerChoiceStrategyContext.workerChoiceStrategy
1674 ).currentWorkerNodeId
1675 ).toBe(0)
1676 expect(
1677 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1678 pool.workerChoiceStrategyContext.workerChoiceStrategy
1679 ).roundWeights
1680 ).toStrictEqual([
1681 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1682 pool.workerChoiceStrategyContext.workerChoiceStrategy
1683 ).defaultWorkerWeight
1684 ])
1685 // We need to clean up the resources after our test
1686 await pool.destroy()
1687 })
1688
1689 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
1690 const pool = new DynamicThreadPool(
1691 min,
1692 max,
1693 './tests/worker-files/thread/testWorker.js',
1694 {
1695 workerChoiceStrategy:
1696 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1697 }
1698 )
1699 // TODO: Create a better test to cover `InterleavedWeightedRoundRobinWorkerChoiceStrategy#choose`
1700 const promises = new Set()
1701 const maxMultiplier = 2
1702 for (let i = 0; i < max * maxMultiplier; i++) {
1703 promises.add(pool.execute())
1704 }
1705 await Promise.all(promises)
1706 for (const workerNode of pool.workerNodes) {
1707 expect(workerNode.workerUsage).toStrictEqual({
1708 tasks: {
1709 executed: maxMultiplier,
1710 executing: 0,
1711 queued: 0,
1712 failed: 0
1713 },
1714 runTime: {
1715 aggregate: 0,
1716 average: 0,
1717 median: 0,
1718 history: expect.any(CircularArray)
1719 },
1720 waitTime: {
1721 aggregate: 0,
1722 average: 0,
1723 median: 0,
1724 history: expect.any(CircularArray)
1725 },
1726 elu: {
1727 idle: {
1728 aggregate: 0,
1729 average: 0,
1730 median: 0,
1731 history: expect.any(CircularArray)
1732 },
1733 active: {
1734 aggregate: 0,
1735 average: 0,
1736 median: 0,
1737 history: expect.any(CircularArray)
1738 },
1739 utilization: 0
1740 }
1741 })
1742 }
1743 expect(
1744 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1745 pool.workerChoiceStrategyContext.workerChoiceStrategy
1746 ).defaultWorkerWeight
1747 ).toBeGreaterThan(0)
1748 expect(
1749 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1750 pool.workerChoiceStrategyContext.workerChoiceStrategy
1751 ).currentRoundId
1752 ).toBe(0)
1753 expect(
1754 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1755 pool.workerChoiceStrategyContext.workerChoiceStrategy
1756 ).currentWorkerNodeId
1757 ).toBe(0)
1758 expect(
1759 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1760 pool.workerChoiceStrategyContext.workerChoiceStrategy
1761 ).roundWeights
1762 ).toStrictEqual([
1763 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1764 pool.workerChoiceStrategyContext.workerChoiceStrategy
1765 ).defaultWorkerWeight
1766 ])
1767 // We need to clean up the resources after our test
1768 await pool.destroy()
1769 })
1770
1771 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
1772 const workerChoiceStrategy =
1773 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1774 let pool = new FixedThreadPool(
1775 max,
1776 './tests/worker-files/thread/testWorker.js'
1777 )
1778 expect(
1779 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1780 workerChoiceStrategy
1781 ).currentRoundId
1782 ).toBeDefined()
1783 expect(
1784 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1785 workerChoiceStrategy
1786 ).currentWorkerNodeId
1787 ).toBeDefined()
1788 expect(
1789 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1790 workerChoiceStrategy
1791 ).defaultWorkerWeight
1792 ).toBeDefined()
1793 expect(
1794 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1795 workerChoiceStrategy
1796 ).roundWeights
1797 ).toBeDefined()
1798 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1799 expect(
1800 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1801 workerChoiceStrategy
1802 ).currentRoundId
1803 ).toBe(0)
1804 expect(
1805 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1806 pool.workerChoiceStrategyContext.workerChoiceStrategy
1807 ).currentWorkerNodeId
1808 ).toBe(0)
1809 expect(
1810 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1811 pool.workerChoiceStrategyContext.workerChoiceStrategy
1812 ).defaultWorkerWeight
1813 ).toBeGreaterThan(0)
1814 expect(
1815 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1816 workerChoiceStrategy
1817 ).roundWeights
1818 ).toStrictEqual([
1819 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1820 pool.workerChoiceStrategyContext.workerChoiceStrategy
1821 ).defaultWorkerWeight
1822 ])
1823 await pool.destroy()
1824 pool = new DynamicThreadPool(
1825 min,
1826 max,
1827 './tests/worker-files/thread/testWorker.js'
1828 )
1829 expect(
1830 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1831 workerChoiceStrategy
1832 ).currentRoundId
1833 ).toBeDefined()
1834 expect(
1835 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1836 workerChoiceStrategy
1837 ).currentWorkerNodeId
1838 ).toBeDefined()
1839 expect(
1840 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1841 workerChoiceStrategy
1842 ).defaultWorkerWeight
1843 ).toBeDefined()
1844 expect(
1845 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1846 workerChoiceStrategy
1847 ).roundWeights
1848 ).toBeDefined()
1849 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1850 expect(
1851 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1852 pool.workerChoiceStrategyContext.workerChoiceStrategy
1853 ).currentWorkerNodeId
1854 ).toBe(0)
1855 expect(
1856 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1857 pool.workerChoiceStrategyContext.workerChoiceStrategy
1858 ).defaultWorkerWeight
1859 ).toBeGreaterThan(0)
1860 expect(
1861 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1862 workerChoiceStrategy
1863 ).roundWeights
1864 ).toStrictEqual([
1865 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1866 pool.workerChoiceStrategyContext.workerChoiceStrategy
1867 ).defaultWorkerWeight
1868 ])
1869 // We need to clean up the resources after our test
1870 await pool.destroy()
1871 })
1872
1873 it('Verify unknown strategy throw error', () => {
1874 expect(
1875 () =>
1876 new DynamicThreadPool(
1877 min,
1878 max,
1879 './tests/worker-files/thread/testWorker.js',
1880 { workerChoiceStrategy: 'UNKNOWN_STRATEGY' }
1881 )
1882 ).toThrowError("Invalid worker choice strategy 'UNKNOWN_STRATEGY'")
1883 })
1884 })