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