Merge branch 'master' into interleaved-weighted-round-robin-worker-choice-strategy
[poolifier.git] / tests / pools / selection-strategies / selection-strategies.test.js
CommitLineData
a61a0724 1const { expect } = require('expect')
a35560ba
S
2const {
3 WorkerChoiceStrategies,
4 DynamicThreadPool,
2ced693a
JB
5 FixedThreadPool,
6 FixedClusterPool
cdace0e5 7} = require('../../../lib')
86bf340d 8const { CircularArray } = require('../../../lib/circular-array')
a35560ba
S
9
10describe('Selection strategies test suite', () => {
e1ffb94f
JB
11 const min = 0
12 const max = 3
13
a35560ba
S
14 it('Verify that WorkerChoiceStrategies enumeration provides string values', () => {
15 expect(WorkerChoiceStrategies.ROUND_ROBIN).toBe('ROUND_ROBIN')
e4543b14
JB
16 expect(WorkerChoiceStrategies.LEAST_USED).toBe('LEAST_USED')
17 expect(WorkerChoiceStrategies.LEAST_BUSY).toBe('LEAST_BUSY')
23ff945a 18 expect(WorkerChoiceStrategies.FAIR_SHARE).toBe('FAIR_SHARE')
b3432a63
JB
19 expect(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN).toBe(
20 'WEIGHTED_ROUND_ROBIN'
21 )
feec6e8c
JB
22 expect(WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN).toBe(
23 'INTERLEAVED_WEIGHTED_ROUND_ROBIN'
24 )
a35560ba
S
25 })
26
e843b904 27 it('Verify ROUND_ROBIN strategy is the default at pool creation', async () => {
e843b904
JB
28 const pool = new DynamicThreadPool(
29 min,
30 max,
31 './tests/worker-files/thread/testWorker.js'
32 )
33 expect(pool.opts.workerChoiceStrategy).toBe(
34 WorkerChoiceStrategies.ROUND_ROBIN
35 )
36 // We need to clean up the resources after our test
37 await pool.destroy()
38 })
39
594bfb84
JB
40 it('Verify available strategies are taken at pool creation', async () => {
41 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
42 const pool = new FixedThreadPool(
43 max,
44 './tests/worker-files/thread/testWorker.js',
45 { workerChoiceStrategy }
46 )
47 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
48 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
49 workerChoiceStrategy
50 )
51 await pool.destroy()
52 }
d2f7b7a2
JB
53 })
54
594bfb84
JB
55 it('Verify available strategies can be set after pool creation', async () => {
56 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
57 const pool = new DynamicThreadPool(
58 min,
59 max,
ec82cfa1 60 './tests/worker-files/thread/testWorker.js'
594bfb84
JB
61 )
62 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
63 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
64 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
65 workerChoiceStrategy
66 )
67 await pool.destroy()
68 }
69 })
70
71 it('Verify available strategies default internals at pool creation', async () => {
72 const pool = new FixedThreadPool(
e843b904
JB
73 max,
74 './tests/worker-files/thread/testWorker.js'
75 )
594bfb84
JB
76 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
77 if (workerChoiceStrategy === WorkerChoiceStrategies.ROUND_ROBIN) {
78 expect(
79 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
80 workerChoiceStrategy
81 ).nextWorkerNodeId
82 ).toBe(0)
83 } else if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) {
08f3f44c
JB
84 expect(
85 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
86 workerChoiceStrategy
b0d6ed8f 87 ).workersVirtualTaskEndTimestamp
08f3f44c
JB
88 ).toBeInstanceOf(Array)
89 expect(
90 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
91 workerChoiceStrategy
b0d6ed8f 92 ).workersVirtualTaskEndTimestamp.length
08f3f44c 93 ).toBe(0)
594bfb84
JB
94 } else if (
95 workerChoiceStrategy === WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
96 ) {
97 expect(
98 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
99 workerChoiceStrategy
100 ).currentWorkerNodeId
101 ).toBe(0)
102 expect(
103 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
104 workerChoiceStrategy
105 ).defaultWorkerWeight
106 ).toBeGreaterThan(0)
08f3f44c
JB
107 expect(
108 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
109 workerChoiceStrategy
110 ).workerVirtualTaskRunTime
111 ).toBe(0)
594bfb84
JB
112 }
113 }
e843b904
JB
114 await pool.destroy()
115 })
116
10fcfaf4 117 it('Verify ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
594bfb84 118 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
10fcfaf4
JB
119 let pool = new FixedThreadPool(
120 max,
d710242d 121 './tests/worker-files/thread/testWorker.js',
594bfb84 122 { workerChoiceStrategy }
10fcfaf4 123 )
10fcfaf4 124 expect(
86bf340d
JB
125 pool.workerChoiceStrategyContext.getRequiredStatistics()
126 ).toStrictEqual({
127 runTime: false,
128 avgRunTime: false,
129 medRunTime: false,
130 waitTime: false,
131 avgWaitTime: false,
132 medWaitTime: false
133 })
fd7ebd49 134 await pool.destroy()
10fcfaf4
JB
135 pool = new DynamicThreadPool(
136 min,
137 max,
d710242d 138 './tests/worker-files/thread/testWorker.js',
594bfb84 139 { workerChoiceStrategy }
10fcfaf4 140 )
10fcfaf4 141 expect(
86bf340d
JB
142 pool.workerChoiceStrategyContext.getRequiredStatistics()
143 ).toStrictEqual({
144 runTime: false,
145 avgRunTime: false,
146 medRunTime: false,
147 waitTime: false,
148 avgWaitTime: false,
149 medWaitTime: false
150 })
10fcfaf4
JB
151 // We need to clean up the resources after our test
152 await pool.destroy()
153 })
154
bdaf31cd 155 it('Verify ROUND_ROBIN strategy can be run in a fixed pool', async () => {
bdaf31cd
JB
156 const pool = new FixedThreadPool(
157 max,
158 './tests/worker-files/thread/testWorker.js',
159 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
160 )
bdaf31cd 161 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
ee9f5295 162 const promises = new Set()
a20f0ba5
JB
163 const maxMultiplier = 2
164 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 165 promises.add(pool.execute())
e211bc18
JB
166 }
167 await Promise.all(promises)
168 for (const workerNode of pool.workerNodes) {
169 expect(workerNode.tasksUsage).toStrictEqual({
fc027381 170 run: maxMultiplier,
e211bc18
JB
171 running: 0,
172 runTime: 0,
173 runTimeHistory: expect.any(CircularArray),
174 avgRunTime: 0,
175 medRunTime: 0,
176 waitTime: 0,
177 waitTimeHistory: expect.any(CircularArray),
178 avgWaitTime: 0,
179 medWaitTime: 0,
180 error: 0
181 })
bdaf31cd 182 }
bdaf31cd
JB
183 // We need to clean up the resources after our test
184 await pool.destroy()
185 })
186
187 it('Verify ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
bdaf31cd
JB
188 const pool = new DynamicThreadPool(
189 min,
190 max,
191 './tests/worker-files/thread/testWorker.js',
192 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
193 )
bdaf31cd 194 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
ee9f5295 195 const promises = new Set()
a20f0ba5
JB
196 const maxMultiplier = 2
197 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 198 promises.add(pool.execute())
e211bc18
JB
199 }
200 await Promise.all(promises)
201 for (const workerNode of pool.workerNodes) {
202 expect(workerNode.tasksUsage).toStrictEqual({
fc027381 203 run: maxMultiplier,
e211bc18
JB
204 running: 0,
205 runTime: 0,
206 runTimeHistory: expect.any(CircularArray),
207 avgRunTime: 0,
208 medRunTime: 0,
209 waitTime: 0,
210 waitTimeHistory: expect.any(CircularArray),
211 avgWaitTime: 0,
212 medWaitTime: 0,
213 error: 0
214 })
bdaf31cd 215 }
bdaf31cd
JB
216 // We need to clean up the resources after our test
217 await pool.destroy()
218 })
219
2ced693a 220 it('Verify ROUND_ROBIN strategy runtime behavior', async () => {
594bfb84 221 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
2ced693a
JB
222 let pool = new FixedClusterPool(
223 max,
594bfb84
JB
224 './tests/worker-files/cluster/testWorker.js',
225 { workerChoiceStrategy }
2ced693a
JB
226 )
227 let results = new Set()
228 for (let i = 0; i < max; i++) {
20dcad1a 229 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.id)
2ced693a
JB
230 }
231 expect(results.size).toBe(max)
232 await pool.destroy()
594bfb84
JB
233 pool = new FixedThreadPool(
234 max,
235 './tests/worker-files/thread/testWorker.js',
236 { workerChoiceStrategy }
237 )
2ced693a
JB
238 results = new Set()
239 for (let i = 0; i < max; i++) {
20dcad1a 240 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.threadId)
2ced693a
JB
241 }
242 expect(results.size).toBe(max)
243 await pool.destroy()
244 })
245
a6f7f1b4 246 it('Verify ROUND_ROBIN strategy internals are resets after setting it', async () => {
594bfb84 247 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
a6f7f1b4
JB
248 let pool = new FixedThreadPool(
249 max,
250 './tests/worker-files/thread/testWorker.js',
251 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
252 )
38f6e859 253 expect(
95c83464 254 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 255 workerChoiceStrategy
f06e48d8 256 ).nextWorkerNodeId
b529c323 257 ).toBeDefined()
594bfb84 258 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 259 expect(
95c83464 260 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 261 pool.workerChoiceStrategyContext.workerChoiceStrategy
f06e48d8 262 ).nextWorkerNodeId
a6f7f1b4
JB
263 ).toBe(0)
264 await pool.destroy()
265 pool = new DynamicThreadPool(
266 min,
267 max,
268 './tests/worker-files/thread/testWorker.js',
269 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
270 )
38f6e859 271 expect(
95c83464 272 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 273 workerChoiceStrategy
f06e48d8 274 ).nextWorkerNodeId
b529c323 275 ).toBeDefined()
594bfb84 276 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 277 expect(
95c83464 278 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 279 pool.workerChoiceStrategyContext.workerChoiceStrategy
f06e48d8 280 ).nextWorkerNodeId
a6f7f1b4
JB
281 ).toBe(0)
282 // We need to clean up the resources after our test
283 await pool.destroy()
284 })
285
e4543b14
JB
286 it('Verify LEAST_USED strategy default tasks usage statistics requirements', async () => {
287 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
10fcfaf4
JB
288 let pool = new FixedThreadPool(
289 max,
d710242d 290 './tests/worker-files/thread/testWorker.js',
594bfb84 291 { workerChoiceStrategy }
10fcfaf4 292 )
10fcfaf4 293 expect(
86bf340d
JB
294 pool.workerChoiceStrategyContext.getRequiredStatistics()
295 ).toStrictEqual({
296 runTime: false,
297 avgRunTime: false,
298 medRunTime: false,
299 waitTime: false,
300 avgWaitTime: false,
301 medWaitTime: false
302 })
fd7ebd49 303 await pool.destroy()
10fcfaf4
JB
304 pool = new DynamicThreadPool(
305 min,
306 max,
d710242d 307 './tests/worker-files/thread/testWorker.js',
594bfb84 308 { workerChoiceStrategy }
10fcfaf4 309 )
10fcfaf4 310 expect(
86bf340d
JB
311 pool.workerChoiceStrategyContext.getRequiredStatistics()
312 ).toStrictEqual({
313 runTime: false,
314 avgRunTime: false,
315 medRunTime: false,
316 waitTime: false,
317 avgWaitTime: false,
318 medWaitTime: false
319 })
10fcfaf4
JB
320 // We need to clean up the resources after our test
321 await pool.destroy()
322 })
323
e4543b14 324 it('Verify LEAST_USED strategy can be run in a fixed pool', async () => {
b98ec2e6
JB
325 const pool = new FixedThreadPool(
326 max,
327 './tests/worker-files/thread/testWorker.js',
e4543b14 328 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
b98ec2e6 329 )
e4543b14 330 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
ee9f5295 331 const promises = new Set()
a20f0ba5
JB
332 const maxMultiplier = 2
333 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 334 promises.add(pool.execute())
e211bc18
JB
335 }
336 await Promise.all(promises)
337 for (const workerNode of pool.workerNodes) {
338 expect(workerNode.tasksUsage).toStrictEqual({
fc027381 339 run: maxMultiplier,
e211bc18
JB
340 running: 0,
341 runTime: 0,
342 runTimeHistory: expect.any(CircularArray),
343 avgRunTime: 0,
344 medRunTime: 0,
345 waitTime: 0,
346 waitTimeHistory: expect.any(CircularArray),
347 avgWaitTime: 0,
348 medWaitTime: 0,
349 error: 0
350 })
a35560ba 351 }
a35560ba
S
352 // We need to clean up the resources after our test
353 await pool.destroy()
354 })
355
e4543b14 356 it('Verify LEAST_USED strategy can be run in a dynamic pool', async () => {
ff5e76e1
JB
357 const pool = new DynamicThreadPool(
358 min,
359 max,
360 './tests/worker-files/thread/testWorker.js',
e4543b14 361 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
ff5e76e1 362 )
e4543b14 363 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
ee9f5295 364 const promises = new Set()
a20f0ba5
JB
365 const maxMultiplier = 2
366 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 367 promises.add(pool.execute())
e211bc18
JB
368 }
369 await Promise.all(promises)
370 for (const workerNode of pool.workerNodes) {
371 expect(workerNode.tasksUsage).toStrictEqual({
fc027381 372 run: maxMultiplier,
e211bc18
JB
373 running: 0,
374 runTime: 0,
375 runTimeHistory: expect.any(CircularArray),
376 avgRunTime: 0,
377 medRunTime: 0,
378 waitTime: 0,
379 waitTimeHistory: expect.any(CircularArray),
380 avgWaitTime: 0,
381 medWaitTime: 0,
382 error: 0
383 })
168c526f 384 }
168c526f
JB
385 // We need to clean up the resources after our test
386 await pool.destroy()
387 })
388
e4543b14
JB
389 it('Verify LEAST_BUSY strategy default tasks usage statistics requirements', async () => {
390 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
168c526f
JB
391 let pool = new FixedThreadPool(
392 max,
d710242d 393 './tests/worker-files/thread/testWorker.js',
594bfb84 394 { workerChoiceStrategy }
168c526f 395 )
168c526f 396 expect(
86bf340d
JB
397 pool.workerChoiceStrategyContext.getRequiredStatistics()
398 ).toStrictEqual({
399 runTime: true,
400 avgRunTime: false,
401 medRunTime: false,
402 waitTime: false,
403 avgWaitTime: false,
404 medWaitTime: false
405 })
168c526f
JB
406 await pool.destroy()
407 pool = new DynamicThreadPool(
408 min,
409 max,
d710242d 410 './tests/worker-files/thread/testWorker.js',
594bfb84 411 { workerChoiceStrategy }
168c526f 412 )
168c526f 413 expect(
86bf340d
JB
414 pool.workerChoiceStrategyContext.getRequiredStatistics()
415 ).toStrictEqual({
416 runTime: true,
417 avgRunTime: false,
418 medRunTime: false,
419 waitTime: false,
420 avgWaitTime: false,
421 medWaitTime: false
422 })
168c526f
JB
423 // We need to clean up the resources after our test
424 await pool.destroy()
425 })
426
e4543b14 427 it('Verify LEAST_BUSY strategy can be run in a fixed pool', async () => {
168c526f
JB
428 const pool = new FixedThreadPool(
429 max,
430 './tests/worker-files/thread/testWorker.js',
e4543b14 431 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
168c526f 432 )
e4543b14 433 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
ee9f5295 434 const promises = new Set()
a20f0ba5
JB
435 const maxMultiplier = 2
436 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 437 promises.add(pool.execute())
e211bc18
JB
438 }
439 await Promise.all(promises)
440 for (const workerNode of pool.workerNodes) {
441 expect(workerNode.tasksUsage).toStrictEqual({
442 run: expect.any(Number),
443 running: 0,
444 runTime: expect.any(Number),
445 runTimeHistory: expect.any(CircularArray),
446 avgRunTime: 0,
447 medRunTime: 0,
448 waitTime: 0,
449 waitTimeHistory: expect.any(CircularArray),
450 avgWaitTime: 0,
451 medWaitTime: 0,
452 error: 0
453 })
454 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
ee9f5295 455 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
e211bc18 456 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
168c526f 457 }
168c526f
JB
458 // We need to clean up the resources after our test
459 await pool.destroy()
460 })
461
e4543b14 462 it('Verify LEAST_BUSY strategy can be run in a dynamic pool', async () => {
168c526f
JB
463 const pool = new DynamicThreadPool(
464 min,
465 max,
466 './tests/worker-files/thread/testWorker.js',
e4543b14 467 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
168c526f 468 )
e4543b14 469 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
ee9f5295 470 const promises = new Set()
a20f0ba5
JB
471 const maxMultiplier = 2
472 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 473 promises.add(pool.execute())
e211bc18
JB
474 }
475 await Promise.all(promises)
476 for (const workerNode of pool.workerNodes) {
477 expect(workerNode.tasksUsage).toStrictEqual({
478 run: expect.any(Number),
479 running: 0,
480 runTime: expect.any(Number),
481 runTimeHistory: expect.any(CircularArray),
482 avgRunTime: 0,
483 medRunTime: 0,
484 waitTime: 0,
485 waitTimeHistory: expect.any(CircularArray),
486 avgWaitTime: 0,
487 medWaitTime: 0,
488 error: 0
489 })
490 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
ee9f5295 491 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
e211bc18 492 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
ff5e76e1 493 }
ff5e76e1
JB
494 // We need to clean up the resources after our test
495 await pool.destroy()
496 })
497
10fcfaf4 498 it('Verify FAIR_SHARE strategy default tasks usage statistics requirements', async () => {
594bfb84 499 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
10fcfaf4
JB
500 let pool = new FixedThreadPool(
501 max,
d710242d 502 './tests/worker-files/thread/testWorker.js',
594bfb84 503 { workerChoiceStrategy }
10fcfaf4 504 )
10fcfaf4 505 expect(
86bf340d
JB
506 pool.workerChoiceStrategyContext.getRequiredStatistics()
507 ).toStrictEqual({
508 runTime: true,
509 avgRunTime: true,
510 medRunTime: false,
511 waitTime: false,
512 avgWaitTime: false,
513 medWaitTime: false
514 })
fd7ebd49 515 await pool.destroy()
10fcfaf4
JB
516 pool = new DynamicThreadPool(
517 min,
518 max,
d710242d 519 './tests/worker-files/thread/testWorker.js',
594bfb84 520 { workerChoiceStrategy }
10fcfaf4 521 )
10fcfaf4 522 expect(
86bf340d
JB
523 pool.workerChoiceStrategyContext.getRequiredStatistics()
524 ).toStrictEqual({
525 runTime: true,
526 avgRunTime: true,
527 medRunTime: false,
528 waitTime: false,
529 avgWaitTime: false,
530 medWaitTime: false
531 })
10fcfaf4
JB
532 // We need to clean up the resources after our test
533 await pool.destroy()
534 })
535
23ff945a 536 it('Verify FAIR_SHARE strategy can be run in a fixed pool', async () => {
23ff945a
JB
537 const pool = new FixedThreadPool(
538 max,
539 './tests/worker-files/thread/testWorker.js',
540 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
541 )
542 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
ee9f5295 543 const promises = new Set()
a20f0ba5
JB
544 const maxMultiplier = 2
545 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 546 promises.add(pool.execute())
23ff945a 547 }
e211bc18 548 await Promise.all(promises)
138d29a8 549 for (const workerNode of pool.workerNodes) {
86bf340d 550 expect(workerNode.tasksUsage).toStrictEqual({
fc027381 551 run: maxMultiplier,
86bf340d
JB
552 running: 0,
553 runTime: expect.any(Number),
554 runTimeHistory: expect.any(CircularArray),
555 avgRunTime: expect.any(Number),
556 medRunTime: 0,
557 waitTime: 0,
558 waitTimeHistory: expect.any(CircularArray),
559 avgWaitTime: 0,
560 medWaitTime: 0,
561 error: 0
562 })
e211bc18
JB
563 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
564 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThan(0)
138d29a8 565 }
97a2abc3 566 expect(
95c83464 567 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 568 pool.workerChoiceStrategyContext.workerChoiceStrategy
b0d6ed8f 569 ).workersVirtualTaskEndTimestamp.length
f06e48d8 570 ).toBe(pool.workerNodes.length)
23ff945a
JB
571 // We need to clean up the resources after our test
572 await pool.destroy()
573 })
574
575 it('Verify FAIR_SHARE strategy can be run in a dynamic pool', async () => {
23ff945a
JB
576 const pool = new DynamicThreadPool(
577 min,
578 max,
579 './tests/worker-files/thread/testWorker.js',
580 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
581 )
582 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
ee9f5295 583 const promises = new Set()
f7070eee 584 const maxMultiplier = 2
804a889e 585 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 586 promises.add(pool.execute())
23ff945a 587 }
e211bc18 588 await Promise.all(promises)
138d29a8 589 for (const workerNode of pool.workerNodes) {
86bf340d 590 expect(workerNode.tasksUsage).toStrictEqual({
fc027381 591 run: maxMultiplier,
86bf340d
JB
592 running: 0,
593 runTime: expect.any(Number),
594 runTimeHistory: expect.any(CircularArray),
595 avgRunTime: expect.any(Number),
596 medRunTime: 0,
597 waitTime: 0,
598 waitTimeHistory: expect.any(CircularArray),
599 avgWaitTime: 0,
600 medWaitTime: 0,
601 error: 0
602 })
e211bc18
JB
603 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
604 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThan(0)
138d29a8 605 }
2b4fddb8
JB
606 expect(
607 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
608 pool.workerChoiceStrategyContext.workerChoiceStrategy
b0d6ed8f 609 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 610 ).toBe(pool.workerNodes.length)
23ff945a
JB
611 // We need to clean up the resources after our test
612 await pool.destroy()
613 })
614
9e775f96 615 it('Verify FAIR_SHARE strategy can be run in a dynamic pool with median runtime statistic', async () => {
010d7020
JB
616 const pool = new DynamicThreadPool(
617 min,
618 max,
619 './tests/worker-files/thread/testWorker.js',
620 {
621 workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE,
622 workerChoiceStrategyOptions: {
623 medRunTime: true
624 }
625 }
626 )
627 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
ee9f5295 628 const promises = new Set()
010d7020
JB
629 const maxMultiplier = 2
630 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 631 promises.add(pool.execute())
010d7020 632 }
e211bc18 633 await Promise.all(promises)
010d7020 634 for (const workerNode of pool.workerNodes) {
86bf340d 635 expect(workerNode.tasksUsage).toStrictEqual({
fc027381 636 run: maxMultiplier,
86bf340d
JB
637 running: 0,
638 runTime: expect.any(Number),
639 runTimeHistory: expect.any(CircularArray),
640 avgRunTime: 0,
641 medRunTime: expect.any(Number),
642 waitTime: 0,
643 waitTimeHistory: expect.any(CircularArray),
644 avgWaitTime: 0,
645 medWaitTime: 0,
646 error: 0
647 })
e211bc18
JB
648 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
649 expect(workerNode.tasksUsage.medRunTime).toBeGreaterThan(0)
010d7020 650 }
2b4fddb8
JB
651 expect(
652 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
653 pool.workerChoiceStrategyContext.workerChoiceStrategy
b0d6ed8f 654 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 655 ).toBe(pool.workerNodes.length)
010d7020
JB
656 // We need to clean up the resources after our test
657 await pool.destroy()
658 })
659
a6f7f1b4 660 it('Verify FAIR_SHARE strategy internals are resets after setting it', async () => {
594bfb84 661 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
f0829c53 662 let pool = new FixedThreadPool(
caeb9817
JB
663 max,
664 './tests/worker-files/thread/testWorker.js'
665 )
666 expect(
95c83464 667 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 668 workerChoiceStrategy
b0d6ed8f 669 ).workersVirtualTaskEndTimestamp
08f3f44c
JB
670 ).toBeInstanceOf(Array)
671 expect(
672 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
673 workerChoiceStrategy
b0d6ed8f 674 ).workersVirtualTaskEndTimestamp.length
08f3f44c 675 ).toBe(0)
2b4fddb8
JB
676 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
677 workerChoiceStrategy
b0d6ed8f 678 ).workersVirtualTaskEndTimestamp[0] = performance.now()
2b4fddb8
JB
679 expect(
680 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
681 workerChoiceStrategy
b0d6ed8f 682 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 683 ).toBe(1)
594bfb84 684 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
08f3f44c
JB
685 expect(
686 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
687 workerChoiceStrategy
b0d6ed8f 688 ).workersVirtualTaskEndTimestamp
08f3f44c 689 ).toBeInstanceOf(Array)
08f3f44c
JB
690 expect(
691 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
692 workerChoiceStrategy
b0d6ed8f 693 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 694 ).toBe(0)
f0829c53
JB
695 await pool.destroy()
696 pool = new DynamicThreadPool(
697 min,
698 max,
699 './tests/worker-files/thread/testWorker.js'
700 )
701 expect(
95c83464 702 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 703 workerChoiceStrategy
b0d6ed8f 704 ).workersVirtualTaskEndTimestamp
08f3f44c 705 ).toBeInstanceOf(Array)
2b4fddb8
JB
706 expect(
707 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
708 workerChoiceStrategy
b0d6ed8f 709 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 710 ).toBe(0)
08f3f44c
JB
711 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
712 workerChoiceStrategy
b0d6ed8f 713 ).workersVirtualTaskEndTimestamp[0] = performance.now()
08f3f44c
JB
714 expect(
715 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
716 workerChoiceStrategy
b0d6ed8f 717 ).workersVirtualTaskEndTimestamp.length
08f3f44c 718 ).toBe(1)
594bfb84 719 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
08f3f44c
JB
720 expect(
721 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
722 workerChoiceStrategy
b0d6ed8f 723 ).workersVirtualTaskEndTimestamp
08f3f44c
JB
724 ).toBeInstanceOf(Array)
725 expect(
726 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
727 workerChoiceStrategy
b0d6ed8f 728 ).workersVirtualTaskEndTimestamp.length
08f3f44c 729 ).toBe(0)
caeb9817
JB
730 // We need to clean up the resources after our test
731 await pool.destroy()
732 })
733
10fcfaf4 734 it('Verify WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
594bfb84 735 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
10fcfaf4
JB
736 let pool = new FixedThreadPool(
737 max,
d710242d 738 './tests/worker-files/thread/testWorker.js',
594bfb84 739 { workerChoiceStrategy }
10fcfaf4 740 )
10fcfaf4 741 expect(
86bf340d
JB
742 pool.workerChoiceStrategyContext.getRequiredStatistics()
743 ).toStrictEqual({
744 runTime: true,
745 avgRunTime: true,
746 medRunTime: false,
747 waitTime: false,
748 avgWaitTime: false,
749 medWaitTime: false
750 })
fd7ebd49 751 await pool.destroy()
10fcfaf4
JB
752 pool = new DynamicThreadPool(
753 min,
754 max,
d710242d 755 './tests/worker-files/thread/testWorker.js',
594bfb84 756 { workerChoiceStrategy }
10fcfaf4 757 )
10fcfaf4 758 expect(
86bf340d
JB
759 pool.workerChoiceStrategyContext.getRequiredStatistics()
760 ).toStrictEqual({
761 runTime: true,
762 avgRunTime: true,
763 medRunTime: false,
764 waitTime: false,
765 avgWaitTime: false,
766 medWaitTime: false
767 })
10fcfaf4
JB
768 // We need to clean up the resources after our test
769 await pool.destroy()
770 })
771
b3432a63 772 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
b3432a63
JB
773 const pool = new FixedThreadPool(
774 max,
775 './tests/worker-files/thread/testWorker.js',
776 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
777 )
778 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
ee9f5295 779 const promises = new Set()
a20f0ba5
JB
780 const maxMultiplier = 2
781 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 782 promises.add(pool.execute())
b3432a63 783 }
e211bc18 784 await Promise.all(promises)
138d29a8 785 for (const workerNode of pool.workerNodes) {
86bf340d 786 expect(workerNode.tasksUsage).toStrictEqual({
cb4a00ab 787 run: expect.any(Number),
86bf340d
JB
788 running: 0,
789 runTime: expect.any(Number),
790 runTimeHistory: expect.any(CircularArray),
791 avgRunTime: expect.any(Number),
792 medRunTime: 0,
793 waitTime: 0,
794 waitTimeHistory: expect.any(CircularArray),
795 avgWaitTime: 0,
796 medWaitTime: 0,
797 error: 0
798 })
ee9f5295
JB
799 expect(workerNode.tasksUsage.run).toBeGreaterThanOrEqual(0)
800 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
801 expect(workerNode.tasksUsage.runTime).toBeGreaterThanOrEqual(0)
138d29a8 802 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0)
138d29a8 803 }
97a2abc3 804 expect(
95c83464 805 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 806 pool.workerChoiceStrategyContext.workerChoiceStrategy
08f3f44c
JB
807 ).defaultWorkerWeight
808 ).toBeGreaterThan(0)
809 expect(
810 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
811 pool.workerChoiceStrategyContext.workerChoiceStrategy
812 ).workerVirtualTaskRunTime
813 ).toBeGreaterThanOrEqual(0)
b3432a63
JB
814 // We need to clean up the resources after our test
815 await pool.destroy()
816 })
817
818 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
b3432a63
JB
819 const pool = new DynamicThreadPool(
820 min,
821 max,
822 './tests/worker-files/thread/testWorker.js',
823 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
824 )
825 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
ee9f5295 826 const promises = new Set()
138d29a8 827 const maxMultiplier = 2
5502c07c 828 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 829 promises.add(pool.execute())
b3432a63 830 }
e211bc18 831 await Promise.all(promises)
138d29a8 832 for (const workerNode of pool.workerNodes) {
86bf340d 833 expect(workerNode.tasksUsage).toStrictEqual({
e211bc18 834 run: expect.any(Number),
86bf340d
JB
835 running: 0,
836 runTime: expect.any(Number),
837 runTimeHistory: expect.any(CircularArray),
838 avgRunTime: expect.any(Number),
839 medRunTime: 0,
840 waitTime: 0,
841 waitTimeHistory: expect.any(CircularArray),
842 avgWaitTime: 0,
843 medWaitTime: 0,
844 error: 0
845 })
e211bc18 846 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
ee9f5295 847 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
e211bc18
JB
848 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
849 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThan(0)
138d29a8 850 }
2b4fddb8
JB
851 expect(
852 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
853 pool.workerChoiceStrategyContext.workerChoiceStrategy
854 ).defaultWorkerWeight
855 ).toBeGreaterThan(0)
856 expect(
857 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
858 pool.workerChoiceStrategyContext.workerChoiceStrategy
859 ).workerVirtualTaskRunTime
860 ).toBeGreaterThanOrEqual(0)
b3432a63
JB
861 // We need to clean up the resources after our test
862 await pool.destroy()
863 })
864
9e775f96 865 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool with median runtime statistic', async () => {
010d7020
JB
866 const pool = new DynamicThreadPool(
867 min,
868 max,
869 './tests/worker-files/thread/testWorker.js',
870 {
871 workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN,
872 workerChoiceStrategyOptions: {
873 medRunTime: true
874 }
875 }
876 )
877 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
ee9f5295 878 const promises = new Set()
010d7020
JB
879 const maxMultiplier = 2
880 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 881 promises.add(pool.execute())
010d7020 882 }
e211bc18 883 await Promise.all(promises)
010d7020 884 for (const workerNode of pool.workerNodes) {
86bf340d 885 expect(workerNode.tasksUsage).toStrictEqual({
e211bc18 886 run: expect.any(Number),
86bf340d
JB
887 running: 0,
888 runTime: expect.any(Number),
889 runTimeHistory: expect.any(CircularArray),
890 avgRunTime: 0,
891 medRunTime: expect.any(Number),
892 waitTime: 0,
893 waitTimeHistory: expect.any(CircularArray),
894 avgWaitTime: 0,
895 medWaitTime: 0,
896 error: 0
897 })
e211bc18 898 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
ee9f5295 899 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
e211bc18
JB
900 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
901 expect(workerNode.tasksUsage.medRunTime).toBeGreaterThan(0)
010d7020 902 }
08f3f44c
JB
903 expect(
904 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
905 pool.workerChoiceStrategyContext.workerChoiceStrategy
906 ).defaultWorkerWeight
907 ).toBeGreaterThan(0)
908 expect(
909 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
910 pool.workerChoiceStrategyContext.workerChoiceStrategy
911 ).workerVirtualTaskRunTime
912 ).toBeGreaterThanOrEqual(0)
010d7020
JB
913 // We need to clean up the resources after our test
914 await pool.destroy()
915 })
916
a6f7f1b4 917 it('Verify WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
594bfb84 918 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
f0829c53 919 let pool = new FixedThreadPool(
caeb9817
JB
920 max,
921 './tests/worker-files/thread/testWorker.js'
922 )
38f6e859 923 expect(
95c83464 924 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 925 workerChoiceStrategy
f06e48d8 926 ).currentWorkerNodeId
b529c323 927 ).toBeDefined()
38f6e859 928 expect(
95c83464 929 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 930 workerChoiceStrategy
b529c323
JB
931 ).defaultWorkerWeight
932 ).toBeDefined()
caeb9817 933 expect(
95c83464 934 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 935 workerChoiceStrategy
08f3f44c 936 ).workerVirtualTaskRunTime
b529c323 937 ).toBeDefined()
594bfb84 938 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 939 expect(
95c83464 940 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 941 pool.workerChoiceStrategyContext.workerChoiceStrategy
f06e48d8 942 ).currentWorkerNodeId
a6f7f1b4
JB
943 ).toBe(0)
944 expect(
95c83464 945 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 946 pool.workerChoiceStrategyContext.workerChoiceStrategy
95c83464 947 ).defaultWorkerWeight
a6f7f1b4 948 ).toBeGreaterThan(0)
08f3f44c
JB
949 expect(
950 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
951 workerChoiceStrategy
952 ).workerVirtualTaskRunTime
953 ).toBe(0)
f0829c53
JB
954 await pool.destroy()
955 pool = new DynamicThreadPool(
956 min,
957 max,
958 './tests/worker-files/thread/testWorker.js'
959 )
38f6e859 960 expect(
95c83464 961 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 962 workerChoiceStrategy
f06e48d8 963 ).currentWorkerNodeId
b529c323 964 ).toBeDefined()
38f6e859 965 expect(
95c83464 966 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 967 workerChoiceStrategy
b529c323
JB
968 ).defaultWorkerWeight
969 ).toBeDefined()
f0829c53 970 expect(
95c83464 971 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 972 workerChoiceStrategy
08f3f44c 973 ).workerVirtualTaskRunTime
b529c323 974 ).toBeDefined()
594bfb84 975 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 976 expect(
95c83464 977 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 978 pool.workerChoiceStrategyContext.workerChoiceStrategy
f06e48d8 979 ).currentWorkerNodeId
a6f7f1b4
JB
980 ).toBe(0)
981 expect(
95c83464 982 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 983 pool.workerChoiceStrategyContext.workerChoiceStrategy
95c83464 984 ).defaultWorkerWeight
a6f7f1b4 985 ).toBeGreaterThan(0)
08f3f44c
JB
986 expect(
987 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
988 workerChoiceStrategy
989 ).workerVirtualTaskRunTime
990 ).toBe(0)
caeb9817
JB
991 // We need to clean up the resources after our test
992 await pool.destroy()
993 })
994
e52fb978
JB
995 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
996 const workerChoiceStrategy =
997 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
998 let pool = new FixedThreadPool(
999 max,
1000 './tests/worker-files/thread/testWorker.js',
1001 { workerChoiceStrategy }
1002 )
1003 expect(
1004 pool.workerChoiceStrategyContext.getRequiredStatistics()
1005 ).toStrictEqual({
1006 runTime: false,
1007 avgRunTime: false,
1008 medRunTime: false,
1009 waitTime: false,
1010 avgWaitTime: false,
1011 medWaitTime: false
1012 })
1013 await pool.destroy()
1014 pool = new DynamicThreadPool(
1015 min,
1016 max,
1017 './tests/worker-files/thread/testWorker.js',
1018 { workerChoiceStrategy }
1019 )
1020 expect(
1021 pool.workerChoiceStrategyContext.getRequiredStatistics()
1022 ).toStrictEqual({
1023 runTime: false,
1024 avgRunTime: false,
1025 medRunTime: false,
1026 waitTime: false,
1027 avgWaitTime: false,
1028 medWaitTime: false
1029 })
1030 // We need to clean up the resources after our test
1031 await pool.destroy()
1032 })
1033
89b09b26 1034 it('Verify unknown strategy throw error', () => {
a35560ba
S
1035 expect(
1036 () =>
1037 new DynamicThreadPool(
1038 min,
1039 max,
1040 './tests/worker-files/thread/testWorker.js',
1927ee67 1041 { workerChoiceStrategy: 'UNKNOWN_STRATEGY' }
a35560ba 1042 )
d4aeae5a 1043 ).toThrowError("Invalid worker choice strategy 'UNKNOWN_STRATEGY'")
a35560ba
S
1044 })
1045})