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