test: test for IWRR statistics requirements
[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.FAIR_SHARE).toBe('FAIR_SHARE')
19 expect(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN).toBe(
20 'WEIGHTED_ROUND_ROBIN'
21 )
22 expect(WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN).toBe(
23 'INTERLEAVED_WEIGHTED_ROUND_ROBIN'
24 )
25 })
26
27 it('Verify ROUND_ROBIN strategy is the default at pool creation', async () => {
28 const pool = new DynamicThreadPool(
29 min,
30 max,
31 './tests/worker-files/thread/testWorker.js'
32 )
33 expect(pool.opts.workerChoiceStrategy).toBe(
34 WorkerChoiceStrategies.ROUND_ROBIN
35 )
36 // We need to clean up the resources after our test
37 await pool.destroy()
38 })
39
40 it('Verify available strategies are taken at pool creation', async () => {
41 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
42 const pool = new FixedThreadPool(
43 max,
44 './tests/worker-files/thread/testWorker.js',
45 { workerChoiceStrategy }
46 )
47 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
48 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
49 workerChoiceStrategy
50 )
51 await pool.destroy()
52 }
53 })
54
55 it('Verify available strategies can be set after pool creation', async () => {
56 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
57 const pool = new DynamicThreadPool(
58 min,
59 max,
60 './tests/worker-files/thread/testWorker.js'
61 )
62 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
63 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
64 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
65 workerChoiceStrategy
66 )
67 await pool.destroy()
68 }
69 })
70
71 it('Verify available strategies default internals at pool creation', async () => {
72 const pool = new FixedThreadPool(
73 max,
74 './tests/worker-files/thread/testWorker.js'
75 )
76 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
77 if (workerChoiceStrategy === WorkerChoiceStrategies.ROUND_ROBIN) {
78 expect(
79 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
80 workerChoiceStrategy
81 ).nextWorkerNodeId
82 ).toBe(0)
83 } else if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) {
84 expect(
85 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
86 workerChoiceStrategy
87 ).workersVirtualTaskEndTimestamp
88 ).toBeInstanceOf(Array)
89 expect(
90 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
91 workerChoiceStrategy
92 ).workersVirtualTaskEndTimestamp.length
93 ).toBe(0)
94 } else if (
95 workerChoiceStrategy === WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
96 ) {
97 expect(
98 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
99 workerChoiceStrategy
100 ).currentWorkerNodeId
101 ).toBe(0)
102 expect(
103 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
104 workerChoiceStrategy
105 ).defaultWorkerWeight
106 ).toBeGreaterThan(0)
107 expect(
108 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
109 workerChoiceStrategy
110 ).workerVirtualTaskRunTime
111 ).toBe(0)
112 }
113 }
114 await pool.destroy()
115 })
116
117 it('Verify ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
118 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
119 let pool = new FixedThreadPool(
120 max,
121 './tests/worker-files/thread/testWorker.js',
122 { workerChoiceStrategy }
123 )
124 expect(
125 pool.workerChoiceStrategyContext.getRequiredStatistics()
126 ).toStrictEqual({
127 runTime: false,
128 avgRunTime: false,
129 medRunTime: false,
130 waitTime: false,
131 avgWaitTime: false,
132 medWaitTime: false
133 })
134 await pool.destroy()
135 pool = new DynamicThreadPool(
136 min,
137 max,
138 './tests/worker-files/thread/testWorker.js',
139 { workerChoiceStrategy }
140 )
141 expect(
142 pool.workerChoiceStrategyContext.getRequiredStatistics()
143 ).toStrictEqual({
144 runTime: false,
145 avgRunTime: false,
146 medRunTime: false,
147 waitTime: false,
148 avgWaitTime: false,
149 medWaitTime: false
150 })
151 // We need to clean up the resources after our test
152 await pool.destroy()
153 })
154
155 it('Verify ROUND_ROBIN strategy can be run in a fixed pool', async () => {
156 const pool = new FixedThreadPool(
157 max,
158 './tests/worker-files/thread/testWorker.js',
159 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
160 )
161 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
162 const promises = new Set()
163 const maxMultiplier = 2
164 for (let i = 0; i < max * maxMultiplier; i++) {
165 promises.add(pool.execute())
166 }
167 await Promise.all(promises)
168 for (const workerNode of pool.workerNodes) {
169 expect(workerNode.tasksUsage).toStrictEqual({
170 run: maxMultiplier,
171 running: 0,
172 runTime: 0,
173 runTimeHistory: expect.any(CircularArray),
174 avgRunTime: 0,
175 medRunTime: 0,
176 waitTime: 0,
177 waitTimeHistory: expect.any(CircularArray),
178 avgWaitTime: 0,
179 medWaitTime: 0,
180 error: 0
181 })
182 }
183 // We need to clean up the resources after our test
184 await pool.destroy()
185 })
186
187 it('Verify ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
188 const pool = new DynamicThreadPool(
189 min,
190 max,
191 './tests/worker-files/thread/testWorker.js',
192 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
193 )
194 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
195 const promises = new Set()
196 const maxMultiplier = 2
197 for (let i = 0; i < max * maxMultiplier; i++) {
198 promises.add(pool.execute())
199 }
200 await Promise.all(promises)
201 for (const workerNode of pool.workerNodes) {
202 expect(workerNode.tasksUsage).toStrictEqual({
203 run: maxMultiplier,
204 running: 0,
205 runTime: 0,
206 runTimeHistory: expect.any(CircularArray),
207 avgRunTime: 0,
208 medRunTime: 0,
209 waitTime: 0,
210 waitTimeHistory: expect.any(CircularArray),
211 avgWaitTime: 0,
212 medWaitTime: 0,
213 error: 0
214 })
215 }
216 // We need to clean up the resources after our test
217 await pool.destroy()
218 })
219
220 it('Verify ROUND_ROBIN strategy runtime behavior', async () => {
221 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
222 let pool = new FixedClusterPool(
223 max,
224 './tests/worker-files/cluster/testWorker.js',
225 { workerChoiceStrategy }
226 )
227 let results = new Set()
228 for (let i = 0; i < max; i++) {
229 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.id)
230 }
231 expect(results.size).toBe(max)
232 await pool.destroy()
233 pool = new FixedThreadPool(
234 max,
235 './tests/worker-files/thread/testWorker.js',
236 { workerChoiceStrategy }
237 )
238 results = new Set()
239 for (let i = 0; i < max; i++) {
240 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.threadId)
241 }
242 expect(results.size).toBe(max)
243 await pool.destroy()
244 })
245
246 it('Verify ROUND_ROBIN strategy internals are resets after setting it', async () => {
247 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
248 let pool = new FixedThreadPool(
249 max,
250 './tests/worker-files/thread/testWorker.js',
251 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
252 )
253 expect(
254 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
255 workerChoiceStrategy
256 ).nextWorkerNodeId
257 ).toBeDefined()
258 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
259 expect(
260 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
261 pool.workerChoiceStrategyContext.workerChoiceStrategy
262 ).nextWorkerNodeId
263 ).toBe(0)
264 await pool.destroy()
265 pool = new DynamicThreadPool(
266 min,
267 max,
268 './tests/worker-files/thread/testWorker.js',
269 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
270 )
271 expect(
272 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
273 workerChoiceStrategy
274 ).nextWorkerNodeId
275 ).toBeDefined()
276 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
277 expect(
278 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
279 pool.workerChoiceStrategyContext.workerChoiceStrategy
280 ).nextWorkerNodeId
281 ).toBe(0)
282 // We need to clean up the resources after our test
283 await pool.destroy()
284 })
285
286 it('Verify LEAST_USED strategy default tasks usage statistics requirements', async () => {
287 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
288 let pool = new FixedThreadPool(
289 max,
290 './tests/worker-files/thread/testWorker.js',
291 { workerChoiceStrategy }
292 )
293 expect(
294 pool.workerChoiceStrategyContext.getRequiredStatistics()
295 ).toStrictEqual({
296 runTime: false,
297 avgRunTime: false,
298 medRunTime: false,
299 waitTime: false,
300 avgWaitTime: false,
301 medWaitTime: false
302 })
303 await pool.destroy()
304 pool = new DynamicThreadPool(
305 min,
306 max,
307 './tests/worker-files/thread/testWorker.js',
308 { workerChoiceStrategy }
309 )
310 expect(
311 pool.workerChoiceStrategyContext.getRequiredStatistics()
312 ).toStrictEqual({
313 runTime: false,
314 avgRunTime: false,
315 medRunTime: false,
316 waitTime: false,
317 avgWaitTime: false,
318 medWaitTime: false
319 })
320 // We need to clean up the resources after our test
321 await pool.destroy()
322 })
323
324 it('Verify LEAST_USED strategy can be run in a fixed pool', async () => {
325 const pool = new FixedThreadPool(
326 max,
327 './tests/worker-files/thread/testWorker.js',
328 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
329 )
330 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
331 const promises = new Set()
332 const maxMultiplier = 2
333 for (let i = 0; i < max * maxMultiplier; i++) {
334 promises.add(pool.execute())
335 }
336 await Promise.all(promises)
337 for (const workerNode of pool.workerNodes) {
338 expect(workerNode.tasksUsage).toStrictEqual({
339 run: maxMultiplier,
340 running: 0,
341 runTime: 0,
342 runTimeHistory: expect.any(CircularArray),
343 avgRunTime: 0,
344 medRunTime: 0,
345 waitTime: 0,
346 waitTimeHistory: expect.any(CircularArray),
347 avgWaitTime: 0,
348 medWaitTime: 0,
349 error: 0
350 })
351 }
352 // We need to clean up the resources after our test
353 await pool.destroy()
354 })
355
356 it('Verify LEAST_USED strategy can be run in a dynamic pool', async () => {
357 const pool = new DynamicThreadPool(
358 min,
359 max,
360 './tests/worker-files/thread/testWorker.js',
361 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
362 )
363 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
364 const promises = new Set()
365 const maxMultiplier = 2
366 for (let i = 0; i < max * maxMultiplier; i++) {
367 promises.add(pool.execute())
368 }
369 await Promise.all(promises)
370 for (const workerNode of pool.workerNodes) {
371 expect(workerNode.tasksUsage).toStrictEqual({
372 run: maxMultiplier,
373 running: 0,
374 runTime: 0,
375 runTimeHistory: expect.any(CircularArray),
376 avgRunTime: 0,
377 medRunTime: 0,
378 waitTime: 0,
379 waitTimeHistory: expect.any(CircularArray),
380 avgWaitTime: 0,
381 medWaitTime: 0,
382 error: 0
383 })
384 }
385 // We need to clean up the resources after our test
386 await pool.destroy()
387 })
388
389 it('Verify LEAST_BUSY strategy default tasks usage statistics requirements', async () => {
390 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
391 let pool = new FixedThreadPool(
392 max,
393 './tests/worker-files/thread/testWorker.js',
394 { workerChoiceStrategy }
395 )
396 expect(
397 pool.workerChoiceStrategyContext.getRequiredStatistics()
398 ).toStrictEqual({
399 runTime: true,
400 avgRunTime: false,
401 medRunTime: false,
402 waitTime: false,
403 avgWaitTime: false,
404 medWaitTime: false
405 })
406 await pool.destroy()
407 pool = new DynamicThreadPool(
408 min,
409 max,
410 './tests/worker-files/thread/testWorker.js',
411 { workerChoiceStrategy }
412 )
413 expect(
414 pool.workerChoiceStrategyContext.getRequiredStatistics()
415 ).toStrictEqual({
416 runTime: true,
417 avgRunTime: false,
418 medRunTime: false,
419 waitTime: false,
420 avgWaitTime: false,
421 medWaitTime: false
422 })
423 // We need to clean up the resources after our test
424 await pool.destroy()
425 })
426
427 it('Verify LEAST_BUSY strategy can be run in a fixed pool', async () => {
428 const pool = new FixedThreadPool(
429 max,
430 './tests/worker-files/thread/testWorker.js',
431 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
432 )
433 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
434 const promises = new Set()
435 const maxMultiplier = 2
436 for (let i = 0; i < max * maxMultiplier; i++) {
437 promises.add(pool.execute())
438 }
439 await Promise.all(promises)
440 for (const workerNode of pool.workerNodes) {
441 expect(workerNode.tasksUsage).toStrictEqual({
442 run: expect.any(Number),
443 running: 0,
444 runTime: expect.any(Number),
445 runTimeHistory: expect.any(CircularArray),
446 avgRunTime: 0,
447 medRunTime: 0,
448 waitTime: 0,
449 waitTimeHistory: expect.any(CircularArray),
450 avgWaitTime: 0,
451 medWaitTime: 0,
452 error: 0
453 })
454 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
455 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
456 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
457 }
458 // We need to clean up the resources after our test
459 await pool.destroy()
460 })
461
462 it('Verify LEAST_BUSY strategy can be run in a dynamic pool', async () => {
463 const pool = new DynamicThreadPool(
464 min,
465 max,
466 './tests/worker-files/thread/testWorker.js',
467 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
468 )
469 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
470 const promises = new Set()
471 const maxMultiplier = 2
472 for (let i = 0; i < max * maxMultiplier; i++) {
473 promises.add(pool.execute())
474 }
475 await Promise.all(promises)
476 for (const workerNode of pool.workerNodes) {
477 expect(workerNode.tasksUsage).toStrictEqual({
478 run: expect.any(Number),
479 running: 0,
480 runTime: expect.any(Number),
481 runTimeHistory: expect.any(CircularArray),
482 avgRunTime: 0,
483 medRunTime: 0,
484 waitTime: 0,
485 waitTimeHistory: expect.any(CircularArray),
486 avgWaitTime: 0,
487 medWaitTime: 0,
488 error: 0
489 })
490 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
491 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
492 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
493 }
494 // We need to clean up the resources after our test
495 await pool.destroy()
496 })
497
498 it('Verify FAIR_SHARE strategy default tasks usage statistics requirements', async () => {
499 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
500 let pool = new FixedThreadPool(
501 max,
502 './tests/worker-files/thread/testWorker.js',
503 { workerChoiceStrategy }
504 )
505 expect(
506 pool.workerChoiceStrategyContext.getRequiredStatistics()
507 ).toStrictEqual({
508 runTime: true,
509 avgRunTime: true,
510 medRunTime: false,
511 waitTime: false,
512 avgWaitTime: false,
513 medWaitTime: false
514 })
515 await pool.destroy()
516 pool = new DynamicThreadPool(
517 min,
518 max,
519 './tests/worker-files/thread/testWorker.js',
520 { workerChoiceStrategy }
521 )
522 expect(
523 pool.workerChoiceStrategyContext.getRequiredStatistics()
524 ).toStrictEqual({
525 runTime: true,
526 avgRunTime: true,
527 medRunTime: false,
528 waitTime: false,
529 avgWaitTime: false,
530 medWaitTime: false
531 })
532 // We need to clean up the resources after our test
533 await pool.destroy()
534 })
535
536 it('Verify FAIR_SHARE strategy can be run in a fixed pool', async () => {
537 const pool = new FixedThreadPool(
538 max,
539 './tests/worker-files/thread/testWorker.js',
540 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
541 )
542 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
543 const promises = new Set()
544 const maxMultiplier = 2
545 for (let i = 0; i < max * maxMultiplier; i++) {
546 promises.add(pool.execute())
547 }
548 await Promise.all(promises)
549 for (const workerNode of pool.workerNodes) {
550 expect(workerNode.tasksUsage).toStrictEqual({
551 run: maxMultiplier,
552 running: 0,
553 runTime: expect.any(Number),
554 runTimeHistory: expect.any(CircularArray),
555 avgRunTime: expect.any(Number),
556 medRunTime: 0,
557 waitTime: 0,
558 waitTimeHistory: expect.any(CircularArray),
559 avgWaitTime: 0,
560 medWaitTime: 0,
561 error: 0
562 })
563 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
564 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThan(0)
565 }
566 expect(
567 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
568 pool.workerChoiceStrategyContext.workerChoiceStrategy
569 ).workersVirtualTaskEndTimestamp.length
570 ).toBe(pool.workerNodes.length)
571 // We need to clean up the resources after our test
572 await pool.destroy()
573 })
574
575 it('Verify FAIR_SHARE strategy can be run in a dynamic pool', async () => {
576 const pool = new DynamicThreadPool(
577 min,
578 max,
579 './tests/worker-files/thread/testWorker.js',
580 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
581 )
582 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
583 const promises = new Set()
584 const maxMultiplier = 2
585 for (let i = 0; i < max * maxMultiplier; i++) {
586 promises.add(pool.execute())
587 }
588 await Promise.all(promises)
589 for (const workerNode of pool.workerNodes) {
590 expect(workerNode.tasksUsage).toStrictEqual({
591 run: maxMultiplier,
592 running: 0,
593 runTime: expect.any(Number),
594 runTimeHistory: expect.any(CircularArray),
595 avgRunTime: expect.any(Number),
596 medRunTime: 0,
597 waitTime: 0,
598 waitTimeHistory: expect.any(CircularArray),
599 avgWaitTime: 0,
600 medWaitTime: 0,
601 error: 0
602 })
603 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
604 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThan(0)
605 }
606 expect(
607 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
608 pool.workerChoiceStrategyContext.workerChoiceStrategy
609 ).workersVirtualTaskEndTimestamp.length
610 ).toBe(pool.workerNodes.length)
611 // We need to clean up the resources after our test
612 await pool.destroy()
613 })
614
615 it('Verify FAIR_SHARE strategy can be run in a dynamic pool with median runtime statistic', async () => {
616 const pool = new DynamicThreadPool(
617 min,
618 max,
619 './tests/worker-files/thread/testWorker.js',
620 {
621 workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE,
622 workerChoiceStrategyOptions: {
623 medRunTime: true
624 }
625 }
626 )
627 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
628 const promises = new Set()
629 const maxMultiplier = 2
630 for (let i = 0; i < max * maxMultiplier; i++) {
631 promises.add(pool.execute())
632 }
633 await Promise.all(promises)
634 for (const workerNode of pool.workerNodes) {
635 expect(workerNode.tasksUsage).toStrictEqual({
636 run: maxMultiplier,
637 running: 0,
638 runTime: expect.any(Number),
639 runTimeHistory: expect.any(CircularArray),
640 avgRunTime: 0,
641 medRunTime: expect.any(Number),
642 waitTime: 0,
643 waitTimeHistory: expect.any(CircularArray),
644 avgWaitTime: 0,
645 medWaitTime: 0,
646 error: 0
647 })
648 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
649 expect(workerNode.tasksUsage.medRunTime).toBeGreaterThan(0)
650 }
651 expect(
652 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
653 pool.workerChoiceStrategyContext.workerChoiceStrategy
654 ).workersVirtualTaskEndTimestamp.length
655 ).toBe(pool.workerNodes.length)
656 // We need to clean up the resources after our test
657 await pool.destroy()
658 })
659
660 it('Verify FAIR_SHARE strategy internals are resets after setting it', async () => {
661 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
662 let pool = new FixedThreadPool(
663 max,
664 './tests/worker-files/thread/testWorker.js'
665 )
666 expect(
667 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
668 workerChoiceStrategy
669 ).workersVirtualTaskEndTimestamp
670 ).toBeInstanceOf(Array)
671 expect(
672 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
673 workerChoiceStrategy
674 ).workersVirtualTaskEndTimestamp.length
675 ).toBe(0)
676 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
677 workerChoiceStrategy
678 ).workersVirtualTaskEndTimestamp[0] = performance.now()
679 expect(
680 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
681 workerChoiceStrategy
682 ).workersVirtualTaskEndTimestamp.length
683 ).toBe(1)
684 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
685 expect(
686 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
687 workerChoiceStrategy
688 ).workersVirtualTaskEndTimestamp
689 ).toBeInstanceOf(Array)
690 expect(
691 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
692 workerChoiceStrategy
693 ).workersVirtualTaskEndTimestamp.length
694 ).toBe(0)
695 await pool.destroy()
696 pool = new DynamicThreadPool(
697 min,
698 max,
699 './tests/worker-files/thread/testWorker.js'
700 )
701 expect(
702 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
703 workerChoiceStrategy
704 ).workersVirtualTaskEndTimestamp
705 ).toBeInstanceOf(Array)
706 expect(
707 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
708 workerChoiceStrategy
709 ).workersVirtualTaskEndTimestamp.length
710 ).toBe(0)
711 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
712 workerChoiceStrategy
713 ).workersVirtualTaskEndTimestamp[0] = performance.now()
714 expect(
715 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
716 workerChoiceStrategy
717 ).workersVirtualTaskEndTimestamp.length
718 ).toBe(1)
719 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
720 expect(
721 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
722 workerChoiceStrategy
723 ).workersVirtualTaskEndTimestamp
724 ).toBeInstanceOf(Array)
725 expect(
726 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
727 workerChoiceStrategy
728 ).workersVirtualTaskEndTimestamp.length
729 ).toBe(0)
730 // We need to clean up the resources after our test
731 await pool.destroy()
732 })
733
734 it('Verify WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
735 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
736 let pool = new FixedThreadPool(
737 max,
738 './tests/worker-files/thread/testWorker.js',
739 { workerChoiceStrategy }
740 )
741 expect(
742 pool.workerChoiceStrategyContext.getRequiredStatistics()
743 ).toStrictEqual({
744 runTime: true,
745 avgRunTime: true,
746 medRunTime: false,
747 waitTime: false,
748 avgWaitTime: false,
749 medWaitTime: false
750 })
751 await pool.destroy()
752 pool = new DynamicThreadPool(
753 min,
754 max,
755 './tests/worker-files/thread/testWorker.js',
756 { workerChoiceStrategy }
757 )
758 expect(
759 pool.workerChoiceStrategyContext.getRequiredStatistics()
760 ).toStrictEqual({
761 runTime: true,
762 avgRunTime: true,
763 medRunTime: false,
764 waitTime: false,
765 avgWaitTime: false,
766 medWaitTime: false
767 })
768 // We need to clean up the resources after our test
769 await pool.destroy()
770 })
771
772 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
773 const pool = new FixedThreadPool(
774 max,
775 './tests/worker-files/thread/testWorker.js',
776 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
777 )
778 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
779 const promises = new Set()
780 const maxMultiplier = 2
781 for (let i = 0; i < max * maxMultiplier; i++) {
782 promises.add(pool.execute())
783 }
784 await Promise.all(promises)
785 for (const workerNode of pool.workerNodes) {
786 expect(workerNode.tasksUsage).toStrictEqual({
787 run: expect.any(Number),
788 running: 0,
789 runTime: expect.any(Number),
790 runTimeHistory: expect.any(CircularArray),
791 avgRunTime: expect.any(Number),
792 medRunTime: 0,
793 waitTime: 0,
794 waitTimeHistory: expect.any(CircularArray),
795 avgWaitTime: 0,
796 medWaitTime: 0,
797 error: 0
798 })
799 expect(workerNode.tasksUsage.run).toBeGreaterThanOrEqual(0)
800 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
801 expect(workerNode.tasksUsage.runTime).toBeGreaterThanOrEqual(0)
802 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0)
803 }
804 expect(
805 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
806 pool.workerChoiceStrategyContext.workerChoiceStrategy
807 ).defaultWorkerWeight
808 ).toBeGreaterThan(0)
809 expect(
810 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
811 pool.workerChoiceStrategyContext.workerChoiceStrategy
812 ).workerVirtualTaskRunTime
813 ).toBeGreaterThanOrEqual(0)
814 // We need to clean up the resources after our test
815 await pool.destroy()
816 })
817
818 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
819 const pool = new DynamicThreadPool(
820 min,
821 max,
822 './tests/worker-files/thread/testWorker.js',
823 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
824 )
825 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
826 const promises = new Set()
827 const maxMultiplier = 2
828 for (let i = 0; i < max * maxMultiplier; i++) {
829 promises.add(pool.execute())
830 }
831 await Promise.all(promises)
832 for (const workerNode of pool.workerNodes) {
833 expect(workerNode.tasksUsage).toStrictEqual({
834 run: expect.any(Number),
835 running: 0,
836 runTime: expect.any(Number),
837 runTimeHistory: expect.any(CircularArray),
838 avgRunTime: expect.any(Number),
839 medRunTime: 0,
840 waitTime: 0,
841 waitTimeHistory: expect.any(CircularArray),
842 avgWaitTime: 0,
843 medWaitTime: 0,
844 error: 0
845 })
846 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
847 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
848 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
849 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThan(0)
850 }
851 expect(
852 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
853 pool.workerChoiceStrategyContext.workerChoiceStrategy
854 ).defaultWorkerWeight
855 ).toBeGreaterThan(0)
856 expect(
857 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
858 pool.workerChoiceStrategyContext.workerChoiceStrategy
859 ).workerVirtualTaskRunTime
860 ).toBeGreaterThanOrEqual(0)
861 // We need to clean up the resources after our test
862 await pool.destroy()
863 })
864
865 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool with median runtime statistic', async () => {
866 const pool = new DynamicThreadPool(
867 min,
868 max,
869 './tests/worker-files/thread/testWorker.js',
870 {
871 workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN,
872 workerChoiceStrategyOptions: {
873 medRunTime: true
874 }
875 }
876 )
877 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
878 const promises = new Set()
879 const maxMultiplier = 2
880 for (let i = 0; i < max * maxMultiplier; i++) {
881 promises.add(pool.execute())
882 }
883 await Promise.all(promises)
884 for (const workerNode of pool.workerNodes) {
885 expect(workerNode.tasksUsage).toStrictEqual({
886 run: expect.any(Number),
887 running: 0,
888 runTime: expect.any(Number),
889 runTimeHistory: expect.any(CircularArray),
890 avgRunTime: 0,
891 medRunTime: expect.any(Number),
892 waitTime: 0,
893 waitTimeHistory: expect.any(CircularArray),
894 avgWaitTime: 0,
895 medWaitTime: 0,
896 error: 0
897 })
898 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
899 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
900 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
901 expect(workerNode.tasksUsage.medRunTime).toBeGreaterThan(0)
902 }
903 expect(
904 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
905 pool.workerChoiceStrategyContext.workerChoiceStrategy
906 ).defaultWorkerWeight
907 ).toBeGreaterThan(0)
908 expect(
909 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
910 pool.workerChoiceStrategyContext.workerChoiceStrategy
911 ).workerVirtualTaskRunTime
912 ).toBeGreaterThanOrEqual(0)
913 // We need to clean up the resources after our test
914 await pool.destroy()
915 })
916
917 it('Verify WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
918 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
919 let pool = new FixedThreadPool(
920 max,
921 './tests/worker-files/thread/testWorker.js'
922 )
923 expect(
924 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
925 workerChoiceStrategy
926 ).currentWorkerNodeId
927 ).toBeDefined()
928 expect(
929 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
930 workerChoiceStrategy
931 ).defaultWorkerWeight
932 ).toBeDefined()
933 expect(
934 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
935 workerChoiceStrategy
936 ).workerVirtualTaskRunTime
937 ).toBeDefined()
938 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
939 expect(
940 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
941 pool.workerChoiceStrategyContext.workerChoiceStrategy
942 ).currentWorkerNodeId
943 ).toBe(0)
944 expect(
945 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
946 pool.workerChoiceStrategyContext.workerChoiceStrategy
947 ).defaultWorkerWeight
948 ).toBeGreaterThan(0)
949 expect(
950 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
951 workerChoiceStrategy
952 ).workerVirtualTaskRunTime
953 ).toBe(0)
954 await pool.destroy()
955 pool = new DynamicThreadPool(
956 min,
957 max,
958 './tests/worker-files/thread/testWorker.js'
959 )
960 expect(
961 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
962 workerChoiceStrategy
963 ).currentWorkerNodeId
964 ).toBeDefined()
965 expect(
966 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
967 workerChoiceStrategy
968 ).defaultWorkerWeight
969 ).toBeDefined()
970 expect(
971 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
972 workerChoiceStrategy
973 ).workerVirtualTaskRunTime
974 ).toBeDefined()
975 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
976 expect(
977 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
978 pool.workerChoiceStrategyContext.workerChoiceStrategy
979 ).currentWorkerNodeId
980 ).toBe(0)
981 expect(
982 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
983 pool.workerChoiceStrategyContext.workerChoiceStrategy
984 ).defaultWorkerWeight
985 ).toBeGreaterThan(0)
986 expect(
987 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
988 workerChoiceStrategy
989 ).workerVirtualTaskRunTime
990 ).toBe(0)
991 // We need to clean up the resources after our test
992 await pool.destroy()
993 })
994
995 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
996 const workerChoiceStrategy =
997 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
998 let pool = new FixedThreadPool(
999 max,
1000 './tests/worker-files/thread/testWorker.js',
1001 { workerChoiceStrategy }
1002 )
1003 expect(
1004 pool.workerChoiceStrategyContext.getRequiredStatistics()
1005 ).toStrictEqual({
1006 runTime: false,
1007 avgRunTime: false,
1008 medRunTime: false,
1009 waitTime: false,
1010 avgWaitTime: false,
1011 medWaitTime: false
1012 })
1013 await pool.destroy()
1014 pool = new DynamicThreadPool(
1015 min,
1016 max,
1017 './tests/worker-files/thread/testWorker.js',
1018 { workerChoiceStrategy }
1019 )
1020 expect(
1021 pool.workerChoiceStrategyContext.getRequiredStatistics()
1022 ).toStrictEqual({
1023 runTime: false,
1024 avgRunTime: false,
1025 medRunTime: false,
1026 waitTime: false,
1027 avgWaitTime: false,
1028 medWaitTime: false
1029 })
1030 // We need to clean up the resources after our test
1031 await pool.destroy()
1032 })
1033
1034 it('Verify unknown strategy throw error', () => {
1035 expect(
1036 () =>
1037 new DynamicThreadPool(
1038 min,
1039 max,
1040 './tests/worker-files/thread/testWorker.js',
1041 { workerChoiceStrategy: 'UNKNOWN_STRATEGY' }
1042 )
1043 ).toThrowError("Invalid worker choice strategy 'UNKNOWN_STRATEGY'")
1044 })
1045 })