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