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