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