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