test: improve worker choice strategies testing
[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 = []
160 const maxMultiplier = 2
161 for (let i = 0; i < max * maxMultiplier; i++) {
162 promises.push(pool.execute())
163 }
164 await Promise.all(promises)
165 for (const workerNode of pool.workerNodes) {
166 expect(workerNode.tasksUsage).toStrictEqual({
167 run: expect.any(Number),
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 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
180 }
181 // We need to clean up the resources after our test
182 await pool.destroy()
183 })
184
185 it('Verify ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
186 const pool = new DynamicThreadPool(
187 min,
188 max,
189 './tests/worker-files/thread/testWorker.js',
190 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
191 )
192 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
193 const promises = []
194 const maxMultiplier = 2
195 for (let i = 0; i < max * maxMultiplier; i++) {
196 promises.push(pool.execute())
197 }
198 await Promise.all(promises)
199 for (const workerNode of pool.workerNodes) {
200 expect(workerNode.tasksUsage).toStrictEqual({
201 run: expect.any(Number),
202 running: 0,
203 runTime: 0,
204 runTimeHistory: expect.any(CircularArray),
205 avgRunTime: 0,
206 medRunTime: 0,
207 waitTime: 0,
208 waitTimeHistory: expect.any(CircularArray),
209 avgWaitTime: 0,
210 medWaitTime: 0,
211 error: 0
212 })
213 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
214 }
215 // We need to clean up the resources after our test
216 await pool.destroy()
217 })
218
219 it('Verify ROUND_ROBIN strategy runtime behavior', async () => {
220 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
221 let pool = new FixedClusterPool(
222 max,
223 './tests/worker-files/cluster/testWorker.js',
224 { workerChoiceStrategy }
225 )
226 let results = new Set()
227 for (let i = 0; i < max; i++) {
228 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.id)
229 }
230 expect(results.size).toBe(max)
231 await pool.destroy()
232 pool = new FixedThreadPool(
233 max,
234 './tests/worker-files/thread/testWorker.js',
235 { workerChoiceStrategy }
236 )
237 results = new Set()
238 for (let i = 0; i < max; i++) {
239 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.threadId)
240 }
241 expect(results.size).toBe(max)
242 await pool.destroy()
243 })
244
245 it('Verify ROUND_ROBIN strategy internals are resets after setting it', async () => {
246 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
247 let pool = new FixedThreadPool(
248 max,
249 './tests/worker-files/thread/testWorker.js',
250 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
251 )
252 expect(
253 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
254 workerChoiceStrategy
255 ).nextWorkerNodeId
256 ).toBeDefined()
257 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
258 expect(
259 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
260 pool.workerChoiceStrategyContext.workerChoiceStrategy
261 ).nextWorkerNodeId
262 ).toBe(0)
263 await pool.destroy()
264 pool = new DynamicThreadPool(
265 min,
266 max,
267 './tests/worker-files/thread/testWorker.js',
268 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
269 )
270 expect(
271 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
272 workerChoiceStrategy
273 ).nextWorkerNodeId
274 ).toBeDefined()
275 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
276 expect(
277 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
278 pool.workerChoiceStrategyContext.workerChoiceStrategy
279 ).nextWorkerNodeId
280 ).toBe(0)
281 // We need to clean up the resources after our test
282 await pool.destroy()
283 })
284
285 it('Verify LEAST_USED strategy default tasks usage statistics requirements', async () => {
286 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
287 let pool = new FixedThreadPool(
288 max,
289 './tests/worker-files/thread/testWorker.js',
290 { workerChoiceStrategy }
291 )
292 expect(
293 pool.workerChoiceStrategyContext.getRequiredStatistics()
294 ).toStrictEqual({
295 runTime: false,
296 avgRunTime: false,
297 medRunTime: false,
298 waitTime: false,
299 avgWaitTime: false,
300 medWaitTime: false
301 })
302 await pool.destroy()
303 pool = new DynamicThreadPool(
304 min,
305 max,
306 './tests/worker-files/thread/testWorker.js',
307 { workerChoiceStrategy }
308 )
309 expect(
310 pool.workerChoiceStrategyContext.getRequiredStatistics()
311 ).toStrictEqual({
312 runTime: false,
313 avgRunTime: false,
314 medRunTime: false,
315 waitTime: false,
316 avgWaitTime: false,
317 medWaitTime: false
318 })
319 // We need to clean up the resources after our test
320 await pool.destroy()
321 })
322
323 it('Verify LEAST_USED strategy can be run in a fixed pool', async () => {
324 const pool = new FixedThreadPool(
325 max,
326 './tests/worker-files/thread/testWorker.js',
327 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
328 )
329 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
330 const promises = []
331 const maxMultiplier = 2
332 for (let i = 0; i < max * maxMultiplier; i++) {
333 promises.push(pool.execute())
334 }
335 await Promise.all(promises)
336 for (const workerNode of pool.workerNodes) {
337 expect(workerNode.tasksUsage).toStrictEqual({
338 run: expect.any(Number),
339 running: 0,
340 runTime: 0,
341 runTimeHistory: expect.any(CircularArray),
342 avgRunTime: 0,
343 medRunTime: 0,
344 waitTime: 0,
345 waitTimeHistory: expect.any(CircularArray),
346 avgWaitTime: 0,
347 medWaitTime: 0,
348 error: 0
349 })
350 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
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 = []
365 const maxMultiplier = 2
366 for (let i = 0; i < max * maxMultiplier; i++) {
367 promises.push(pool.execute())
368 }
369 await Promise.all(promises)
370 for (const workerNode of pool.workerNodes) {
371 expect(workerNode.tasksUsage).toStrictEqual({
372 run: expect.any(Number),
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 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
385 }
386 // We need to clean up the resources after our test
387 await pool.destroy()
388 })
389
390 it('Verify LEAST_BUSY strategy default tasks usage statistics requirements', async () => {
391 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
392 let pool = new FixedThreadPool(
393 max,
394 './tests/worker-files/thread/testWorker.js',
395 { workerChoiceStrategy }
396 )
397 expect(
398 pool.workerChoiceStrategyContext.getRequiredStatistics()
399 ).toStrictEqual({
400 runTime: true,
401 avgRunTime: false,
402 medRunTime: false,
403 waitTime: false,
404 avgWaitTime: false,
405 medWaitTime: false
406 })
407 await pool.destroy()
408 pool = new DynamicThreadPool(
409 min,
410 max,
411 './tests/worker-files/thread/testWorker.js',
412 { workerChoiceStrategy }
413 )
414 expect(
415 pool.workerChoiceStrategyContext.getRequiredStatistics()
416 ).toStrictEqual({
417 runTime: true,
418 avgRunTime: false,
419 medRunTime: false,
420 waitTime: false,
421 avgWaitTime: false,
422 medWaitTime: false
423 })
424 // We need to clean up the resources after our test
425 await pool.destroy()
426 })
427
428 it('Verify LEAST_BUSY strategy can be run in a fixed pool', async () => {
429 const pool = new FixedThreadPool(
430 max,
431 './tests/worker-files/thread/testWorker.js',
432 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
433 )
434 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
435 const promises = []
436 const maxMultiplier = 2
437 for (let i = 0; i < max * maxMultiplier; i++) {
438 promises.push(pool.execute())
439 }
440 await Promise.all(promises)
441 for (const workerNode of pool.workerNodes) {
442 expect(workerNode.tasksUsage).toStrictEqual({
443 run: expect.any(Number),
444 running: 0,
445 runTime: expect.any(Number),
446 runTimeHistory: expect.any(CircularArray),
447 avgRunTime: 0,
448 medRunTime: 0,
449 waitTime: 0,
450 waitTimeHistory: expect.any(CircularArray),
451 avgWaitTime: 0,
452 medWaitTime: 0,
453 error: 0
454 })
455 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
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 = []
471 const maxMultiplier = 2
472 for (let i = 0; i < max * maxMultiplier; i++) {
473 promises.push(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.runTime).toBeGreaterThan(0)
492 }
493 // We need to clean up the resources after our test
494 await pool.destroy()
495 })
496
497 it('Verify FAIR_SHARE strategy default tasks usage statistics requirements', async () => {
498 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
499 let pool = new FixedThreadPool(
500 max,
501 './tests/worker-files/thread/testWorker.js',
502 { workerChoiceStrategy }
503 )
504 expect(
505 pool.workerChoiceStrategyContext.getRequiredStatistics()
506 ).toStrictEqual({
507 runTime: true,
508 avgRunTime: true,
509 medRunTime: false,
510 waitTime: false,
511 avgWaitTime: false,
512 medWaitTime: false
513 })
514 await pool.destroy()
515 pool = new DynamicThreadPool(
516 min,
517 max,
518 './tests/worker-files/thread/testWorker.js',
519 { workerChoiceStrategy }
520 )
521 expect(
522 pool.workerChoiceStrategyContext.getRequiredStatistics()
523 ).toStrictEqual({
524 runTime: true,
525 avgRunTime: true,
526 medRunTime: false,
527 waitTime: false,
528 avgWaitTime: false,
529 medWaitTime: false
530 })
531 // We need to clean up the resources after our test
532 await pool.destroy()
533 })
534
535 it('Verify FAIR_SHARE strategy can be run in a fixed pool', async () => {
536 const pool = new FixedThreadPool(
537 max,
538 './tests/worker-files/thread/testWorker.js',
539 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
540 )
541 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
542 const promises = []
543 const maxMultiplier = 2
544 for (let i = 0; i < max * maxMultiplier; i++) {
545 promises.push(pool.execute())
546 }
547 await Promise.all(promises)
548 for (const workerNode of pool.workerNodes) {
549 expect(workerNode.tasksUsage).toStrictEqual({
550 run: expect.any(Number),
551 running: 0,
552 runTime: expect.any(Number),
553 runTimeHistory: expect.any(CircularArray),
554 avgRunTime: expect.any(Number),
555 medRunTime: 0,
556 waitTime: 0,
557 waitTimeHistory: expect.any(CircularArray),
558 avgWaitTime: 0,
559 medWaitTime: 0,
560 error: 0
561 })
562 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
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 = []
584 const maxMultiplier = 2
585 for (let i = 0; i < max * maxMultiplier; i++) {
586 promises.push(pool.execute())
587 }
588 await Promise.all(promises)
589 for (const workerNode of pool.workerNodes) {
590 expect(workerNode.tasksUsage).toStrictEqual({
591 run: expect.any(Number),
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.run).toBeGreaterThan(0)
604 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
605 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThan(0)
606 }
607 expect(
608 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
609 pool.workerChoiceStrategyContext.workerChoiceStrategy
610 ).workersVirtualTaskEndTimestamp.length
611 ).toBe(pool.workerNodes.length)
612 // We need to clean up the resources after our test
613 await pool.destroy()
614 })
615
616 it('Verify FAIR_SHARE strategy can be run in a dynamic pool with median runtime statistic', async () => {
617 const pool = new DynamicThreadPool(
618 min,
619 max,
620 './tests/worker-files/thread/testWorker.js',
621 {
622 workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE,
623 workerChoiceStrategyOptions: {
624 medRunTime: true
625 }
626 }
627 )
628 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
629 const promises = []
630 const maxMultiplier = 2
631 for (let i = 0; i < max * maxMultiplier; i++) {
632 promises.push(pool.execute())
633 }
634 await Promise.all(promises)
635 for (const workerNode of pool.workerNodes) {
636 expect(workerNode.tasksUsage).toStrictEqual({
637 run: expect.any(Number),
638 running: 0,
639 runTime: expect.any(Number),
640 runTimeHistory: expect.any(CircularArray),
641 avgRunTime: 0,
642 medRunTime: expect.any(Number),
643 waitTime: 0,
644 waitTimeHistory: expect.any(CircularArray),
645 avgWaitTime: 0,
646 medWaitTime: 0,
647 error: 0
648 })
649 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
650 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
651 expect(workerNode.tasksUsage.medRunTime).toBeGreaterThan(0)
652 }
653 expect(
654 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
655 pool.workerChoiceStrategyContext.workerChoiceStrategy
656 ).workersVirtualTaskEndTimestamp.length
657 ).toBe(pool.workerNodes.length)
658 // We need to clean up the resources after our test
659 await pool.destroy()
660 })
661
662 it('Verify FAIR_SHARE strategy internals are resets after setting it', async () => {
663 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
664 let pool = new FixedThreadPool(
665 max,
666 './tests/worker-files/thread/testWorker.js'
667 )
668 expect(
669 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
670 workerChoiceStrategy
671 ).workersVirtualTaskEndTimestamp
672 ).toBeInstanceOf(Array)
673 expect(
674 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
675 workerChoiceStrategy
676 ).workersVirtualTaskEndTimestamp.length
677 ).toBe(0)
678 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
679 workerChoiceStrategy
680 ).workersVirtualTaskEndTimestamp[0] = performance.now()
681 expect(
682 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
683 workerChoiceStrategy
684 ).workersVirtualTaskEndTimestamp.length
685 ).toBe(1)
686 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
687 expect(
688 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
689 workerChoiceStrategy
690 ).workersVirtualTaskEndTimestamp
691 ).toBeInstanceOf(Array)
692 expect(
693 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
694 workerChoiceStrategy
695 ).workersVirtualTaskEndTimestamp.length
696 ).toBe(0)
697 await pool.destroy()
698 pool = new DynamicThreadPool(
699 min,
700 max,
701 './tests/worker-files/thread/testWorker.js'
702 )
703 expect(
704 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
705 workerChoiceStrategy
706 ).workersVirtualTaskEndTimestamp
707 ).toBeInstanceOf(Array)
708 expect(
709 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
710 workerChoiceStrategy
711 ).workersVirtualTaskEndTimestamp.length
712 ).toBe(0)
713 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
714 workerChoiceStrategy
715 ).workersVirtualTaskEndTimestamp[0] = performance.now()
716 expect(
717 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
718 workerChoiceStrategy
719 ).workersVirtualTaskEndTimestamp.length
720 ).toBe(1)
721 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
722 expect(
723 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
724 workerChoiceStrategy
725 ).workersVirtualTaskEndTimestamp
726 ).toBeInstanceOf(Array)
727 expect(
728 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
729 workerChoiceStrategy
730 ).workersVirtualTaskEndTimestamp.length
731 ).toBe(0)
732 // We need to clean up the resources after our test
733 await pool.destroy()
734 })
735
736 it('Verify WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
737 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
738 let pool = new FixedThreadPool(
739 max,
740 './tests/worker-files/thread/testWorker.js',
741 { workerChoiceStrategy }
742 )
743 expect(
744 pool.workerChoiceStrategyContext.getRequiredStatistics()
745 ).toStrictEqual({
746 runTime: true,
747 avgRunTime: true,
748 medRunTime: false,
749 waitTime: false,
750 avgWaitTime: false,
751 medWaitTime: false
752 })
753 await pool.destroy()
754 pool = new DynamicThreadPool(
755 min,
756 max,
757 './tests/worker-files/thread/testWorker.js',
758 { workerChoiceStrategy }
759 )
760 expect(
761 pool.workerChoiceStrategyContext.getRequiredStatistics()
762 ).toStrictEqual({
763 runTime: true,
764 avgRunTime: true,
765 medRunTime: false,
766 waitTime: false,
767 avgWaitTime: false,
768 medWaitTime: false
769 })
770 // We need to clean up the resources after our test
771 await pool.destroy()
772 })
773
774 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
775 const pool = new FixedThreadPool(
776 max,
777 './tests/worker-files/thread/testWorker.js',
778 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
779 )
780 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
781 const promises = []
782 const maxMultiplier = 2
783 for (let i = 0; i < max * maxMultiplier; i++) {
784 promises.push(pool.execute())
785 }
786 await Promise.all(promises)
787 for (const workerNode of pool.workerNodes) {
788 expect(workerNode.tasksUsage).toStrictEqual({
789 run: expect.any(Number),
790 running: 0,
791 runTime: expect.any(Number),
792 runTimeHistory: expect.any(CircularArray),
793 avgRunTime: expect.any(Number),
794 medRunTime: 0,
795 waitTime: 0,
796 waitTimeHistory: expect.any(CircularArray),
797 avgWaitTime: 0,
798 medWaitTime: 0,
799 error: 0
800 })
801 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
802 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
803 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThan(0)
804 }
805 expect(
806 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
807 pool.workerChoiceStrategyContext.workerChoiceStrategy
808 ).defaultWorkerWeight
809 ).toBeGreaterThan(0)
810 expect(
811 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
812 pool.workerChoiceStrategyContext.workerChoiceStrategy
813 ).workerVirtualTaskRunTime
814 ).toBeGreaterThanOrEqual(0)
815 // We need to clean up the resources after our test
816 await pool.destroy()
817 })
818
819 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
820 const pool = new DynamicThreadPool(
821 min,
822 max,
823 './tests/worker-files/thread/testWorker.js',
824 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
825 )
826 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
827 const promises = []
828 const maxMultiplier = 2
829 for (let i = 0; i < max * maxMultiplier; i++) {
830 promises.push(pool.execute())
831 }
832 await Promise.all(promises)
833 for (const workerNode of pool.workerNodes) {
834 expect(workerNode.tasksUsage).toStrictEqual({
835 run: expect.any(Number),
836 running: 0,
837 runTime: expect.any(Number),
838 runTimeHistory: expect.any(CircularArray),
839 avgRunTime: expect.any(Number),
840 medRunTime: 0,
841 waitTime: 0,
842 waitTimeHistory: expect.any(CircularArray),
843 avgWaitTime: 0,
844 medWaitTime: 0,
845 error: 0
846 })
847 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
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 = []
879 const maxMultiplier = 2
880 for (let i = 0; i < max * maxMultiplier; i++) {
881 promises.push(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.runTime).toBeGreaterThan(0)
900 expect(workerNode.tasksUsage.medRunTime).toBeGreaterThan(0)
901 }
902 expect(
903 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
904 pool.workerChoiceStrategyContext.workerChoiceStrategy
905 ).defaultWorkerWeight
906 ).toBeGreaterThan(0)
907 expect(
908 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
909 pool.workerChoiceStrategyContext.workerChoiceStrategy
910 ).workerVirtualTaskRunTime
911 ).toBeGreaterThanOrEqual(0)
912 // We need to clean up the resources after our test
913 await pool.destroy()
914 })
915
916 it('Verify WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
917 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
918 let pool = new FixedThreadPool(
919 max,
920 './tests/worker-files/thread/testWorker.js'
921 )
922 expect(
923 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
924 workerChoiceStrategy
925 ).currentWorkerNodeId
926 ).toBeDefined()
927 expect(
928 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
929 workerChoiceStrategy
930 ).defaultWorkerWeight
931 ).toBeDefined()
932 expect(
933 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
934 workerChoiceStrategy
935 ).workerVirtualTaskRunTime
936 ).toBeDefined()
937 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
938 expect(
939 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
940 pool.workerChoiceStrategyContext.workerChoiceStrategy
941 ).currentWorkerNodeId
942 ).toBe(0)
943 expect(
944 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
945 pool.workerChoiceStrategyContext.workerChoiceStrategy
946 ).defaultWorkerWeight
947 ).toBeGreaterThan(0)
948 expect(
949 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
950 workerChoiceStrategy
951 ).workerVirtualTaskRunTime
952 ).toBe(0)
953 await pool.destroy()
954 pool = new DynamicThreadPool(
955 min,
956 max,
957 './tests/worker-files/thread/testWorker.js'
958 )
959 expect(
960 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
961 workerChoiceStrategy
962 ).currentWorkerNodeId
963 ).toBeDefined()
964 expect(
965 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
966 workerChoiceStrategy
967 ).defaultWorkerWeight
968 ).toBeDefined()
969 expect(
970 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
971 workerChoiceStrategy
972 ).workerVirtualTaskRunTime
973 ).toBeDefined()
974 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
975 expect(
976 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
977 pool.workerChoiceStrategyContext.workerChoiceStrategy
978 ).currentWorkerNodeId
979 ).toBe(0)
980 expect(
981 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
982 pool.workerChoiceStrategyContext.workerChoiceStrategy
983 ).defaultWorkerWeight
984 ).toBeGreaterThan(0)
985 expect(
986 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
987 workerChoiceStrategy
988 ).workerVirtualTaskRunTime
989 ).toBe(0)
990 // We need to clean up the resources after our test
991 await pool.destroy()
992 })
993
994 it('Verify unknown strategy throw error', () => {
995 expect(
996 () =>
997 new DynamicThreadPool(
998 min,
999 max,
1000 './tests/worker-files/thread/testWorker.js',
1001 { workerChoiceStrategy: 'UNKNOWN_STRATEGY' }
1002 )
1003 ).toThrowError("Invalid worker choice strategy 'UNKNOWN_STRATEGY'")
1004 })
1005 })