feat: untangle worker choice strategies tasks distribution and dynamic worker creatio...
[poolifier.git] / tests / pools / selection-strategies / selection-strategies.test.js
CommitLineData
a61a0724 1const { expect } = require('expect')
a35560ba 2const {
a35560ba 3 DynamicThreadPool,
3d6dd312 4 FixedClusterPool,
2ced693a 5 FixedThreadPool,
3d6dd312 6 WorkerChoiceStrategies
cdace0e5 7} = require('../../../lib')
86bf340d 8const { CircularArray } = require('../../../lib/circular-array')
a35560ba
S
9
10describe('Selection strategies test suite', () => {
e1ffb94f
JB
11 const min = 0
12 const max = 3
13
a35560ba
S
14 it('Verify that WorkerChoiceStrategies enumeration provides string values', () => {
15 expect(WorkerChoiceStrategies.ROUND_ROBIN).toBe('ROUND_ROBIN')
e4543b14
JB
16 expect(WorkerChoiceStrategies.LEAST_USED).toBe('LEAST_USED')
17 expect(WorkerChoiceStrategies.LEAST_BUSY).toBe('LEAST_BUSY')
a7bbf44a 18 expect(WorkerChoiceStrategies.LEAST_ELU).toBe('LEAST_ELU')
23ff945a 19 expect(WorkerChoiceStrategies.FAIR_SHARE).toBe('FAIR_SHARE')
b3432a63
JB
20 expect(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN).toBe(
21 'WEIGHTED_ROUND_ROBIN'
22 )
feec6e8c
JB
23 expect(WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN).toBe(
24 'INTERLEAVED_WEIGHTED_ROUND_ROBIN'
25 )
a35560ba
S
26 })
27
e843b904 28 it('Verify ROUND_ROBIN strategy is the default at pool creation', async () => {
e843b904
JB
29 const pool = new DynamicThreadPool(
30 min,
31 max,
32 './tests/worker-files/thread/testWorker.js'
33 )
34 expect(pool.opts.workerChoiceStrategy).toBe(
35 WorkerChoiceStrategies.ROUND_ROBIN
36 )
37 // We need to clean up the resources after our test
38 await pool.destroy()
39 })
40
594bfb84
JB
41 it('Verify available strategies are taken at pool creation', async () => {
42 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
43 const pool = new FixedThreadPool(
44 max,
45 './tests/worker-files/thread/testWorker.js',
46 { workerChoiceStrategy }
47 )
48 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
49 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
50 workerChoiceStrategy
51 )
52 await pool.destroy()
53 }
d2f7b7a2
JB
54 })
55
594bfb84
JB
56 it('Verify available strategies can be set after pool creation', async () => {
57 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
58 const pool = new DynamicThreadPool(
59 min,
60 max,
ec82cfa1 61 './tests/worker-files/thread/testWorker.js'
594bfb84
JB
62 )
63 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
64 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
65 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
66 workerChoiceStrategy
67 )
68 await pool.destroy()
69 }
70 })
71
72 it('Verify available strategies default internals at pool creation', async () => {
73 const pool = new FixedThreadPool(
e843b904
JB
74 max,
75 './tests/worker-files/thread/testWorker.js'
76 )
594bfb84
JB
77 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
78 if (workerChoiceStrategy === WorkerChoiceStrategies.ROUND_ROBIN) {
79 expect(
80 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
81 workerChoiceStrategy
9b106837 82 ).nextWorkerNodeKey
594bfb84
JB
83 ).toBe(0)
84 } else if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) {
08f3f44c
JB
85 expect(
86 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
87 workerChoiceStrategy
b0d6ed8f 88 ).workersVirtualTaskEndTimestamp
08f3f44c
JB
89 ).toBeInstanceOf(Array)
90 expect(
91 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
92 workerChoiceStrategy
b0d6ed8f 93 ).workersVirtualTaskEndTimestamp.length
08f3f44c 94 ).toBe(0)
594bfb84
JB
95 } else if (
96 workerChoiceStrategy === WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
97 ) {
98 expect(
99 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
100 workerChoiceStrategy
9b106837 101 ).nextWorkerNodeKey
594bfb84
JB
102 ).toBe(0)
103 expect(
104 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
105 workerChoiceStrategy
106 ).defaultWorkerWeight
107 ).toBeGreaterThan(0)
08f3f44c
JB
108 expect(
109 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
110 workerChoiceStrategy
111 ).workerVirtualTaskRunTime
112 ).toBe(0)
594bfb84
JB
113 }
114 }
e843b904
JB
115 await pool.destroy()
116 })
117
6c6afb84
JB
118 it('Verify ROUND_ROBIN strategy default policy', async () => {
119 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
120 let pool = new FixedThreadPool(
121 max,
122 './tests/worker-files/thread/testWorker.js',
123 { workerChoiceStrategy }
124 )
125 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
94407def 126 dynamicWorkerUsage: false,
b1aae695 127 dynamicWorkerReady: true
6c6afb84
JB
128 })
129 await pool.destroy()
130 pool = new DynamicThreadPool(
131 min,
132 max,
133 './tests/worker-files/thread/testWorker.js',
134 { workerChoiceStrategy }
135 )
136 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
94407def 137 dynamicWorkerUsage: false,
b1aae695 138 dynamicWorkerReady: true
6c6afb84
JB
139 })
140 // We need to clean up the resources after our test
141 await pool.destroy()
142 })
143
144 it('Verify ROUND_ROBIN strategy default tasks statistics requirements', async () => {
594bfb84 145 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
10fcfaf4
JB
146 let pool = new FixedThreadPool(
147 max,
d710242d 148 './tests/worker-files/thread/testWorker.js',
594bfb84 149 { workerChoiceStrategy }
10fcfaf4 150 )
87de9ff5
JB
151 expect(
152 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
153 ).toStrictEqual({
932fc8be
JB
154 runTime: {
155 aggregate: false,
156 average: false,
157 median: false
158 },
159 waitTime: {
160 aggregate: false,
161 average: false,
162 median: false
163 },
5df69fab
JB
164 elu: {
165 aggregate: false,
166 average: false,
167 median: false
168 }
86bf340d 169 })
fd7ebd49 170 await pool.destroy()
10fcfaf4
JB
171 pool = new DynamicThreadPool(
172 min,
173 max,
d710242d 174 './tests/worker-files/thread/testWorker.js',
594bfb84 175 { workerChoiceStrategy }
10fcfaf4 176 )
87de9ff5
JB
177 expect(
178 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
179 ).toStrictEqual({
932fc8be
JB
180 runTime: {
181 aggregate: false,
182 average: false,
183 median: false
184 },
185 waitTime: {
186 aggregate: false,
187 average: false,
188 median: false
189 },
5df69fab
JB
190 elu: {
191 aggregate: false,
192 average: false,
193 median: false
194 }
86bf340d 195 })
10fcfaf4
JB
196 // We need to clean up the resources after our test
197 await pool.destroy()
198 })
199
bdaf31cd 200 it('Verify ROUND_ROBIN strategy can be run in a fixed pool', async () => {
bdaf31cd
JB
201 const pool = new FixedThreadPool(
202 max,
203 './tests/worker-files/thread/testWorker.js',
204 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
205 )
bdaf31cd 206 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
ee9f5295 207 const promises = new Set()
a20f0ba5
JB
208 const maxMultiplier = 2
209 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 210 promises.add(pool.execute())
e211bc18
JB
211 }
212 await Promise.all(promises)
213 for (const workerNode of pool.workerNodes) {
465b2940 214 expect(workerNode.usage).toStrictEqual({
a4e07f72
JB
215 tasks: {
216 executed: maxMultiplier,
217 executing: 0,
218 queued: 0,
df593701 219 maxQueued: 0,
a4e07f72
JB
220 failed: 0
221 },
222 runTime: {
a4e07f72
JB
223 history: expect.any(CircularArray)
224 },
225 waitTime: {
a4e07f72
JB
226 history: expect.any(CircularArray)
227 },
5df69fab
JB
228 elu: {
229 idle: {
5df69fab
JB
230 history: expect.any(CircularArray)
231 },
232 active: {
5df69fab 233 history: expect.any(CircularArray)
71514351 234 }
5df69fab 235 }
e211bc18 236 })
bdaf31cd 237 }
9458090a
JB
238 expect(
239 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
240 WorkerChoiceStrategies.ROUND_ROBIN
9b106837 241 ).nextWorkerNodeKey
9458090a 242 ).toBe(0)
bdaf31cd
JB
243 // We need to clean up the resources after our test
244 await pool.destroy()
245 })
246
247 it('Verify ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
bdaf31cd
JB
248 const pool = new DynamicThreadPool(
249 min,
250 max,
251 './tests/worker-files/thread/testWorker.js',
252 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
253 )
bdaf31cd 254 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
ee9f5295 255 const promises = new Set()
a20f0ba5
JB
256 const maxMultiplier = 2
257 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 258 promises.add(pool.execute())
e211bc18
JB
259 }
260 await Promise.all(promises)
261 for (const workerNode of pool.workerNodes) {
465b2940 262 expect(workerNode.usage).toStrictEqual({
a4e07f72 263 tasks: {
94407def 264 executed: expect.any(Number),
a4e07f72
JB
265 executing: 0,
266 queued: 0,
df593701 267 maxQueued: 0,
a4e07f72
JB
268 failed: 0
269 },
270 runTime: {
a4e07f72
JB
271 history: expect.any(CircularArray)
272 },
273 waitTime: {
a4e07f72
JB
274 history: expect.any(CircularArray)
275 },
5df69fab
JB
276 elu: {
277 idle: {
5df69fab
JB
278 history: expect.any(CircularArray)
279 },
280 active: {
5df69fab 281 history: expect.any(CircularArray)
71514351 282 }
5df69fab 283 }
e211bc18 284 })
94407def
JB
285 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
286 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
287 max * maxMultiplier
288 )
bdaf31cd 289 }
9458090a
JB
290 expect(
291 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
292 WorkerChoiceStrategies.ROUND_ROBIN
9b106837 293 ).nextWorkerNodeKey
9458090a 294 ).toBe(0)
bdaf31cd
JB
295 // We need to clean up the resources after our test
296 await pool.destroy()
297 })
298
2ced693a 299 it('Verify ROUND_ROBIN strategy runtime behavior', async () => {
594bfb84 300 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
2ced693a
JB
301 let pool = new FixedClusterPool(
302 max,
594bfb84
JB
303 './tests/worker-files/cluster/testWorker.js',
304 { workerChoiceStrategy }
2ced693a
JB
305 )
306 let results = new Set()
307 for (let i = 0; i < max; i++) {
20dcad1a 308 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.id)
2ced693a
JB
309 }
310 expect(results.size).toBe(max)
311 await pool.destroy()
594bfb84
JB
312 pool = new FixedThreadPool(
313 max,
314 './tests/worker-files/thread/testWorker.js',
315 { workerChoiceStrategy }
316 )
2ced693a
JB
317 results = new Set()
318 for (let i = 0; i < max; i++) {
20dcad1a 319 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.threadId)
2ced693a
JB
320 }
321 expect(results.size).toBe(max)
322 await pool.destroy()
323 })
324
a6f7f1b4 325 it('Verify ROUND_ROBIN strategy internals are resets after setting it', async () => {
594bfb84 326 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
a6f7f1b4
JB
327 let pool = new FixedThreadPool(
328 max,
329 './tests/worker-files/thread/testWorker.js',
330 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
331 )
38f6e859 332 expect(
95c83464 333 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 334 workerChoiceStrategy
9b106837 335 ).nextWorkerNodeKey
b529c323 336 ).toBeDefined()
594bfb84 337 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 338 expect(
95c83464 339 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 340 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 341 ).nextWorkerNodeKey
a6f7f1b4
JB
342 ).toBe(0)
343 await pool.destroy()
344 pool = new DynamicThreadPool(
345 min,
346 max,
347 './tests/worker-files/thread/testWorker.js',
348 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
349 )
38f6e859 350 expect(
95c83464 351 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 352 workerChoiceStrategy
9b106837 353 ).nextWorkerNodeKey
b529c323 354 ).toBeDefined()
594bfb84 355 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 356 expect(
95c83464 357 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 358 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 359 ).nextWorkerNodeKey
a6f7f1b4
JB
360 ).toBe(0)
361 // We need to clean up the resources after our test
362 await pool.destroy()
363 })
364
6c6afb84
JB
365 it('Verify LEAST_USED strategy default policy', async () => {
366 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
367 let pool = new FixedThreadPool(
368 max,
369 './tests/worker-files/thread/testWorker.js',
370 { workerChoiceStrategy }
371 )
372 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
373 dynamicWorkerUsage: false,
374 dynamicWorkerReady: true
6c6afb84
JB
375 })
376 await pool.destroy()
377 pool = new DynamicThreadPool(
378 min,
379 max,
380 './tests/worker-files/thread/testWorker.js',
381 { workerChoiceStrategy }
382 )
383 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
384 dynamicWorkerUsage: false,
385 dynamicWorkerReady: true
6c6afb84
JB
386 })
387 // We need to clean up the resources after our test
388 await pool.destroy()
389 })
390
391 it('Verify LEAST_USED strategy default tasks statistics requirements', async () => {
e4543b14 392 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
10fcfaf4
JB
393 let pool = new FixedThreadPool(
394 max,
d710242d 395 './tests/worker-files/thread/testWorker.js',
594bfb84 396 { workerChoiceStrategy }
10fcfaf4 397 )
87de9ff5
JB
398 expect(
399 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
400 ).toStrictEqual({
932fc8be
JB
401 runTime: {
402 aggregate: false,
403 average: false,
404 median: false
405 },
406 waitTime: {
407 aggregate: false,
408 average: false,
409 median: false
410 },
5df69fab
JB
411 elu: {
412 aggregate: false,
413 average: false,
414 median: false
415 }
86bf340d 416 })
fd7ebd49 417 await pool.destroy()
10fcfaf4
JB
418 pool = new DynamicThreadPool(
419 min,
420 max,
d710242d 421 './tests/worker-files/thread/testWorker.js',
594bfb84 422 { workerChoiceStrategy }
10fcfaf4 423 )
87de9ff5
JB
424 expect(
425 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
426 ).toStrictEqual({
932fc8be
JB
427 runTime: {
428 aggregate: false,
429 average: false,
430 median: false
431 },
432 waitTime: {
433 aggregate: false,
434 average: false,
435 median: false
436 },
5df69fab
JB
437 elu: {
438 aggregate: false,
439 average: false,
440 median: false
441 }
86bf340d 442 })
10fcfaf4
JB
443 // We need to clean up the resources after our test
444 await pool.destroy()
445 })
446
e4543b14 447 it('Verify LEAST_USED strategy can be run in a fixed pool', async () => {
b98ec2e6
JB
448 const pool = new FixedThreadPool(
449 max,
450 './tests/worker-files/thread/testWorker.js',
e4543b14 451 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
b98ec2e6 452 )
e4543b14 453 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
ee9f5295 454 const promises = new Set()
a20f0ba5
JB
455 const maxMultiplier = 2
456 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 457 promises.add(pool.execute())
e211bc18
JB
458 }
459 await Promise.all(promises)
460 for (const workerNode of pool.workerNodes) {
465b2940 461 expect(workerNode.usage).toStrictEqual({
a4e07f72 462 tasks: {
76407b8e 463 executed: expect.any(Number),
a4e07f72
JB
464 executing: 0,
465 queued: 0,
df593701 466 maxQueued: 0,
a4e07f72
JB
467 failed: 0
468 },
469 runTime: {
a4e07f72
JB
470 history: expect.any(CircularArray)
471 },
472 waitTime: {
a4e07f72
JB
473 history: expect.any(CircularArray)
474 },
5df69fab
JB
475 elu: {
476 idle: {
5df69fab
JB
477 history: expect.any(CircularArray)
478 },
479 active: {
5df69fab 480 history: expect.any(CircularArray)
71514351 481 }
5df69fab 482 }
e211bc18 483 })
465b2940
JB
484 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
485 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
76407b8e
JB
486 max * maxMultiplier
487 )
a35560ba 488 }
a35560ba
S
489 // We need to clean up the resources after our test
490 await pool.destroy()
491 })
492
e4543b14 493 it('Verify LEAST_USED strategy can be run in a dynamic pool', async () => {
ff5e76e1
JB
494 const pool = new DynamicThreadPool(
495 min,
496 max,
497 './tests/worker-files/thread/testWorker.js',
e4543b14 498 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
ff5e76e1 499 )
e4543b14 500 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
ee9f5295 501 const promises = new Set()
a20f0ba5
JB
502 const maxMultiplier = 2
503 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 504 promises.add(pool.execute())
e211bc18
JB
505 }
506 await Promise.all(promises)
507 for (const workerNode of pool.workerNodes) {
465b2940 508 expect(workerNode.usage).toStrictEqual({
a4e07f72 509 tasks: {
76407b8e 510 executed: expect.any(Number),
a4e07f72
JB
511 executing: 0,
512 queued: 0,
df593701 513 maxQueued: 0,
a4e07f72
JB
514 failed: 0
515 },
516 runTime: {
a4e07f72
JB
517 history: expect.any(CircularArray)
518 },
519 waitTime: {
a4e07f72
JB
520 history: expect.any(CircularArray)
521 },
5df69fab
JB
522 elu: {
523 idle: {
5df69fab
JB
524 history: expect.any(CircularArray)
525 },
526 active: {
5df69fab 527 history: expect.any(CircularArray)
71514351 528 }
5df69fab 529 }
e211bc18 530 })
465b2940
JB
531 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
532 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
76407b8e
JB
533 max * maxMultiplier
534 )
168c526f 535 }
168c526f
JB
536 // We need to clean up the resources after our test
537 await pool.destroy()
538 })
539
6c6afb84
JB
540 it('Verify LEAST_BUSY strategy default policy', async () => {
541 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
542 let pool = new FixedThreadPool(
543 max,
544 './tests/worker-files/thread/testWorker.js',
545 { workerChoiceStrategy }
546 )
547 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
548 dynamicWorkerUsage: false,
549 dynamicWorkerReady: true
6c6afb84
JB
550 })
551 await pool.destroy()
552 pool = new DynamicThreadPool(
553 min,
554 max,
555 './tests/worker-files/thread/testWorker.js',
556 { workerChoiceStrategy }
557 )
558 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
559 dynamicWorkerUsage: false,
560 dynamicWorkerReady: true
6c6afb84
JB
561 })
562 // We need to clean up the resources after our test
563 await pool.destroy()
564 })
565
566 it('Verify LEAST_BUSY strategy default tasks statistics requirements', async () => {
e4543b14 567 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
168c526f
JB
568 let pool = new FixedThreadPool(
569 max,
d710242d 570 './tests/worker-files/thread/testWorker.js',
594bfb84 571 { workerChoiceStrategy }
168c526f 572 )
87de9ff5
JB
573 expect(
574 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
575 ).toStrictEqual({
932fc8be
JB
576 runTime: {
577 aggregate: true,
578 average: false,
579 median: false
580 },
581 waitTime: {
582 aggregate: true,
583 average: false,
584 median: false
585 },
5df69fab
JB
586 elu: {
587 aggregate: false,
588 average: false,
589 median: false
590 }
86bf340d 591 })
168c526f
JB
592 await pool.destroy()
593 pool = new DynamicThreadPool(
594 min,
595 max,
d710242d 596 './tests/worker-files/thread/testWorker.js',
594bfb84 597 { workerChoiceStrategy }
168c526f 598 )
87de9ff5
JB
599 expect(
600 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
601 ).toStrictEqual({
932fc8be
JB
602 runTime: {
603 aggregate: true,
604 average: false,
605 median: false
606 },
607 waitTime: {
608 aggregate: true,
609 average: false,
610 median: false
611 },
5df69fab
JB
612 elu: {
613 aggregate: false,
614 average: false,
615 median: false
616 }
86bf340d 617 })
168c526f
JB
618 // We need to clean up the resources after our test
619 await pool.destroy()
620 })
621
e4543b14 622 it('Verify LEAST_BUSY strategy can be run in a fixed pool', async () => {
168c526f
JB
623 const pool = new FixedThreadPool(
624 max,
625 './tests/worker-files/thread/testWorker.js',
e4543b14 626 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
168c526f 627 )
e4543b14 628 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
ee9f5295 629 const promises = new Set()
a20f0ba5
JB
630 const maxMultiplier = 2
631 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 632 promises.add(pool.execute())
e211bc18
JB
633 }
634 await Promise.all(promises)
635 for (const workerNode of pool.workerNodes) {
19dbc45b 636 expect(workerNode.usage).toMatchObject({
a4e07f72
JB
637 tasks: {
638 executed: expect.any(Number),
639 executing: 0,
640 queued: 0,
df593701 641 maxQueued: 0,
a4e07f72
JB
642 failed: 0
643 },
644 runTime: {
a4e07f72
JB
645 history: expect.any(CircularArray)
646 },
647 waitTime: {
a4e07f72
JB
648 history: expect.any(CircularArray)
649 },
5df69fab
JB
650 elu: {
651 idle: {
5df69fab
JB
652 history: expect.any(CircularArray)
653 },
654 active: {
5df69fab 655 history: expect.any(CircularArray)
71514351 656 }
5df69fab 657 }
e211bc18 658 })
465b2940
JB
659 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
660 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
a4e07f72
JB
661 max * maxMultiplier
662 )
19dbc45b
JB
663 if (workerNode.usage.runTime.aggregate == null) {
664 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
665 } else {
666 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
667 }
668 if (workerNode.usage.waitTime.aggregate == null) {
669 expect(workerNode.usage.waitTime.aggregate).toBeUndefined()
670 } else {
671 expect(workerNode.usage.waitTime.aggregate).toBeGreaterThan(0)
672 }
168c526f 673 }
168c526f
JB
674 // We need to clean up the resources after our test
675 await pool.destroy()
676 })
677
e4543b14 678 it('Verify LEAST_BUSY strategy can be run in a dynamic pool', async () => {
168c526f
JB
679 const pool = new DynamicThreadPool(
680 min,
681 max,
682 './tests/worker-files/thread/testWorker.js',
e4543b14 683 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
168c526f 684 )
e4543b14 685 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
ee9f5295 686 const promises = new Set()
a20f0ba5
JB
687 const maxMultiplier = 2
688 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 689 promises.add(pool.execute())
e211bc18
JB
690 }
691 await Promise.all(promises)
692 for (const workerNode of pool.workerNodes) {
19dbc45b 693 expect(workerNode.usage).toMatchObject({
a4e07f72
JB
694 tasks: {
695 executed: expect.any(Number),
696 executing: 0,
697 queued: 0,
df593701 698 maxQueued: 0,
a4e07f72
JB
699 failed: 0
700 },
701 runTime: {
a4e07f72
JB
702 history: expect.any(CircularArray)
703 },
704 waitTime: {
a4e07f72
JB
705 history: expect.any(CircularArray)
706 },
5df69fab
JB
707 elu: {
708 idle: {
5df69fab
JB
709 history: expect.any(CircularArray)
710 },
711 active: {
5df69fab 712 history: expect.any(CircularArray)
71514351 713 }
5df69fab 714 }
e211bc18 715 })
465b2940
JB
716 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
717 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
a4e07f72
JB
718 max * maxMultiplier
719 )
19dbc45b
JB
720 if (workerNode.usage.runTime.aggregate == null) {
721 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
722 } else {
723 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
724 }
725 if (workerNode.usage.waitTime.aggregate == null) {
726 expect(workerNode.usage.waitTime.aggregate).toBeUndefined()
727 } else {
728 expect(workerNode.usage.waitTime.aggregate).toBeGreaterThan(0)
729 }
ff5e76e1 730 }
ff5e76e1
JB
731 // We need to clean up the resources after our test
732 await pool.destroy()
733 })
734
6c6afb84
JB
735 it('Verify LEAST_ELU strategy default policy', async () => {
736 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
737 let pool = new FixedThreadPool(
738 max,
739 './tests/worker-files/thread/testWorker.js',
740 { workerChoiceStrategy }
741 )
742 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
743 dynamicWorkerUsage: false,
744 dynamicWorkerReady: true
6c6afb84
JB
745 })
746 await pool.destroy()
747 pool = new DynamicThreadPool(
748 min,
749 max,
750 './tests/worker-files/thread/testWorker.js',
751 { workerChoiceStrategy }
752 )
753 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
754 dynamicWorkerUsage: false,
755 dynamicWorkerReady: true
6c6afb84
JB
756 })
757 // We need to clean up the resources after our test
758 await pool.destroy()
759 })
760
761 it('Verify LEAST_ELU strategy default tasks statistics requirements', async () => {
a7bbf44a
JB
762 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
763 let pool = new FixedThreadPool(
764 max,
765 './tests/worker-files/thread/testWorker.js',
766 { workerChoiceStrategy }
767 )
05302647
JB
768 expect(
769 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
770 ).toStrictEqual({
e460940e
JB
771 runTime: {
772 aggregate: false,
773 average: false,
774 median: false
775 },
776 waitTime: {
777 aggregate: false,
778 average: false,
779 median: false
780 },
5df69fab
JB
781 elu: {
782 aggregate: true,
783 average: false,
784 median: false
785 }
a7bbf44a
JB
786 })
787 await pool.destroy()
788 pool = new DynamicThreadPool(
789 min,
790 max,
791 './tests/worker-files/thread/testWorker.js',
792 { workerChoiceStrategy }
793 )
05302647
JB
794 expect(
795 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
796 ).toStrictEqual({
e460940e
JB
797 runTime: {
798 aggregate: false,
799 average: false,
800 median: false
801 },
802 waitTime: {
803 aggregate: false,
804 average: false,
805 median: false
806 },
5df69fab
JB
807 elu: {
808 aggregate: true,
809 average: false,
810 median: false
811 }
a7bbf44a
JB
812 })
813 // We need to clean up the resources after our test
814 await pool.destroy()
815 })
816
ae9cf3c8 817 it('Verify LEAST_ELU strategy can be run in a fixed pool', async () => {
c5ad42cd
JB
818 const pool = new FixedThreadPool(
819 max,
820 './tests/worker-files/thread/testWorker.js',
821 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_ELU }
822 )
823 // TODO: Create a better test to cover `LeastEluWorkerChoiceStrategy#choose`
dd38581f 824 const promises = new Set()
c5ad42cd
JB
825 const maxMultiplier = 2
826 for (let i = 0; i < max * maxMultiplier; i++) {
dd38581f 827 promises.add(pool.execute())
c5ad42cd 828 }
dd38581f 829 await Promise.all(promises)
c5ad42cd 830 for (const workerNode of pool.workerNodes) {
71514351 831 expect(workerNode.usage).toMatchObject({
c5ad42cd
JB
832 tasks: {
833 executed: expect.any(Number),
834 executing: 0,
835 queued: 0,
df593701 836 maxQueued: 0,
c5ad42cd
JB
837 failed: 0
838 },
839 runTime: {
c5ad42cd 840 history: expect.any(CircularArray)
a1347286
JB
841 },
842 waitTime: {
a1347286 843 history: expect.any(CircularArray)
5df69fab
JB
844 },
845 elu: {
71514351 846 idle: expect.objectContaining({
5df69fab 847 history: expect.any(CircularArray)
71514351
JB
848 }),
849 active: expect.objectContaining({
5df69fab 850 history: expect.any(CircularArray)
71514351 851 })
a1347286 852 }
5df69fab 853 })
465b2940
JB
854 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
855 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
a1347286
JB
856 max * maxMultiplier
857 )
71514351
JB
858 if (workerNode.usage.elu.utilization == null) {
859 expect(workerNode.usage.elu.utilization).toBeUndefined()
860 } else {
861 expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
862 expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
863 }
a1347286
JB
864 }
865 // We need to clean up the resources after our test
866 await pool.destroy()
867 })
868
869 it('Verify LEAST_ELU strategy can be run in a dynamic pool', async () => {
870 const pool = new DynamicThreadPool(
871 min,
872 max,
873 './tests/worker-files/thread/testWorker.js',
874 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_ELU }
875 )
876 // TODO: Create a better test to cover `LeastEluWorkerChoiceStrategy#choose`
dd38581f 877 const promises = new Set()
a1347286
JB
878 const maxMultiplier = 2
879 for (let i = 0; i < max * maxMultiplier; i++) {
dd38581f 880 promises.add(pool.execute())
a1347286 881 }
dd38581f 882 await Promise.all(promises)
a1347286 883 for (const workerNode of pool.workerNodes) {
71514351 884 expect(workerNode.usage).toMatchObject({
a1347286
JB
885 tasks: {
886 executed: expect.any(Number),
887 executing: 0,
888 queued: 0,
df593701 889 maxQueued: 0,
a1347286
JB
890 failed: 0
891 },
892 runTime: {
a1347286 893 history: expect.any(CircularArray)
c5ad42cd
JB
894 },
895 waitTime: {
c5ad42cd 896 history: expect.any(CircularArray)
5df69fab
JB
897 },
898 elu: {
71514351 899 idle: expect.objectContaining({
5df69fab 900 history: expect.any(CircularArray)
71514351
JB
901 }),
902 active: expect.objectContaining({
5df69fab 903 history: expect.any(CircularArray)
71514351 904 })
c5ad42cd 905 }
5df69fab 906 })
465b2940
JB
907 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
908 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
c5ad42cd
JB
909 max * maxMultiplier
910 )
71514351
JB
911 if (workerNode.usage.elu.utilization == null) {
912 expect(workerNode.usage.elu.utilization).toBeUndefined()
913 } else {
914 expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
915 expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
916 }
c5ad42cd
JB
917 }
918 // We need to clean up the resources after our test
919 await pool.destroy()
920 })
921
6c6afb84
JB
922 it('Verify FAIR_SHARE strategy default policy', async () => {
923 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
924 let pool = new FixedThreadPool(
925 max,
926 './tests/worker-files/thread/testWorker.js',
927 { workerChoiceStrategy }
928 )
929 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
930 dynamicWorkerUsage: false,
931 dynamicWorkerReady: true
6c6afb84
JB
932 })
933 await pool.destroy()
934 pool = new DynamicThreadPool(
935 min,
936 max,
937 './tests/worker-files/thread/testWorker.js',
938 { workerChoiceStrategy }
939 )
940 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
941 dynamicWorkerUsage: false,
942 dynamicWorkerReady: true
6c6afb84
JB
943 })
944 // We need to clean up the resources after our test
945 await pool.destroy()
946 })
947
948 it('Verify FAIR_SHARE strategy default tasks statistics requirements', async () => {
594bfb84 949 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
10fcfaf4
JB
950 let pool = new FixedThreadPool(
951 max,
d710242d 952 './tests/worker-files/thread/testWorker.js',
594bfb84 953 { workerChoiceStrategy }
10fcfaf4 954 )
87de9ff5
JB
955 expect(
956 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
957 ).toStrictEqual({
932fc8be
JB
958 runTime: {
959 aggregate: true,
960 average: true,
961 median: false
962 },
963 waitTime: {
964 aggregate: false,
965 average: false,
966 median: false
967 },
5df69fab 968 elu: {
9adcefab
JB
969 aggregate: true,
970 average: true,
5df69fab
JB
971 median: false
972 }
86bf340d 973 })
fd7ebd49 974 await pool.destroy()
10fcfaf4
JB
975 pool = new DynamicThreadPool(
976 min,
977 max,
d710242d 978 './tests/worker-files/thread/testWorker.js',
594bfb84 979 { workerChoiceStrategy }
10fcfaf4 980 )
87de9ff5
JB
981 expect(
982 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
983 ).toStrictEqual({
932fc8be
JB
984 runTime: {
985 aggregate: true,
986 average: true,
987 median: false
988 },
989 waitTime: {
990 aggregate: false,
991 average: false,
992 median: false
993 },
5df69fab 994 elu: {
9adcefab
JB
995 aggregate: true,
996 average: true,
5df69fab
JB
997 median: false
998 }
86bf340d 999 })
10fcfaf4
JB
1000 // We need to clean up the resources after our test
1001 await pool.destroy()
1002 })
1003
23ff945a 1004 it('Verify FAIR_SHARE strategy can be run in a fixed pool', async () => {
23ff945a
JB
1005 const pool = new FixedThreadPool(
1006 max,
1007 './tests/worker-files/thread/testWorker.js',
1008 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
1009 )
1010 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
ee9f5295 1011 const promises = new Set()
a20f0ba5
JB
1012 const maxMultiplier = 2
1013 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 1014 promises.add(pool.execute())
23ff945a 1015 }
e211bc18 1016 await Promise.all(promises)
138d29a8 1017 for (const workerNode of pool.workerNodes) {
71514351 1018 expect(workerNode.usage).toMatchObject({
a4e07f72 1019 tasks: {
d33be430 1020 executed: expect.any(Number),
a4e07f72
JB
1021 executing: 0,
1022 queued: 0,
df593701 1023 maxQueued: 0,
a4e07f72
JB
1024 failed: 0
1025 },
71514351 1026 runTime: expect.objectContaining({
a4e07f72 1027 history: expect.any(CircularArray)
71514351 1028 }),
a4e07f72 1029 waitTime: {
a4e07f72
JB
1030 history: expect.any(CircularArray)
1031 },
5df69fab 1032 elu: {
71514351 1033 idle: expect.objectContaining({
5df69fab 1034 history: expect.any(CircularArray)
71514351
JB
1035 }),
1036 active: expect.objectContaining({
5df69fab 1037 history: expect.any(CircularArray)
71514351 1038 })
5df69fab 1039 }
86bf340d 1040 })
465b2940
JB
1041 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
1042 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
d33be430
JB
1043 max * maxMultiplier
1044 )
71514351
JB
1045 if (workerNode.usage.runTime.aggregate == null) {
1046 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
1047 } else {
1048 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
1049 }
1050 if (workerNode.usage.runTime.average == null) {
1051 expect(workerNode.usage.runTime.average).toBeUndefined()
1052 } else {
1053 expect(workerNode.usage.runTime.average).toBeGreaterThan(0)
1054 }
1055 if (workerNode.usage.elu.utilization == null) {
1056 expect(workerNode.usage.elu.utilization).toBeUndefined()
1057 } else {
1058 expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
1059 expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
1060 }
138d29a8 1061 }
97a2abc3 1062 expect(
95c83464 1063 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 1064 pool.workerChoiceStrategyContext.workerChoiceStrategy
b0d6ed8f 1065 ).workersVirtualTaskEndTimestamp.length
f06e48d8 1066 ).toBe(pool.workerNodes.length)
23ff945a
JB
1067 // We need to clean up the resources after our test
1068 await pool.destroy()
1069 })
1070
1071 it('Verify FAIR_SHARE strategy can be run in a dynamic pool', async () => {
23ff945a
JB
1072 const pool = new DynamicThreadPool(
1073 min,
1074 max,
1075 './tests/worker-files/thread/testWorker.js',
1076 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
1077 )
1078 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
ee9f5295 1079 const promises = new Set()
f7070eee 1080 const maxMultiplier = 2
804a889e 1081 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 1082 promises.add(pool.execute())
23ff945a 1083 }
e211bc18 1084 await Promise.all(promises)
138d29a8 1085 for (const workerNode of pool.workerNodes) {
71514351 1086 expect(workerNode.usage).toMatchObject({
a4e07f72 1087 tasks: {
6c6afb84 1088 executed: expect.any(Number),
a4e07f72
JB
1089 executing: 0,
1090 queued: 0,
df593701 1091 maxQueued: 0,
a4e07f72
JB
1092 failed: 0
1093 },
71514351 1094 runTime: expect.objectContaining({
a4e07f72 1095 history: expect.any(CircularArray)
71514351 1096 }),
a4e07f72 1097 waitTime: {
a4e07f72
JB
1098 history: expect.any(CircularArray)
1099 },
5df69fab 1100 elu: {
71514351 1101 idle: expect.objectContaining({
5df69fab 1102 history: expect.any(CircularArray)
71514351
JB
1103 }),
1104 active: expect.objectContaining({
5df69fab 1105 history: expect.any(CircularArray)
71514351 1106 })
5df69fab 1107 }
86bf340d 1108 })
465b2940
JB
1109 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
1110 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
6c6afb84
JB
1111 max * maxMultiplier
1112 )
71514351
JB
1113 if (workerNode.usage.runTime.aggregate == null) {
1114 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
1115 } else {
1116 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
1117 }
1118 if (workerNode.usage.runTime.average == null) {
1119 expect(workerNode.usage.runTime.average).toBeUndefined()
1120 } else {
1121 expect(workerNode.usage.runTime.average).toBeGreaterThan(0)
1122 }
1123 if (workerNode.usage.elu.utilization == null) {
1124 expect(workerNode.usage.elu.utilization).toBeUndefined()
1125 } else {
1126 expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
1127 expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
1128 }
138d29a8 1129 }
2b4fddb8
JB
1130 expect(
1131 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1132 pool.workerChoiceStrategyContext.workerChoiceStrategy
b0d6ed8f 1133 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 1134 ).toBe(pool.workerNodes.length)
23ff945a
JB
1135 // We need to clean up the resources after our test
1136 await pool.destroy()
1137 })
1138
9e775f96 1139 it('Verify FAIR_SHARE strategy can be run in a dynamic pool with median runtime statistic', async () => {
010d7020
JB
1140 const pool = new DynamicThreadPool(
1141 min,
1142 max,
1143 './tests/worker-files/thread/testWorker.js',
1144 {
1145 workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE,
1146 workerChoiceStrategyOptions: {
932fc8be 1147 runTime: { median: true }
010d7020
JB
1148 }
1149 }
1150 )
1151 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
ee9f5295 1152 const promises = new Set()
010d7020
JB
1153 const maxMultiplier = 2
1154 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 1155 promises.add(pool.execute())
010d7020 1156 }
e211bc18 1157 await Promise.all(promises)
010d7020 1158 for (const workerNode of pool.workerNodes) {
71514351 1159 expect(workerNode.usage).toMatchObject({
a4e07f72 1160 tasks: {
6c6afb84 1161 executed: expect.any(Number),
a4e07f72
JB
1162 executing: 0,
1163 queued: 0,
df593701 1164 maxQueued: 0,
a4e07f72
JB
1165 failed: 0
1166 },
71514351 1167 runTime: expect.objectContaining({
a4e07f72 1168 history: expect.any(CircularArray)
71514351 1169 }),
a4e07f72 1170 waitTime: {
a4e07f72
JB
1171 history: expect.any(CircularArray)
1172 },
5df69fab 1173 elu: {
71514351 1174 idle: expect.objectContaining({
5df69fab 1175 history: expect.any(CircularArray)
71514351
JB
1176 }),
1177 active: expect.objectContaining({
5df69fab 1178 history: expect.any(CircularArray)
71514351 1179 })
5df69fab 1180 }
86bf340d 1181 })
465b2940
JB
1182 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
1183 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
6c6afb84
JB
1184 max * maxMultiplier
1185 )
71514351
JB
1186 if (workerNode.usage.runTime.aggregate == null) {
1187 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
1188 } else {
1189 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
1190 }
1191 if (workerNode.usage.runTime.median == null) {
1192 expect(workerNode.usage.runTime.median).toBeUndefined()
1193 } else {
1194 expect(workerNode.usage.runTime.median).toBeGreaterThan(0)
1195 }
1196 if (workerNode.usage.elu.utilization == null) {
1197 expect(workerNode.usage.elu.utilization).toBeUndefined()
1198 } else {
1199 expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
1200 expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
1201 }
010d7020 1202 }
2b4fddb8
JB
1203 expect(
1204 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1205 pool.workerChoiceStrategyContext.workerChoiceStrategy
b0d6ed8f 1206 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 1207 ).toBe(pool.workerNodes.length)
010d7020
JB
1208 // We need to clean up the resources after our test
1209 await pool.destroy()
1210 })
1211
a6f7f1b4 1212 it('Verify FAIR_SHARE strategy internals are resets after setting it', async () => {
594bfb84 1213 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
f0829c53 1214 let pool = new FixedThreadPool(
caeb9817
JB
1215 max,
1216 './tests/worker-files/thread/testWorker.js'
1217 )
1218 expect(
95c83464 1219 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1220 workerChoiceStrategy
b0d6ed8f 1221 ).workersVirtualTaskEndTimestamp
08f3f44c
JB
1222 ).toBeInstanceOf(Array)
1223 expect(
1224 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1225 workerChoiceStrategy
b0d6ed8f 1226 ).workersVirtualTaskEndTimestamp.length
08f3f44c 1227 ).toBe(0)
2b4fddb8
JB
1228 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1229 workerChoiceStrategy
b0d6ed8f 1230 ).workersVirtualTaskEndTimestamp[0] = performance.now()
2b4fddb8
JB
1231 expect(
1232 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1233 workerChoiceStrategy
b0d6ed8f 1234 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 1235 ).toBe(1)
594bfb84 1236 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
08f3f44c
JB
1237 expect(
1238 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1239 workerChoiceStrategy
b0d6ed8f 1240 ).workersVirtualTaskEndTimestamp
08f3f44c 1241 ).toBeInstanceOf(Array)
08f3f44c
JB
1242 expect(
1243 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1244 workerChoiceStrategy
b0d6ed8f 1245 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 1246 ).toBe(0)
f0829c53
JB
1247 await pool.destroy()
1248 pool = new DynamicThreadPool(
1249 min,
1250 max,
1251 './tests/worker-files/thread/testWorker.js'
1252 )
1253 expect(
95c83464 1254 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1255 workerChoiceStrategy
b0d6ed8f 1256 ).workersVirtualTaskEndTimestamp
08f3f44c 1257 ).toBeInstanceOf(Array)
2b4fddb8
JB
1258 expect(
1259 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1260 workerChoiceStrategy
b0d6ed8f 1261 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 1262 ).toBe(0)
08f3f44c
JB
1263 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1264 workerChoiceStrategy
b0d6ed8f 1265 ).workersVirtualTaskEndTimestamp[0] = performance.now()
08f3f44c
JB
1266 expect(
1267 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1268 workerChoiceStrategy
b0d6ed8f 1269 ).workersVirtualTaskEndTimestamp.length
08f3f44c 1270 ).toBe(1)
594bfb84 1271 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
08f3f44c
JB
1272 expect(
1273 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1274 workerChoiceStrategy
b0d6ed8f 1275 ).workersVirtualTaskEndTimestamp
08f3f44c
JB
1276 ).toBeInstanceOf(Array)
1277 expect(
1278 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1279 workerChoiceStrategy
b0d6ed8f 1280 ).workersVirtualTaskEndTimestamp.length
08f3f44c 1281 ).toBe(0)
caeb9817
JB
1282 // We need to clean up the resources after our test
1283 await pool.destroy()
1284 })
1285
6c6afb84
JB
1286 it('Verify WEIGHTED_ROUND_ROBIN strategy default policy', async () => {
1287 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
1288 let pool = new FixedThreadPool(
1289 max,
1290 './tests/worker-files/thread/testWorker.js',
1291 { workerChoiceStrategy }
1292 )
1293 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
1294 dynamicWorkerUsage: false,
1295 dynamicWorkerReady: true
6c6afb84
JB
1296 })
1297 await pool.destroy()
1298 pool = new DynamicThreadPool(
1299 min,
1300 max,
1301 './tests/worker-files/thread/testWorker.js',
1302 { workerChoiceStrategy }
1303 )
1304 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
1305 dynamicWorkerUsage: false,
1306 dynamicWorkerReady: true
6c6afb84
JB
1307 })
1308 // We need to clean up the resources after our test
1309 await pool.destroy()
1310 })
1311
1312 it('Verify WEIGHTED_ROUND_ROBIN strategy default tasks statistics requirements', async () => {
594bfb84 1313 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
10fcfaf4
JB
1314 let pool = new FixedThreadPool(
1315 max,
d710242d 1316 './tests/worker-files/thread/testWorker.js',
594bfb84 1317 { workerChoiceStrategy }
10fcfaf4 1318 )
87de9ff5
JB
1319 expect(
1320 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1321 ).toStrictEqual({
932fc8be
JB
1322 runTime: {
1323 aggregate: true,
1324 average: true,
1325 median: false
1326 },
1327 waitTime: {
1328 aggregate: false,
1329 average: false,
1330 median: false
1331 },
5df69fab
JB
1332 elu: {
1333 aggregate: false,
1334 average: false,
1335 median: false
1336 }
86bf340d 1337 })
fd7ebd49 1338 await pool.destroy()
10fcfaf4
JB
1339 pool = new DynamicThreadPool(
1340 min,
1341 max,
d710242d 1342 './tests/worker-files/thread/testWorker.js',
594bfb84 1343 { workerChoiceStrategy }
10fcfaf4 1344 )
87de9ff5
JB
1345 expect(
1346 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1347 ).toStrictEqual({
932fc8be
JB
1348 runTime: {
1349 aggregate: true,
1350 average: true,
1351 median: false
1352 },
1353 waitTime: {
1354 aggregate: false,
1355 average: false,
1356 median: false
1357 },
5df69fab
JB
1358 elu: {
1359 aggregate: false,
1360 average: false,
1361 median: false
1362 }
86bf340d 1363 })
10fcfaf4
JB
1364 // We need to clean up the resources after our test
1365 await pool.destroy()
1366 })
1367
b3432a63 1368 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
b3432a63
JB
1369 const pool = new FixedThreadPool(
1370 max,
1371 './tests/worker-files/thread/testWorker.js',
1372 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
1373 )
1374 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
ee9f5295 1375 const promises = new Set()
a20f0ba5
JB
1376 const maxMultiplier = 2
1377 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 1378 promises.add(pool.execute())
b3432a63 1379 }
e211bc18 1380 await Promise.all(promises)
138d29a8 1381 for (const workerNode of pool.workerNodes) {
465b2940 1382 expect(workerNode.usage).toStrictEqual({
a4e07f72
JB
1383 tasks: {
1384 executed: expect.any(Number),
1385 executing: 0,
1386 queued: 0,
df593701 1387 maxQueued: 0,
a4e07f72
JB
1388 failed: 0
1389 },
71514351 1390 runTime: expect.objectContaining({
a4e07f72 1391 history: expect.any(CircularArray)
71514351 1392 }),
a4e07f72 1393 waitTime: {
a4e07f72
JB
1394 history: expect.any(CircularArray)
1395 },
5df69fab
JB
1396 elu: {
1397 idle: {
5df69fab
JB
1398 history: expect.any(CircularArray)
1399 },
1400 active: {
5df69fab 1401 history: expect.any(CircularArray)
71514351 1402 }
5df69fab 1403 }
86bf340d 1404 })
465b2940
JB
1405 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
1406 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
a4e07f72
JB
1407 max * maxMultiplier
1408 )
71514351
JB
1409 if (workerNode.usage.runTime.aggregate == null) {
1410 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
1411 } else {
1412 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
1413 }
1414 if (workerNode.usage.runTime.average == null) {
1415 expect(workerNode.usage.runTime.average).toBeUndefined()
1416 } else {
1417 expect(workerNode.usage.runTime.average).toBeGreaterThan(0)
1418 }
138d29a8 1419 }
97a2abc3 1420 expect(
95c83464 1421 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 1422 pool.workerChoiceStrategyContext.workerChoiceStrategy
08f3f44c
JB
1423 ).defaultWorkerWeight
1424 ).toBeGreaterThan(0)
1425 expect(
1426 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1427 pool.workerChoiceStrategyContext.workerChoiceStrategy
1428 ).workerVirtualTaskRunTime
1429 ).toBeGreaterThanOrEqual(0)
b3432a63
JB
1430 // We need to clean up the resources after our test
1431 await pool.destroy()
1432 })
1433
1434 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
b3432a63
JB
1435 const pool = new DynamicThreadPool(
1436 min,
1437 max,
1438 './tests/worker-files/thread/testWorker.js',
1439 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
1440 )
1441 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
ee9f5295 1442 const promises = new Set()
138d29a8 1443 const maxMultiplier = 2
5502c07c 1444 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 1445 promises.add(pool.execute())
b3432a63 1446 }
e211bc18 1447 await Promise.all(promises)
138d29a8 1448 for (const workerNode of pool.workerNodes) {
465b2940 1449 expect(workerNode.usage).toStrictEqual({
a4e07f72
JB
1450 tasks: {
1451 executed: expect.any(Number),
1452 executing: 0,
1453 queued: 0,
df593701 1454 maxQueued: 0,
a4e07f72
JB
1455 failed: 0
1456 },
b1aae695 1457 runTime: expect.objectContaining({
a4e07f72 1458 history: expect.any(CircularArray)
b1aae695 1459 }),
a4e07f72 1460 waitTime: {
a4e07f72
JB
1461 history: expect.any(CircularArray)
1462 },
5df69fab
JB
1463 elu: {
1464 idle: {
5df69fab
JB
1465 history: expect.any(CircularArray)
1466 },
1467 active: {
5df69fab 1468 history: expect.any(CircularArray)
71514351 1469 }
5df69fab 1470 }
86bf340d 1471 })
465b2940
JB
1472 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
1473 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
a4e07f72
JB
1474 max * maxMultiplier
1475 )
b1aae695
JB
1476 if (workerNode.usage.runTime.aggregate == null) {
1477 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
1478 } else {
1479 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
1480 }
1481 if (workerNode.usage.runTime.average == null) {
1482 expect(workerNode.usage.runTime.average).toBeUndefined()
1483 } else {
1484 expect(workerNode.usage.runTime.average).toBeGreaterThan(0)
1485 }
138d29a8 1486 }
2b4fddb8
JB
1487 expect(
1488 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1489 pool.workerChoiceStrategyContext.workerChoiceStrategy
1490 ).defaultWorkerWeight
1491 ).toBeGreaterThan(0)
1492 expect(
1493 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1494 pool.workerChoiceStrategyContext.workerChoiceStrategy
1495 ).workerVirtualTaskRunTime
1496 ).toBeGreaterThanOrEqual(0)
b3432a63
JB
1497 // We need to clean up the resources after our test
1498 await pool.destroy()
1499 })
1500
9e775f96 1501 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool with median runtime statistic', async () => {
010d7020
JB
1502 const pool = new DynamicThreadPool(
1503 min,
1504 max,
1505 './tests/worker-files/thread/testWorker.js',
1506 {
1507 workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN,
1508 workerChoiceStrategyOptions: {
932fc8be 1509 runTime: { median: true }
010d7020
JB
1510 }
1511 }
1512 )
1513 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
ee9f5295 1514 const promises = new Set()
010d7020
JB
1515 const maxMultiplier = 2
1516 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 1517 promises.add(pool.execute())
010d7020 1518 }
e211bc18 1519 await Promise.all(promises)
010d7020 1520 for (const workerNode of pool.workerNodes) {
465b2940 1521 expect(workerNode.usage).toStrictEqual({
a4e07f72
JB
1522 tasks: {
1523 executed: expect.any(Number),
1524 executing: 0,
1525 queued: 0,
df593701 1526 maxQueued: 0,
a4e07f72
JB
1527 failed: 0
1528 },
b1aae695 1529 runTime: expect.objectContaining({
a4e07f72 1530 history: expect.any(CircularArray)
b1aae695 1531 }),
a4e07f72 1532 waitTime: {
a4e07f72
JB
1533 history: expect.any(CircularArray)
1534 },
5df69fab
JB
1535 elu: {
1536 idle: {
5df69fab
JB
1537 history: expect.any(CircularArray)
1538 },
1539 active: {
5df69fab 1540 history: expect.any(CircularArray)
71514351 1541 }
5df69fab 1542 }
86bf340d 1543 })
465b2940
JB
1544 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
1545 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
a4e07f72
JB
1546 max * maxMultiplier
1547 )
b1aae695
JB
1548 if (workerNode.usage.runTime.aggregate == null) {
1549 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
1550 } else {
1551 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
1552 }
1553 if (workerNode.usage.runTime.median == null) {
1554 expect(workerNode.usage.runTime.median).toBeUndefined()
1555 } else {
1556 expect(workerNode.usage.runTime.median).toBeGreaterThan(0)
1557 }
010d7020 1558 }
08f3f44c
JB
1559 expect(
1560 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1561 pool.workerChoiceStrategyContext.workerChoiceStrategy
1562 ).defaultWorkerWeight
1563 ).toBeGreaterThan(0)
1564 expect(
1565 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1566 pool.workerChoiceStrategyContext.workerChoiceStrategy
1567 ).workerVirtualTaskRunTime
1568 ).toBeGreaterThanOrEqual(0)
010d7020
JB
1569 // We need to clean up the resources after our test
1570 await pool.destroy()
1571 })
1572
a6f7f1b4 1573 it('Verify WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
594bfb84 1574 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
f0829c53 1575 let pool = new FixedThreadPool(
caeb9817
JB
1576 max,
1577 './tests/worker-files/thread/testWorker.js'
1578 )
38f6e859 1579 expect(
95c83464 1580 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1581 workerChoiceStrategy
9b106837 1582 ).nextWorkerNodeKey
b529c323 1583 ).toBeDefined()
38f6e859 1584 expect(
95c83464 1585 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1586 workerChoiceStrategy
b529c323
JB
1587 ).defaultWorkerWeight
1588 ).toBeDefined()
caeb9817 1589 expect(
95c83464 1590 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1591 workerChoiceStrategy
08f3f44c 1592 ).workerVirtualTaskRunTime
b529c323 1593 ).toBeDefined()
594bfb84 1594 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 1595 expect(
95c83464 1596 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 1597 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 1598 ).nextWorkerNodeKey
a6f7f1b4
JB
1599 ).toBe(0)
1600 expect(
95c83464 1601 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 1602 pool.workerChoiceStrategyContext.workerChoiceStrategy
95c83464 1603 ).defaultWorkerWeight
a6f7f1b4 1604 ).toBeGreaterThan(0)
08f3f44c
JB
1605 expect(
1606 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1607 workerChoiceStrategy
1608 ).workerVirtualTaskRunTime
1609 ).toBe(0)
f0829c53
JB
1610 await pool.destroy()
1611 pool = new DynamicThreadPool(
1612 min,
1613 max,
1614 './tests/worker-files/thread/testWorker.js'
1615 )
38f6e859 1616 expect(
95c83464 1617 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1618 workerChoiceStrategy
9b106837 1619 ).nextWorkerNodeKey
b529c323 1620 ).toBeDefined()
38f6e859 1621 expect(
95c83464 1622 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1623 workerChoiceStrategy
b529c323
JB
1624 ).defaultWorkerWeight
1625 ).toBeDefined()
f0829c53 1626 expect(
95c83464 1627 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1628 workerChoiceStrategy
08f3f44c 1629 ).workerVirtualTaskRunTime
b529c323 1630 ).toBeDefined()
594bfb84 1631 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 1632 expect(
95c83464 1633 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 1634 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 1635 ).nextWorkerNodeKey
a6f7f1b4
JB
1636 ).toBe(0)
1637 expect(
95c83464 1638 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 1639 pool.workerChoiceStrategyContext.workerChoiceStrategy
95c83464 1640 ).defaultWorkerWeight
a6f7f1b4 1641 ).toBeGreaterThan(0)
08f3f44c
JB
1642 expect(
1643 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1644 workerChoiceStrategy
1645 ).workerVirtualTaskRunTime
1646 ).toBe(0)
caeb9817
JB
1647 // We need to clean up the resources after our test
1648 await pool.destroy()
1649 })
1650
6c6afb84
JB
1651 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy default policy', async () => {
1652 const workerChoiceStrategy =
1653 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1654 let pool = new FixedThreadPool(
1655 max,
1656 './tests/worker-files/thread/testWorker.js',
1657 { workerChoiceStrategy }
1658 )
1659 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
1660 dynamicWorkerUsage: false,
1661 dynamicWorkerReady: true
6c6afb84
JB
1662 })
1663 await pool.destroy()
1664 pool = new DynamicThreadPool(
1665 min,
1666 max,
1667 './tests/worker-files/thread/testWorker.js',
1668 { workerChoiceStrategy }
1669 )
1670 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
1671 dynamicWorkerUsage: false,
1672 dynamicWorkerReady: true
6c6afb84
JB
1673 })
1674 // We need to clean up the resources after our test
1675 await pool.destroy()
1676 })
1677
1678 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy default tasks statistics requirements', async () => {
e52fb978
JB
1679 const workerChoiceStrategy =
1680 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1681 let pool = new FixedThreadPool(
1682 max,
1683 './tests/worker-files/thread/testWorker.js',
1684 { workerChoiceStrategy }
1685 )
87de9ff5
JB
1686 expect(
1687 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1688 ).toStrictEqual({
932fc8be
JB
1689 runTime: {
1690 aggregate: false,
1691 average: false,
1692 median: false
1693 },
1694 waitTime: {
1695 aggregate: false,
1696 average: false,
1697 median: false
1698 },
5df69fab
JB
1699 elu: {
1700 aggregate: false,
1701 average: false,
1702 median: false
1703 }
e52fb978
JB
1704 })
1705 await pool.destroy()
1706 pool = new DynamicThreadPool(
1707 min,
1708 max,
1709 './tests/worker-files/thread/testWorker.js',
1710 { workerChoiceStrategy }
1711 )
87de9ff5
JB
1712 expect(
1713 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1714 ).toStrictEqual({
932fc8be
JB
1715 runTime: {
1716 aggregate: false,
1717 average: false,
1718 median: false
1719 },
1720 waitTime: {
1721 aggregate: false,
1722 average: false,
1723 median: false
1724 },
5df69fab
JB
1725 elu: {
1726 aggregate: false,
1727 average: false,
1728 median: false
1729 }
e52fb978
JB
1730 })
1731 // We need to clean up the resources after our test
1732 await pool.destroy()
1733 })
1734
e62e7646
JB
1735 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
1736 const pool = new FixedThreadPool(
1737 max,
1738 './tests/worker-files/thread/testWorker.js',
1739 {
1740 workerChoiceStrategy:
1741 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1742 }
1743 )
1744 // TODO: Create a better test to cover `InterleavedWeightedRoundRobinWorkerChoiceStrategy#choose`
1745 const promises = new Set()
1746 const maxMultiplier = 2
1747 for (let i = 0; i < max * maxMultiplier; i++) {
1748 promises.add(pool.execute())
1749 }
1750 await Promise.all(promises)
1751 for (const workerNode of pool.workerNodes) {
465b2940 1752 expect(workerNode.usage).toStrictEqual({
a4e07f72
JB
1753 tasks: {
1754 executed: maxMultiplier,
1755 executing: 0,
1756 queued: 0,
df593701 1757 maxQueued: 0,
a4e07f72
JB
1758 failed: 0
1759 },
1760 runTime: {
a4e07f72
JB
1761 history: expect.any(CircularArray)
1762 },
1763 waitTime: {
a4e07f72
JB
1764 history: expect.any(CircularArray)
1765 },
5df69fab
JB
1766 elu: {
1767 idle: {
5df69fab
JB
1768 history: expect.any(CircularArray)
1769 },
1770 active: {
5df69fab 1771 history: expect.any(CircularArray)
71514351 1772 }
5df69fab 1773 }
e62e7646
JB
1774 })
1775 }
1776 expect(
1777 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1778 pool.workerChoiceStrategyContext.workerChoiceStrategy
1779 ).defaultWorkerWeight
1780 ).toBeGreaterThan(0)
1781 expect(
1782 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1783 pool.workerChoiceStrategyContext.workerChoiceStrategy
d33be430 1784 ).roundId
e62e7646
JB
1785 ).toBe(0)
1786 expect(
1787 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1788 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 1789 ).nextWorkerNodeKey
e62e7646
JB
1790 ).toBe(0)
1791 expect(
1792 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1793 pool.workerChoiceStrategyContext.workerChoiceStrategy
1794 ).roundWeights
1795 ).toStrictEqual([
1796 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1797 pool.workerChoiceStrategyContext.workerChoiceStrategy
1798 ).defaultWorkerWeight
1799 ])
1800 // We need to clean up the resources after our test
1801 await pool.destroy()
1802 })
1803
1804 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
1805 const pool = new DynamicThreadPool(
1806 min,
1807 max,
1808 './tests/worker-files/thread/testWorker.js',
1809 {
1810 workerChoiceStrategy:
1811 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1812 }
1813 )
1814 // TODO: Create a better test to cover `InterleavedWeightedRoundRobinWorkerChoiceStrategy#choose`
1815 const promises = new Set()
1816 const maxMultiplier = 2
1817 for (let i = 0; i < max * maxMultiplier; i++) {
1818 promises.add(pool.execute())
1819 }
1820 await Promise.all(promises)
1821 for (const workerNode of pool.workerNodes) {
465b2940 1822 expect(workerNode.usage).toStrictEqual({
a4e07f72 1823 tasks: {
b1aae695 1824 executed: expect.any(Number),
a4e07f72
JB
1825 executing: 0,
1826 queued: 0,
df593701 1827 maxQueued: 0,
a4e07f72
JB
1828 failed: 0
1829 },
1830 runTime: {
a4e07f72
JB
1831 history: expect.any(CircularArray)
1832 },
1833 waitTime: {
a4e07f72
JB
1834 history: expect.any(CircularArray)
1835 },
5df69fab
JB
1836 elu: {
1837 idle: {
5df69fab
JB
1838 history: expect.any(CircularArray)
1839 },
1840 active: {
5df69fab 1841 history: expect.any(CircularArray)
71514351 1842 }
5df69fab 1843 }
e62e7646 1844 })
e43b8c2d
JB
1845 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
1846 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
1847 max * maxMultiplier
1848 )
e62e7646
JB
1849 }
1850 expect(
1851 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1852 pool.workerChoiceStrategyContext.workerChoiceStrategy
1853 ).defaultWorkerWeight
1854 ).toBeGreaterThan(0)
1855 expect(
1856 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1857 pool.workerChoiceStrategyContext.workerChoiceStrategy
d33be430 1858 ).roundId
e62e7646
JB
1859 ).toBe(0)
1860 expect(
1861 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1862 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 1863 ).nextWorkerNodeKey
d2c73f82 1864 ).toBe(0)
e62e7646
JB
1865 expect(
1866 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1867 pool.workerChoiceStrategyContext.workerChoiceStrategy
1868 ).roundWeights
1869 ).toStrictEqual([
1870 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1871 pool.workerChoiceStrategyContext.workerChoiceStrategy
1872 ).defaultWorkerWeight
1873 ])
1874 // We need to clean up the resources after our test
1875 await pool.destroy()
1876 })
1877
8c3ec188
JB
1878 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
1879 const workerChoiceStrategy =
1880 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1881 let pool = new FixedThreadPool(
1882 max,
1883 './tests/worker-files/thread/testWorker.js'
1884 )
1885 expect(
1886 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1887 workerChoiceStrategy
d33be430 1888 ).roundId
8c3ec188
JB
1889 ).toBeDefined()
1890 expect(
1891 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1892 workerChoiceStrategy
9b106837 1893 ).nextWorkerNodeKey
8c3ec188
JB
1894 ).toBeDefined()
1895 expect(
1896 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1897 workerChoiceStrategy
1898 ).defaultWorkerWeight
1899 ).toBeDefined()
1900 expect(
1901 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1902 workerChoiceStrategy
1903 ).roundWeights
1904 ).toBeDefined()
1905 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1906 expect(
1907 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1908 workerChoiceStrategy
d33be430 1909 ).roundId
8c3ec188
JB
1910 ).toBe(0)
1911 expect(
1912 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1913 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 1914 ).nextWorkerNodeKey
8c3ec188
JB
1915 ).toBe(0)
1916 expect(
1917 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1918 pool.workerChoiceStrategyContext.workerChoiceStrategy
1919 ).defaultWorkerWeight
1920 ).toBeGreaterThan(0)
1921 expect(
1922 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1923 workerChoiceStrategy
1924 ).roundWeights
1925 ).toStrictEqual([
1926 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1927 pool.workerChoiceStrategyContext.workerChoiceStrategy
1928 ).defaultWorkerWeight
1929 ])
1930 await pool.destroy()
1931 pool = new DynamicThreadPool(
1932 min,
1933 max,
1934 './tests/worker-files/thread/testWorker.js'
1935 )
1936 expect(
1937 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1938 workerChoiceStrategy
d33be430 1939 ).roundId
8c3ec188
JB
1940 ).toBeDefined()
1941 expect(
1942 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1943 workerChoiceStrategy
9b106837 1944 ).nextWorkerNodeKey
8c3ec188
JB
1945 ).toBeDefined()
1946 expect(
1947 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1948 workerChoiceStrategy
1949 ).defaultWorkerWeight
1950 ).toBeDefined()
1951 expect(
1952 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1953 workerChoiceStrategy
1954 ).roundWeights
1955 ).toBeDefined()
1956 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
1957 expect(
1958 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1959 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 1960 ).nextWorkerNodeKey
8c3ec188
JB
1961 ).toBe(0)
1962 expect(
1963 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1964 pool.workerChoiceStrategyContext.workerChoiceStrategy
1965 ).defaultWorkerWeight
1966 ).toBeGreaterThan(0)
1967 expect(
1968 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1969 workerChoiceStrategy
1970 ).roundWeights
1971 ).toStrictEqual([
1972 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1973 pool.workerChoiceStrategyContext.workerChoiceStrategy
1974 ).defaultWorkerWeight
1975 ])
1976 // We need to clean up the resources after our test
1977 await pool.destroy()
1978 })
1979
89b09b26 1980 it('Verify unknown strategy throw error', () => {
a35560ba
S
1981 expect(
1982 () =>
1983 new DynamicThreadPool(
1984 min,
1985 max,
1986 './tests/worker-files/thread/testWorker.js',
1927ee67 1987 { workerChoiceStrategy: 'UNKNOWN_STRATEGY' }
a35560ba 1988 )
d4aeae5a 1989 ).toThrowError("Invalid worker choice strategy 'UNKNOWN_STRATEGY'")
a35560ba
S
1990 })
1991})