Merge pull request #773 from poolifier/elu-strategy
[poolifier.git] / tests / pools / selection-strategies / selection-strategies.test.js
1 const { expect } = require('expect')
2 const {
3 WorkerChoiceStrategies,
4 DynamicThreadPool,
5 FixedThreadPool,
6 FixedClusterPool
7 } = require('../../../lib')
8 const { CircularArray } = require('../../../lib/circular-array')
9
10 describe('Selection strategies test suite', () => {
11 const min = 0
12 const max = 3
13
14 it('Verify that WorkerChoiceStrategies enumeration provides string values', () => {
15 expect(WorkerChoiceStrategies.ROUND_ROBIN).toBe('ROUND_ROBIN')
16 expect(WorkerChoiceStrategies.LEAST_USED).toBe('LEAST_USED')
17 expect(WorkerChoiceStrategies.LEAST_BUSY).toBe('LEAST_BUSY')
18 expect(WorkerChoiceStrategies.LEAST_ELU).toBe('LEAST_ELU')
19 expect(WorkerChoiceStrategies.FAIR_SHARE).toBe('FAIR_SHARE')
20 expect(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN).toBe(
21 'WEIGHTED_ROUND_ROBIN'
22 )
23 expect(WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN).toBe(
24 'INTERLEAVED_WEIGHTED_ROUND_ROBIN'
25 )
26 })
27
28 it('Verify ROUND_ROBIN strategy is the default at pool creation', async () => {
29 const pool = new DynamicThreadPool(
30 min,
31 max,
32 './tests/worker-files/thread/testWorker.js'
33 )
34 expect(pool.opts.workerChoiceStrategy).toBe(
35 WorkerChoiceStrategies.ROUND_ROBIN
36 )
37 // We need to clean up the resources after our test
38 await pool.destroy()
39 })
40
41 it('Verify available strategies are taken at pool creation', async () => {
42 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
43 const pool = new FixedThreadPool(
44 max,
45 './tests/worker-files/thread/testWorker.js',
46 { workerChoiceStrategy }
47 )
48 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
49 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
50 workerChoiceStrategy
51 )
52 await pool.destroy()
53 }
54 })
55
56 it('Verify available strategies can be set after pool creation', async () => {
57 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
58 const pool = new DynamicThreadPool(
59 min,
60 max,
61 './tests/worker-files/thread/testWorker.js'
62 )
63 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
64 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
65 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
66 workerChoiceStrategy
67 )
68 await pool.destroy()
69 }
70 })
71
72 it('Verify available strategies default internals at pool creation', async () => {
73 const pool = new FixedThreadPool(
74 max,
75 './tests/worker-files/thread/testWorker.js'
76 )
77 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
78 if (workerChoiceStrategy === WorkerChoiceStrategies.ROUND_ROBIN) {
79 expect(
80 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
81 workerChoiceStrategy
82 ).nextWorkerNodeId
83 ).toBe(0)
84 } else if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) {
85 expect(
86 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
87 workerChoiceStrategy
88 ).workersVirtualTaskEndTimestamp
89 ).toBeInstanceOf(Array)
90 expect(
91 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
92 workerChoiceStrategy
93 ).workersVirtualTaskEndTimestamp.length
94 ).toBe(0)
95 } else if (
96 workerChoiceStrategy === WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
97 ) {
98 expect(
99 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
100 workerChoiceStrategy
101 ).currentWorkerNodeId
102 ).toBe(0)
103 expect(
104 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
105 workerChoiceStrategy
106 ).defaultWorkerWeight
107 ).toBeGreaterThan(0)
108 expect(
109 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
110 workerChoiceStrategy
111 ).workerVirtualTaskRunTime
112 ).toBe(0)
113 }
114 }
115 await pool.destroy()
116 })
117
118 it('Verify ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
119 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
120 let pool = new FixedThreadPool(
121 max,
122 './tests/worker-files/thread/testWorker.js',
123 { workerChoiceStrategy }
124 )
125 expect(
126 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
127 ).toStrictEqual({
128 runTime: {
129 aggregate: false,
130 average: false,
131 median: false
132 },
133 waitTime: {
134 aggregate: false,
135 average: false,
136 median: false
137 },
138 elu: false
139 })
140 await pool.destroy()
141 pool = new DynamicThreadPool(
142 min,
143 max,
144 './tests/worker-files/thread/testWorker.js',
145 { workerChoiceStrategy }
146 )
147 expect(
148 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
149 ).toStrictEqual({
150 runTime: {
151 aggregate: false,
152 average: false,
153 median: false
154 },
155 waitTime: {
156 aggregate: false,
157 average: false,
158 median: false
159 },
160 elu: false
161 })
162 // We need to clean up the resources after our test
163 await pool.destroy()
164 })
165
166 it('Verify ROUND_ROBIN strategy can be run in a fixed pool', async () => {
167 const pool = new FixedThreadPool(
168 max,
169 './tests/worker-files/thread/testWorker.js',
170 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
171 )
172 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
173 const promises = new Set()
174 const maxMultiplier = 2
175 for (let i = 0; i < max * maxMultiplier; i++) {
176 promises.add(pool.execute())
177 }
178 await Promise.all(promises)
179 for (const workerNode of pool.workerNodes) {
180 expect(workerNode.workerUsage).toStrictEqual({
181 tasks: {
182 executed: maxMultiplier,
183 executing: 0,
184 queued: 0,
185 failed: 0
186 },
187 runTime: {
188 aggregate: 0,
189 average: 0,
190 median: 0,
191 history: expect.any(CircularArray)
192 },
193 waitTime: {
194 aggregate: 0,
195 average: 0,
196 median: 0,
197 history: expect.any(CircularArray)
198 },
199 elu: undefined
200 })
201 }
202 expect(
203 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
204 WorkerChoiceStrategies.ROUND_ROBIN
205 ).nextWorkerNodeId
206 ).toBe(0)
207 // We need to clean up the resources after our test
208 await pool.destroy()
209 })
210
211 it('Verify ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
212 const pool = new DynamicThreadPool(
213 min,
214 max,
215 './tests/worker-files/thread/testWorker.js',
216 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
217 )
218 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
219 const promises = new Set()
220 const maxMultiplier = 2
221 for (let i = 0; i < max * maxMultiplier; i++) {
222 promises.add(pool.execute())
223 }
224 await Promise.all(promises)
225 for (const workerNode of pool.workerNodes) {
226 expect(workerNode.workerUsage).toStrictEqual({
227 tasks: {
228 executed: maxMultiplier,
229 executing: 0,
230 queued: 0,
231 failed: 0
232 },
233 runTime: {
234 aggregate: 0,
235 average: 0,
236 median: 0,
237 history: expect.any(CircularArray)
238 },
239 waitTime: {
240 aggregate: 0,
241 average: 0,
242 median: 0,
243 history: expect.any(CircularArray)
244 },
245 elu: undefined
246 })
247 }
248 expect(
249 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
250 WorkerChoiceStrategies.ROUND_ROBIN
251 ).nextWorkerNodeId
252 ).toBe(0)
253 // We need to clean up the resources after our test
254 await pool.destroy()
255 })
256
257 it('Verify ROUND_ROBIN strategy runtime behavior', async () => {
258 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
259 let pool = new FixedClusterPool(
260 max,
261 './tests/worker-files/cluster/testWorker.js',
262 { workerChoiceStrategy }
263 )
264 let results = new Set()
265 for (let i = 0; i < max; i++) {
266 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.id)
267 }
268 expect(results.size).toBe(max)
269 await pool.destroy()
270 pool = new FixedThreadPool(
271 max,
272 './tests/worker-files/thread/testWorker.js',
273 { workerChoiceStrategy }
274 )
275 results = new Set()
276 for (let i = 0; i < max; i++) {
277 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.threadId)
278 }
279 expect(results.size).toBe(max)
280 await pool.destroy()
281 })
282
283 it('Verify ROUND_ROBIN strategy internals are resets after setting it', async () => {
284 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
285 let pool = new FixedThreadPool(
286 max,
287 './tests/worker-files/thread/testWorker.js',
288 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
289 )
290 expect(
291 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
292 workerChoiceStrategy
293 ).nextWorkerNodeId
294 ).toBeDefined()
295 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
296 expect(
297 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
298 pool.workerChoiceStrategyContext.workerChoiceStrategy
299 ).nextWorkerNodeId
300 ).toBe(0)
301 await pool.destroy()
302 pool = new DynamicThreadPool(
303 min,
304 max,
305 './tests/worker-files/thread/testWorker.js',
306 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
307 )
308 expect(
309 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
310 workerChoiceStrategy
311 ).nextWorkerNodeId
312 ).toBeDefined()
313 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
314 expect(
315 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
316 pool.workerChoiceStrategyContext.workerChoiceStrategy
317 ).nextWorkerNodeId
318 ).toBe(0)
319 // We need to clean up the resources after our test
320 await pool.destroy()
321 })
322
323 it('Verify LEAST_USED strategy default tasks usage statistics requirements', async () => {
324 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
325 let pool = new FixedThreadPool(
326 max,
327 './tests/worker-files/thread/testWorker.js',
328 { workerChoiceStrategy }
329 )
330 expect(
331 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
332 ).toStrictEqual({
333 runTime: {
334 aggregate: false,
335 average: false,
336 median: false
337 },
338 waitTime: {
339 aggregate: false,
340 average: false,
341 median: false
342 },
343 elu: false
344 })
345 await pool.destroy()
346 pool = new DynamicThreadPool(
347 min,
348 max,
349 './tests/worker-files/thread/testWorker.js',
350 { workerChoiceStrategy }
351 )
352 expect(
353 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
354 ).toStrictEqual({
355 runTime: {
356 aggregate: false,
357 average: false,
358 median: false
359 },
360 waitTime: {
361 aggregate: false,
362 average: false,
363 median: false
364 },
365 elu: false
366 })
367 // We need to clean up the resources after our test
368 await pool.destroy()
369 })
370
371 it('Verify LEAST_USED strategy can be run in a fixed pool', async () => {
372 const pool = new FixedThreadPool(
373 max,
374 './tests/worker-files/thread/testWorker.js',
375 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
376 )
377 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
378 const promises = new Set()
379 const maxMultiplier = 2
380 for (let i = 0; i < max * maxMultiplier; i++) {
381 promises.add(pool.execute())
382 }
383 await Promise.all(promises)
384 for (const workerNode of pool.workerNodes) {
385 expect(workerNode.workerUsage).toStrictEqual({
386 tasks: {
387 executed: maxMultiplier,
388 executing: 0,
389 queued: 0,
390 failed: 0
391 },
392 runTime: {
393 aggregate: 0,
394 average: 0,
395 median: 0,
396 history: expect.any(CircularArray)
397 },
398 waitTime: {
399 aggregate: 0,
400 average: 0,
401 median: 0,
402 history: expect.any(CircularArray)
403 },
404 elu: undefined
405 })
406 }
407 // We need to clean up the resources after our test
408 await pool.destroy()
409 })
410
411 it('Verify LEAST_USED strategy can be run in a dynamic pool', async () => {
412 const pool = new DynamicThreadPool(
413 min,
414 max,
415 './tests/worker-files/thread/testWorker.js',
416 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
417 )
418 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
419 const promises = new Set()
420 const maxMultiplier = 2
421 for (let i = 0; i < max * maxMultiplier; i++) {
422 promises.add(pool.execute())
423 }
424 await Promise.all(promises)
425 for (const workerNode of pool.workerNodes) {
426 expect(workerNode.workerUsage).toStrictEqual({
427 tasks: {
428 executed: maxMultiplier,
429 executing: 0,
430 queued: 0,
431 failed: 0
432 },
433 runTime: {
434 aggregate: 0,
435 average: 0,
436 median: 0,
437 history: expect.any(CircularArray)
438 },
439 waitTime: {
440 aggregate: 0,
441 average: 0,
442 median: 0,
443 history: expect.any(CircularArray)
444 },
445
446 elu: undefined
447 })
448 }
449 // We need to clean up the resources after our test
450 await pool.destroy()
451 })
452
453 it('Verify LEAST_BUSY strategy default tasks usage statistics requirements', async () => {
454 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
455 let pool = new FixedThreadPool(
456 max,
457 './tests/worker-files/thread/testWorker.js',
458 { workerChoiceStrategy }
459 )
460 expect(
461 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
462 ).toStrictEqual({
463 runTime: {
464 aggregate: true,
465 average: false,
466 median: false
467 },
468 waitTime: {
469 aggregate: true,
470 average: false,
471 median: false
472 },
473 elu: false
474 })
475 await pool.destroy()
476 pool = new DynamicThreadPool(
477 min,
478 max,
479 './tests/worker-files/thread/testWorker.js',
480 { workerChoiceStrategy }
481 )
482 expect(
483 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
484 ).toStrictEqual({
485 runTime: {
486 aggregate: true,
487 average: false,
488 median: false
489 },
490 waitTime: {
491 aggregate: true,
492 average: false,
493 median: false
494 },
495 elu: false
496 })
497 // We need to clean up the resources after our test
498 await pool.destroy()
499 })
500
501 it('Verify LEAST_BUSY strategy can be run in a fixed pool', async () => {
502 const pool = new FixedThreadPool(
503 max,
504 './tests/worker-files/thread/testWorker.js',
505 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
506 )
507 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
508 const promises = new Set()
509 const maxMultiplier = 2
510 for (let i = 0; i < max * maxMultiplier; i++) {
511 promises.add(pool.execute())
512 }
513 await Promise.all(promises)
514 for (const workerNode of pool.workerNodes) {
515 expect(workerNode.workerUsage).toStrictEqual({
516 tasks: {
517 executed: expect.any(Number),
518 executing: 0,
519 queued: 0,
520 failed: 0
521 },
522 runTime: {
523 aggregate: expect.any(Number),
524 average: 0,
525 median: 0,
526 history: expect.any(CircularArray)
527 },
528 waitTime: {
529 aggregate: expect.any(Number),
530 average: 0,
531 median: 0,
532 history: expect.any(CircularArray)
533 },
534 elu: undefined
535 })
536 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
537 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
538 max * maxMultiplier
539 )
540 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThanOrEqual(0)
541 expect(workerNode.workerUsage.waitTime.aggregate).toBeGreaterThanOrEqual(
542 0
543 )
544 }
545 // We need to clean up the resources after our test
546 await pool.destroy()
547 })
548
549 it('Verify LEAST_BUSY strategy can be run in a dynamic pool', async () => {
550 const pool = new DynamicThreadPool(
551 min,
552 max,
553 './tests/worker-files/thread/testWorker.js',
554 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
555 )
556 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
557 const promises = new Set()
558 const maxMultiplier = 2
559 for (let i = 0; i < max * maxMultiplier; i++) {
560 promises.add(pool.execute())
561 }
562 await Promise.all(promises)
563 for (const workerNode of pool.workerNodes) {
564 expect(workerNode.workerUsage).toStrictEqual({
565 tasks: {
566 executed: expect.any(Number),
567 executing: 0,
568 queued: 0,
569 failed: 0
570 },
571 runTime: {
572 aggregate: expect.any(Number),
573 average: 0,
574 median: 0,
575 history: expect.any(CircularArray)
576 },
577 waitTime: {
578 aggregate: expect.any(Number),
579 average: 0,
580 median: 0,
581 history: expect.any(CircularArray)
582 },
583 elu: undefined
584 })
585 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThan(0)
586 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
587 max * maxMultiplier
588 )
589 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0)
590 expect(workerNode.workerUsage.waitTime.aggregate).toBeGreaterThan(0)
591 }
592 // We need to clean up the resources after our test
593 await pool.destroy()
594 })
595
596 it('Verify LEAST_ELU strategy default tasks usage statistics requirements', async () => {
597 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
598 let pool = new FixedThreadPool(
599 max,
600 './tests/worker-files/thread/testWorker.js',
601 { workerChoiceStrategy }
602 )
603 expect(
604 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
605 ).toStrictEqual({
606 runTime: {
607 aggregate: false,
608 average: false,
609 median: false
610 },
611 waitTime: {
612 aggregate: false,
613 average: false,
614 median: false
615 },
616 elu: true
617 })
618 await pool.destroy()
619 pool = new DynamicThreadPool(
620 min,
621 max,
622 './tests/worker-files/thread/testWorker.js',
623 { workerChoiceStrategy }
624 )
625 expect(
626 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
627 ).toStrictEqual({
628 runTime: {
629 aggregate: false,
630 average: false,
631 median: false
632 },
633 waitTime: {
634 aggregate: false,
635 average: false,
636 median: false
637 },
638 elu: true
639 })
640 // We need to clean up the resources after our test
641 await pool.destroy()
642 })
643
644 it('Verify LEAST_ELU strategy can be run in a fixed pool', async () => {
645 const pool = new FixedThreadPool(
646 max,
647 './tests/worker-files/thread/testWorker.js',
648 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_ELU }
649 )
650 // TODO: Create a better test to cover `LeastEluWorkerChoiceStrategy#choose`
651 const promises = new Set()
652 const maxMultiplier = 2
653 for (let i = 0; i < max * maxMultiplier; i++) {
654 promises.add(pool.execute())
655 }
656 await Promise.all(promises)
657 for (const workerNode of pool.workerNodes) {
658 const expectedWorkerUsage = {
659 tasks: {
660 executed: expect.any(Number),
661 executing: 0,
662 queued: 0,
663 failed: 0
664 },
665 runTime: {
666 aggregate: 0,
667 average: 0,
668 median: 0,
669 history: expect.any(CircularArray)
670 },
671 waitTime: {
672 aggregate: 0,
673 average: 0,
674 median: 0,
675 history: expect.any(CircularArray)
676 }
677 }
678 if (workerNode.workerUsage.elu === undefined) {
679 expect(workerNode.workerUsage).toStrictEqual({
680 ...expectedWorkerUsage,
681 elu: undefined
682 })
683 } else {
684 expect(workerNode.workerUsage).toStrictEqual({
685 ...expectedWorkerUsage,
686 elu: {
687 active: expect.any(Number),
688 idle: 0,
689 utilization: 1
690 }
691 })
692 }
693 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
694 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
695 max * maxMultiplier
696 )
697 }
698 // We need to clean up the resources after our test
699 await pool.destroy()
700 })
701
702 it('Verify LEAST_ELU strategy can be run in a dynamic pool', async () => {
703 const pool = new DynamicThreadPool(
704 min,
705 max,
706 './tests/worker-files/thread/testWorker.js',
707 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_ELU }
708 )
709 // TODO: Create a better test to cover `LeastEluWorkerChoiceStrategy#choose`
710 const promises = new Set()
711 const maxMultiplier = 2
712 for (let i = 0; i < max * maxMultiplier; i++) {
713 promises.add(pool.execute())
714 }
715 await Promise.all(promises)
716 for (const workerNode of pool.workerNodes) {
717 const expectedWorkerUsage = {
718 tasks: {
719 executed: expect.any(Number),
720 executing: 0,
721 queued: 0,
722 failed: 0
723 },
724 runTime: {
725 aggregate: 0,
726 average: 0,
727 median: 0,
728 history: expect.any(CircularArray)
729 },
730 waitTime: {
731 aggregate: 0,
732 average: 0,
733 median: 0,
734 history: expect.any(CircularArray)
735 }
736 }
737 if (workerNode.workerUsage.elu === undefined) {
738 expect(workerNode.workerUsage).toStrictEqual({
739 ...expectedWorkerUsage,
740 elu: undefined
741 })
742 } else {
743 expect(workerNode.workerUsage).toStrictEqual({
744 ...expectedWorkerUsage,
745 elu: {
746 active: expect.any(Number),
747 idle: 0,
748 utilization: 1
749 }
750 })
751 }
752 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
753 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
754 max * maxMultiplier
755 )
756 }
757 // We need to clean up the resources after our test
758 await pool.destroy()
759 })
760
761 it('Verify FAIR_SHARE strategy default tasks usage statistics requirements', async () => {
762 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
763 let pool = new FixedThreadPool(
764 max,
765 './tests/worker-files/thread/testWorker.js',
766 { workerChoiceStrategy }
767 )
768 expect(
769 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
770 ).toStrictEqual({
771 runTime: {
772 aggregate: true,
773 average: true,
774 median: false
775 },
776 waitTime: {
777 aggregate: false,
778 average: false,
779 median: false
780 },
781 elu: false
782 })
783 await pool.destroy()
784 pool = new DynamicThreadPool(
785 min,
786 max,
787 './tests/worker-files/thread/testWorker.js',
788 { workerChoiceStrategy }
789 )
790 expect(
791 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
792 ).toStrictEqual({
793 runTime: {
794 aggregate: true,
795 average: true,
796 median: false
797 },
798 waitTime: {
799 aggregate: false,
800 average: false,
801 median: false
802 },
803 elu: false
804 })
805 // We need to clean up the resources after our test
806 await pool.destroy()
807 })
808
809 it('Verify FAIR_SHARE strategy can be run in a fixed pool', async () => {
810 const pool = new FixedThreadPool(
811 max,
812 './tests/worker-files/thread/testWorker.js',
813 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
814 )
815 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
816 const promises = new Set()
817 const maxMultiplier = 2
818 for (let i = 0; i < max * maxMultiplier; i++) {
819 promises.add(pool.execute())
820 }
821 await Promise.all(promises)
822 for (const workerNode of pool.workerNodes) {
823 expect(workerNode.workerUsage).toStrictEqual({
824 tasks: {
825 executed: maxMultiplier,
826 executing: 0,
827 queued: 0,
828 failed: 0
829 },
830 runTime: {
831 aggregate: expect.any(Number),
832 average: expect.any(Number),
833 median: 0,
834 history: expect.any(CircularArray)
835 },
836 waitTime: {
837 aggregate: 0,
838 average: 0,
839 median: 0,
840 history: expect.any(CircularArray)
841 },
842 elu: undefined
843 })
844 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0)
845 expect(workerNode.workerUsage.runTime.average).toBeGreaterThan(0)
846 }
847 expect(
848 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
849 pool.workerChoiceStrategyContext.workerChoiceStrategy
850 ).workersVirtualTaskEndTimestamp.length
851 ).toBe(pool.workerNodes.length)
852 // We need to clean up the resources after our test
853 await pool.destroy()
854 })
855
856 it('Verify FAIR_SHARE strategy can be run in a dynamic pool', async () => {
857 const pool = new DynamicThreadPool(
858 min,
859 max,
860 './tests/worker-files/thread/testWorker.js',
861 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
862 )
863 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
864 const promises = new Set()
865 const maxMultiplier = 2
866 for (let i = 0; i < max * maxMultiplier; i++) {
867 promises.add(pool.execute())
868 }
869 await Promise.all(promises)
870 for (const workerNode of pool.workerNodes) {
871 expect(workerNode.workerUsage).toStrictEqual({
872 tasks: {
873 executed: maxMultiplier,
874 executing: 0,
875 queued: 0,
876 failed: 0
877 },
878 runTime: {
879 aggregate: expect.any(Number),
880 average: expect.any(Number),
881 median: 0,
882 history: expect.any(CircularArray)
883 },
884 waitTime: {
885 aggregate: 0,
886 average: 0,
887 median: 0,
888 history: expect.any(CircularArray)
889 },
890 elu: undefined
891 })
892 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0)
893 expect(workerNode.workerUsage.runTime.average).toBeGreaterThan(0)
894 }
895 expect(
896 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
897 pool.workerChoiceStrategyContext.workerChoiceStrategy
898 ).workersVirtualTaskEndTimestamp.length
899 ).toBe(pool.workerNodes.length)
900 // We need to clean up the resources after our test
901 await pool.destroy()
902 })
903
904 it('Verify FAIR_SHARE strategy can be run in a dynamic pool with median runtime statistic', async () => {
905 const pool = new DynamicThreadPool(
906 min,
907 max,
908 './tests/worker-files/thread/testWorker.js',
909 {
910 workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE,
911 workerChoiceStrategyOptions: {
912 runTime: { median: true }
913 }
914 }
915 )
916 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
917 const promises = new Set()
918 const maxMultiplier = 2
919 for (let i = 0; i < max * maxMultiplier; i++) {
920 promises.add(pool.execute())
921 }
922 await Promise.all(promises)
923 for (const workerNode of pool.workerNodes) {
924 expect(workerNode.workerUsage).toStrictEqual({
925 tasks: {
926 executed: maxMultiplier,
927 executing: 0,
928 queued: 0,
929 failed: 0
930 },
931 runTime: {
932 aggregate: expect.any(Number),
933 average: 0,
934 median: expect.any(Number),
935 history: expect.any(CircularArray)
936 },
937 waitTime: {
938 aggregate: 0,
939 average: 0,
940 median: 0,
941 history: expect.any(CircularArray)
942 },
943 elu: undefined
944 })
945 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0)
946 expect(workerNode.workerUsage.runTime.median).toBeGreaterThan(0)
947 }
948 expect(
949 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
950 pool.workerChoiceStrategyContext.workerChoiceStrategy
951 ).workersVirtualTaskEndTimestamp.length
952 ).toBe(pool.workerNodes.length)
953 // We need to clean up the resources after our test
954 await pool.destroy()
955 })
956
957 it('Verify FAIR_SHARE strategy internals are resets after setting it', async () => {
958 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
959 let pool = new FixedThreadPool(
960 max,
961 './tests/worker-files/thread/testWorker.js'
962 )
963 expect(
964 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
965 workerChoiceStrategy
966 ).workersVirtualTaskEndTimestamp
967 ).toBeInstanceOf(Array)
968 expect(
969 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
970 workerChoiceStrategy
971 ).workersVirtualTaskEndTimestamp.length
972 ).toBe(0)
973 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
974 workerChoiceStrategy
975 ).workersVirtualTaskEndTimestamp[0] = performance.now()
976 expect(
977 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
978 workerChoiceStrategy
979 ).workersVirtualTaskEndTimestamp.length
980 ).toBe(1)
981 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
982 expect(
983 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
984 workerChoiceStrategy
985 ).workersVirtualTaskEndTimestamp
986 ).toBeInstanceOf(Array)
987 expect(
988 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
989 workerChoiceStrategy
990 ).workersVirtualTaskEndTimestamp.length
991 ).toBe(0)
992 await pool.destroy()
993 pool = new DynamicThreadPool(
994 min,
995 max,
996 './tests/worker-files/thread/testWorker.js'
997 )
998 expect(
999 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1000 workerChoiceStrategy
1001 ).workersVirtualTaskEndTimestamp
1002 ).toBeInstanceOf(Array)
1003 expect(
1004 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1005 workerChoiceStrategy
1006 ).workersVirtualTaskEndTimestamp.length
1007 ).toBe(0)
1008 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1009 workerChoiceStrategy
1010 ).workersVirtualTaskEndTimestamp[0] = performance.now()
1011 expect(
1012 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1013 workerChoiceStrategy
1014 ).workersVirtualTaskEndTimestamp.length
1015 ).toBe(1)
1016 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1017 expect(
1018 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1019 workerChoiceStrategy
1020 ).workersVirtualTaskEndTimestamp
1021 ).toBeInstanceOf(Array)
1022 expect(
1023 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1024 workerChoiceStrategy
1025 ).workersVirtualTaskEndTimestamp.length
1026 ).toBe(0)
1027 // We need to clean up the resources after our test
1028 await pool.destroy()
1029 })
1030
1031 it('Verify WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
1032 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
1033 let pool = new FixedThreadPool(
1034 max,
1035 './tests/worker-files/thread/testWorker.js',
1036 { workerChoiceStrategy }
1037 )
1038 expect(
1039 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1040 ).toStrictEqual({
1041 runTime: {
1042 aggregate: true,
1043 average: true,
1044 median: false
1045 },
1046 waitTime: {
1047 aggregate: false,
1048 average: false,
1049 median: false
1050 },
1051 elu: false
1052 })
1053 await pool.destroy()
1054 pool = new DynamicThreadPool(
1055 min,
1056 max,
1057 './tests/worker-files/thread/testWorker.js',
1058 { workerChoiceStrategy }
1059 )
1060 expect(
1061 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1062 ).toStrictEqual({
1063 runTime: {
1064 aggregate: true,
1065 average: true,
1066 median: false
1067 },
1068 waitTime: {
1069 aggregate: false,
1070 average: false,
1071 median: false
1072 },
1073 elu: false
1074 })
1075 // We need to clean up the resources after our test
1076 await pool.destroy()
1077 })
1078
1079 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
1080 const pool = new FixedThreadPool(
1081 max,
1082 './tests/worker-files/thread/testWorker.js',
1083 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
1084 )
1085 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
1086 const promises = new Set()
1087 const maxMultiplier = 2
1088 for (let i = 0; i < max * maxMultiplier; i++) {
1089 promises.add(pool.execute())
1090 }
1091 await Promise.all(promises)
1092 for (const workerNode of pool.workerNodes) {
1093 expect(workerNode.workerUsage).toStrictEqual({
1094 tasks: {
1095 executed: expect.any(Number),
1096 executing: 0,
1097 queued: 0,
1098 failed: 0
1099 },
1100 runTime: {
1101 aggregate: expect.any(Number),
1102 average: expect.any(Number),
1103 median: 0,
1104 history: expect.any(CircularArray)
1105 },
1106 waitTime: {
1107 aggregate: 0,
1108 average: 0,
1109 median: 0,
1110 history: expect.any(CircularArray)
1111 },
1112 elu: undefined
1113 })
1114 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0)
1115 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
1116 max * maxMultiplier
1117 )
1118 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThanOrEqual(0)
1119 expect(workerNode.workerUsage.runTime.average).toBeGreaterThanOrEqual(0)
1120 }
1121 expect(
1122 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1123 pool.workerChoiceStrategyContext.workerChoiceStrategy
1124 ).defaultWorkerWeight
1125 ).toBeGreaterThan(0)
1126 expect(
1127 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1128 pool.workerChoiceStrategyContext.workerChoiceStrategy
1129 ).workerVirtualTaskRunTime
1130 ).toBeGreaterThanOrEqual(0)
1131 // We need to clean up the resources after our test
1132 await pool.destroy()
1133 })
1134
1135 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
1136 const pool = new DynamicThreadPool(
1137 min,
1138 max,
1139 './tests/worker-files/thread/testWorker.js',
1140 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
1141 )
1142 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
1143 const promises = new Set()
1144 const maxMultiplier = 2
1145 for (let i = 0; i < max * maxMultiplier; i++) {
1146 promises.add(pool.execute())
1147 }
1148 await Promise.all(promises)
1149 for (const workerNode of pool.workerNodes) {
1150 expect(workerNode.workerUsage).toStrictEqual({
1151 tasks: {
1152 executed: expect.any(Number),
1153 executing: 0,
1154 queued: 0,
1155 failed: 0
1156 },
1157 runTime: {
1158 aggregate: expect.any(Number),
1159 average: expect.any(Number),
1160 median: 0,
1161 history: expect.any(CircularArray)
1162 },
1163 waitTime: {
1164 aggregate: 0,
1165 average: 0,
1166 median: 0,
1167 history: expect.any(CircularArray)
1168 },
1169 elu: undefined
1170 })
1171 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThan(0)
1172 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
1173 max * maxMultiplier
1174 )
1175 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0)
1176 expect(workerNode.workerUsage.runTime.average).toBeGreaterThan(0)
1177 }
1178 expect(
1179 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1180 pool.workerChoiceStrategyContext.workerChoiceStrategy
1181 ).defaultWorkerWeight
1182 ).toBeGreaterThan(0)
1183 expect(
1184 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1185 pool.workerChoiceStrategyContext.workerChoiceStrategy
1186 ).workerVirtualTaskRunTime
1187 ).toBeGreaterThanOrEqual(0)
1188 // We need to clean up the resources after our test
1189 await pool.destroy()
1190 })
1191
1192 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool with median runtime statistic', async () => {
1193 const pool = new DynamicThreadPool(
1194 min,
1195 max,
1196 './tests/worker-files/thread/testWorker.js',
1197 {
1198 workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN,
1199 workerChoiceStrategyOptions: {
1200 runTime: { median: true }
1201 }
1202 }
1203 )
1204 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
1205 const promises = new Set()
1206 const maxMultiplier = 2
1207 for (let i = 0; i < max * maxMultiplier; i++) {
1208 promises.add(pool.execute())
1209 }
1210 await Promise.all(promises)
1211 for (const workerNode of pool.workerNodes) {
1212 expect(workerNode.workerUsage).toStrictEqual({
1213 tasks: {
1214 executed: expect.any(Number),
1215 executing: 0,
1216 queued: 0,
1217 failed: 0
1218 },
1219 runTime: {
1220 aggregate: expect.any(Number),
1221 average: 0,
1222 median: expect.any(Number),
1223 history: expect.any(CircularArray)
1224 },
1225 waitTime: {
1226 aggregate: 0,
1227 average: 0,
1228 median: 0,
1229 history: expect.any(CircularArray)
1230 },
1231 elu: undefined
1232 })
1233 expect(workerNode.workerUsage.tasks.executed).toBeGreaterThan(0)
1234 expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual(
1235 max * maxMultiplier
1236 )
1237 expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0)
1238 expect(workerNode.workerUsage.runTime.median).toBeGreaterThan(0)
1239 }
1240 expect(
1241 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1242 pool.workerChoiceStrategyContext.workerChoiceStrategy
1243 ).defaultWorkerWeight
1244 ).toBeGreaterThan(0)
1245 expect(
1246 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1247 pool.workerChoiceStrategyContext.workerChoiceStrategy
1248 ).workerVirtualTaskRunTime
1249 ).toBeGreaterThanOrEqual(0)
1250 // We need to clean up the resources after our test
1251 await pool.destroy()
1252 })
1253
1254 it('Verify WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
1255 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
1256 let pool = new FixedThreadPool(
1257 max,
1258 './tests/worker-files/thread/testWorker.js'
1259 )
1260 expect(
1261 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1262 workerChoiceStrategy
1263 ).currentWorkerNodeId
1264 ).toBeDefined()
1265 expect(
1266 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1267 workerChoiceStrategy
1268 ).defaultWorkerWeight
1269 ).toBeDefined()
1270 expect(
1271 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1272 workerChoiceStrategy
1273 ).workerVirtualTaskRunTime
1274 ).toBeDefined()
1275 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1276 expect(
1277 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1278 pool.workerChoiceStrategyContext.workerChoiceStrategy
1279 ).currentWorkerNodeId
1280 ).toBe(0)
1281 expect(
1282 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1283 pool.workerChoiceStrategyContext.workerChoiceStrategy
1284 ).defaultWorkerWeight
1285 ).toBeGreaterThan(0)
1286 expect(
1287 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1288 workerChoiceStrategy
1289 ).workerVirtualTaskRunTime
1290 ).toBe(0)
1291 await pool.destroy()
1292 pool = new DynamicThreadPool(
1293 min,
1294 max,
1295 './tests/worker-files/thread/testWorker.js'
1296 )
1297 expect(
1298 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1299 workerChoiceStrategy
1300 ).currentWorkerNodeId
1301 ).toBeDefined()
1302 expect(
1303 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1304 workerChoiceStrategy
1305 ).defaultWorkerWeight
1306 ).toBeDefined()
1307 expect(
1308 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1309 workerChoiceStrategy
1310 ).workerVirtualTaskRunTime
1311 ).toBeDefined()
1312 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1313 expect(
1314 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1315 pool.workerChoiceStrategyContext.workerChoiceStrategy
1316 ).currentWorkerNodeId
1317 ).toBe(0)
1318 expect(
1319 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1320 pool.workerChoiceStrategyContext.workerChoiceStrategy
1321 ).defaultWorkerWeight
1322 ).toBeGreaterThan(0)
1323 expect(
1324 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1325 workerChoiceStrategy
1326 ).workerVirtualTaskRunTime
1327 ).toBe(0)
1328 // We need to clean up the resources after our test
1329 await pool.destroy()
1330 })
1331
1332 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
1333 const workerChoiceStrategy =
1334 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1335 let pool = new FixedThreadPool(
1336 max,
1337 './tests/worker-files/thread/testWorker.js',
1338 { workerChoiceStrategy }
1339 )
1340 expect(
1341 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1342 ).toStrictEqual({
1343 runTime: {
1344 aggregate: false,
1345 average: false,
1346 median: false
1347 },
1348 waitTime: {
1349 aggregate: false,
1350 average: false,
1351 median: false
1352 },
1353 elu: false
1354 })
1355 await pool.destroy()
1356 pool = new DynamicThreadPool(
1357 min,
1358 max,
1359 './tests/worker-files/thread/testWorker.js',
1360 { workerChoiceStrategy }
1361 )
1362 expect(
1363 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1364 ).toStrictEqual({
1365 runTime: {
1366 aggregate: false,
1367 average: false,
1368 median: false
1369 },
1370 waitTime: {
1371 aggregate: false,
1372 average: false,
1373 median: false
1374 },
1375 elu: false
1376 })
1377 // We need to clean up the resources after our test
1378 await pool.destroy()
1379 })
1380
1381 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
1382 const pool = new FixedThreadPool(
1383 max,
1384 './tests/worker-files/thread/testWorker.js',
1385 {
1386 workerChoiceStrategy:
1387 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1388 }
1389 )
1390 // TODO: Create a better test to cover `InterleavedWeightedRoundRobinWorkerChoiceStrategy#choose`
1391 const promises = new Set()
1392 const maxMultiplier = 2
1393 for (let i = 0; i < max * maxMultiplier; i++) {
1394 promises.add(pool.execute())
1395 }
1396 await Promise.all(promises)
1397 for (const workerNode of pool.workerNodes) {
1398 expect(workerNode.workerUsage).toStrictEqual({
1399 tasks: {
1400 executed: maxMultiplier,
1401 executing: 0,
1402 queued: 0,
1403 failed: 0
1404 },
1405 runTime: {
1406 aggregate: 0,
1407 average: 0,
1408 median: 0,
1409 history: expect.any(CircularArray)
1410 },
1411 waitTime: {
1412 aggregate: 0,
1413 average: 0,
1414 median: 0,
1415 history: expect.any(CircularArray)
1416 },
1417 elu: undefined
1418 })
1419 }
1420 expect(
1421 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1422 pool.workerChoiceStrategyContext.workerChoiceStrategy
1423 ).defaultWorkerWeight
1424 ).toBeGreaterThan(0)
1425 expect(
1426 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1427 pool.workerChoiceStrategyContext.workerChoiceStrategy
1428 ).currentRoundId
1429 ).toBe(0)
1430 expect(
1431 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1432 pool.workerChoiceStrategyContext.workerChoiceStrategy
1433 ).currentWorkerNodeId
1434 ).toBe(0)
1435 expect(
1436 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1437 pool.workerChoiceStrategyContext.workerChoiceStrategy
1438 ).roundWeights
1439 ).toStrictEqual([
1440 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1441 pool.workerChoiceStrategyContext.workerChoiceStrategy
1442 ).defaultWorkerWeight
1443 ])
1444 // We need to clean up the resources after our test
1445 await pool.destroy()
1446 })
1447
1448 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
1449 const pool = new DynamicThreadPool(
1450 min,
1451 max,
1452 './tests/worker-files/thread/testWorker.js',
1453 {
1454 workerChoiceStrategy:
1455 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1456 }
1457 )
1458 // TODO: Create a better test to cover `InterleavedWeightedRoundRobinWorkerChoiceStrategy#choose`
1459 const promises = new Set()
1460 const maxMultiplier = 2
1461 for (let i = 0; i < max * maxMultiplier; i++) {
1462 promises.add(pool.execute())
1463 }
1464 await Promise.all(promises)
1465 for (const workerNode of pool.workerNodes) {
1466 expect(workerNode.workerUsage).toStrictEqual({
1467 tasks: {
1468 executed: maxMultiplier,
1469 executing: 0,
1470 queued: 0,
1471 failed: 0
1472 },
1473 runTime: {
1474 aggregate: 0,
1475 average: 0,
1476 median: 0,
1477 history: expect.any(CircularArray)
1478 },
1479 waitTime: {
1480 aggregate: 0,
1481 average: 0,
1482 median: 0,
1483 history: expect.any(CircularArray)
1484 },
1485 elu: undefined
1486 })
1487 }
1488 expect(
1489 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1490 pool.workerChoiceStrategyContext.workerChoiceStrategy
1491 ).defaultWorkerWeight
1492 ).toBeGreaterThan(0)
1493 expect(
1494 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1495 pool.workerChoiceStrategyContext.workerChoiceStrategy
1496 ).currentRoundId
1497 ).toBe(0)
1498 expect(
1499 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1500 pool.workerChoiceStrategyContext.workerChoiceStrategy
1501 ).currentWorkerNodeId
1502 ).toBe(0)
1503 expect(
1504 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1505 pool.workerChoiceStrategyContext.workerChoiceStrategy
1506 ).roundWeights
1507 ).toStrictEqual([
1508 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1509 pool.workerChoiceStrategyContext.workerChoiceStrategy
1510 ).defaultWorkerWeight
1511 ])
1512 // We need to clean up the resources after our test
1513 await pool.destroy()
1514 })
1515
1516 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
1517 const workerChoiceStrategy =
1518 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1519 let pool = new FixedThreadPool(
1520 max,
1521 './tests/worker-files/thread/testWorker.js'
1522 )
1523 expect(
1524 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1525 workerChoiceStrategy
1526 ).currentRoundId
1527 ).toBeDefined()
1528 expect(
1529 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1530 workerChoiceStrategy
1531 ).currentWorkerNodeId
1532 ).toBeDefined()
1533 expect(
1534 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1535 workerChoiceStrategy
1536 ).defaultWorkerWeight
1537 ).toBeDefined()
1538 expect(
1539 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1540 workerChoiceStrategy
1541 ).roundWeights
1542 ).toBeDefined()
1543 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1544 expect(
1545 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1546 workerChoiceStrategy
1547 ).currentRoundId
1548 ).toBe(0)
1549 expect(
1550 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1551 pool.workerChoiceStrategyContext.workerChoiceStrategy
1552 ).currentWorkerNodeId
1553 ).toBe(0)
1554 expect(
1555 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1556 pool.workerChoiceStrategyContext.workerChoiceStrategy
1557 ).defaultWorkerWeight
1558 ).toBeGreaterThan(0)
1559 expect(
1560 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1561 workerChoiceStrategy
1562 ).roundWeights
1563 ).toStrictEqual([
1564 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1565 pool.workerChoiceStrategyContext.workerChoiceStrategy
1566 ).defaultWorkerWeight
1567 ])
1568 await pool.destroy()
1569 pool = new DynamicThreadPool(
1570 min,
1571 max,
1572 './tests/worker-files/thread/testWorker.js'
1573 )
1574 expect(
1575 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1576 workerChoiceStrategy
1577 ).currentRoundId
1578 ).toBeDefined()
1579 expect(
1580 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1581 workerChoiceStrategy
1582 ).currentWorkerNodeId
1583 ).toBeDefined()
1584 expect(
1585 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1586 workerChoiceStrategy
1587 ).defaultWorkerWeight
1588 ).toBeDefined()
1589 expect(
1590 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1591 workerChoiceStrategy
1592 ).roundWeights
1593 ).toBeDefined()
1594 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1595 expect(
1596 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1597 pool.workerChoiceStrategyContext.workerChoiceStrategy
1598 ).currentWorkerNodeId
1599 ).toBe(0)
1600 expect(
1601 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1602 pool.workerChoiceStrategyContext.workerChoiceStrategy
1603 ).defaultWorkerWeight
1604 ).toBeGreaterThan(0)
1605 expect(
1606 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1607 workerChoiceStrategy
1608 ).roundWeights
1609 ).toStrictEqual([
1610 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1611 pool.workerChoiceStrategyContext.workerChoiceStrategy
1612 ).defaultWorkerWeight
1613 ])
1614 // We need to clean up the resources after our test
1615 await pool.destroy()
1616 })
1617
1618 it('Verify unknown strategy throw error', () => {
1619 expect(
1620 () =>
1621 new DynamicThreadPool(
1622 min,
1623 max,
1624 './tests/worker-files/thread/testWorker.js',
1625 { workerChoiceStrategy: 'UNKNOWN_STRATEGY' }
1626 )
1627 ).toThrowError("Invalid worker choice strategy 'UNKNOWN_STRATEGY'")
1628 })
1629 })