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