Commit | Line | Data |
---|---|---|
a61a0724 | 1 | const { expect } = require('expect') |
a35560ba S |
2 | const { |
3 | WorkerChoiceStrategies, | |
4 | DynamicThreadPool, | |
2ced693a JB |
5 | FixedThreadPool, |
6 | FixedClusterPool | |
cdace0e5 | 7 | } = require('../../../lib') |
86bf340d | 8 | const { CircularArray } = require('../../../lib/circular-array') |
a35560ba S |
9 | |
10 | describe('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 | ) |
87de9ff5 JB |
124 | expect( |
125 | pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() | |
126 | ).toStrictEqual({ | |
932fc8be JB |
127 | runTime: { |
128 | aggregate: false, | |
129 | average: false, | |
130 | median: false | |
131 | }, | |
132 | waitTime: { | |
133 | aggregate: false, | |
134 | average: false, | |
135 | median: false | |
136 | }, | |
d44d5953 | 137 | elu: false |
86bf340d | 138 | }) |
fd7ebd49 | 139 | await pool.destroy() |
10fcfaf4 JB |
140 | pool = new DynamicThreadPool( |
141 | min, | |
142 | max, | |
d710242d | 143 | './tests/worker-files/thread/testWorker.js', |
594bfb84 | 144 | { workerChoiceStrategy } |
10fcfaf4 | 145 | ) |
87de9ff5 JB |
146 | expect( |
147 | pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() | |
148 | ).toStrictEqual({ | |
932fc8be JB |
149 | runTime: { |
150 | aggregate: false, | |
151 | average: false, | |
152 | median: false | |
153 | }, | |
154 | waitTime: { | |
155 | aggregate: false, | |
156 | average: false, | |
157 | median: false | |
158 | }, | |
d44d5953 | 159 | elu: false |
86bf340d | 160 | }) |
10fcfaf4 JB |
161 | // We need to clean up the resources after our test |
162 | await pool.destroy() | |
163 | }) | |
164 | ||
bdaf31cd | 165 | it('Verify ROUND_ROBIN strategy can be run in a fixed pool', async () => { |
bdaf31cd JB |
166 | const pool = new FixedThreadPool( |
167 | max, | |
168 | './tests/worker-files/thread/testWorker.js', | |
169 | { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN } | |
170 | ) | |
bdaf31cd | 171 | // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose` |
ee9f5295 | 172 | const promises = new Set() |
a20f0ba5 JB |
173 | const maxMultiplier = 2 |
174 | for (let i = 0; i < max * maxMultiplier; i++) { | |
ee9f5295 | 175 | promises.add(pool.execute()) |
e211bc18 JB |
176 | } |
177 | await Promise.all(promises) | |
178 | for (const workerNode of pool.workerNodes) { | |
a4e07f72 JB |
179 | expect(workerNode.workerUsage).toStrictEqual({ |
180 | tasks: { | |
181 | executed: maxMultiplier, | |
182 | executing: 0, | |
183 | queued: 0, | |
184 | failed: 0 | |
185 | }, | |
186 | runTime: { | |
932fc8be | 187 | aggregate: 0, |
a4e07f72 JB |
188 | average: 0, |
189 | median: 0, | |
190 | history: expect.any(CircularArray) | |
191 | }, | |
192 | waitTime: { | |
932fc8be | 193 | aggregate: 0, |
a4e07f72 JB |
194 | average: 0, |
195 | median: 0, | |
196 | history: expect.any(CircularArray) | |
197 | }, | |
d44d5953 | 198 | elu: undefined |
e211bc18 | 199 | }) |
bdaf31cd | 200 | } |
9458090a JB |
201 | expect( |
202 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
203 | WorkerChoiceStrategies.ROUND_ROBIN | |
204 | ).nextWorkerNodeId | |
205 | ).toBe(0) | |
bdaf31cd JB |
206 | // We need to clean up the resources after our test |
207 | await pool.destroy() | |
208 | }) | |
209 | ||
210 | it('Verify ROUND_ROBIN strategy can be run in a dynamic pool', async () => { | |
bdaf31cd JB |
211 | const pool = new DynamicThreadPool( |
212 | min, | |
213 | max, | |
214 | './tests/worker-files/thread/testWorker.js', | |
215 | { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN } | |
216 | ) | |
bdaf31cd | 217 | // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose` |
ee9f5295 | 218 | const promises = new Set() |
a20f0ba5 JB |
219 | const maxMultiplier = 2 |
220 | for (let i = 0; i < max * maxMultiplier; i++) { | |
ee9f5295 | 221 | promises.add(pool.execute()) |
e211bc18 JB |
222 | } |
223 | await Promise.all(promises) | |
224 | for (const workerNode of pool.workerNodes) { | |
a4e07f72 JB |
225 | expect(workerNode.workerUsage).toStrictEqual({ |
226 | tasks: { | |
227 | executed: maxMultiplier, | |
228 | executing: 0, | |
229 | queued: 0, | |
230 | failed: 0 | |
231 | }, | |
232 | runTime: { | |
932fc8be | 233 | aggregate: 0, |
a4e07f72 JB |
234 | average: 0, |
235 | median: 0, | |
236 | history: expect.any(CircularArray) | |
237 | }, | |
238 | waitTime: { | |
932fc8be | 239 | aggregate: 0, |
a4e07f72 JB |
240 | average: 0, |
241 | median: 0, | |
242 | history: expect.any(CircularArray) | |
243 | }, | |
d44d5953 | 244 | elu: undefined |
e211bc18 | 245 | }) |
bdaf31cd | 246 | } |
9458090a JB |
247 | expect( |
248 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
249 | WorkerChoiceStrategies.ROUND_ROBIN | |
250 | ).nextWorkerNodeId | |
251 | ).toBe(0) | |
bdaf31cd JB |
252 | // We need to clean up the resources after our test |
253 | await pool.destroy() | |
254 | }) | |
255 | ||
2ced693a | 256 | it('Verify ROUND_ROBIN strategy runtime behavior', async () => { |
594bfb84 | 257 | const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN |
2ced693a JB |
258 | let pool = new FixedClusterPool( |
259 | max, | |
594bfb84 JB |
260 | './tests/worker-files/cluster/testWorker.js', |
261 | { workerChoiceStrategy } | |
2ced693a JB |
262 | ) |
263 | let results = new Set() | |
264 | for (let i = 0; i < max; i++) { | |
20dcad1a | 265 | results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.id) |
2ced693a JB |
266 | } |
267 | expect(results.size).toBe(max) | |
268 | await pool.destroy() | |
594bfb84 JB |
269 | pool = new FixedThreadPool( |
270 | max, | |
271 | './tests/worker-files/thread/testWorker.js', | |
272 | { workerChoiceStrategy } | |
273 | ) | |
2ced693a JB |
274 | results = new Set() |
275 | for (let i = 0; i < max; i++) { | |
20dcad1a | 276 | results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.threadId) |
2ced693a JB |
277 | } |
278 | expect(results.size).toBe(max) | |
279 | await pool.destroy() | |
280 | }) | |
281 | ||
a6f7f1b4 | 282 | it('Verify ROUND_ROBIN strategy internals are resets after setting it', async () => { |
594bfb84 | 283 | const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN |
a6f7f1b4 JB |
284 | let pool = new FixedThreadPool( |
285 | max, | |
286 | './tests/worker-files/thread/testWorker.js', | |
287 | { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN } | |
288 | ) | |
38f6e859 | 289 | expect( |
95c83464 | 290 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
594bfb84 | 291 | workerChoiceStrategy |
f06e48d8 | 292 | ).nextWorkerNodeId |
b529c323 | 293 | ).toBeDefined() |
594bfb84 | 294 | pool.setWorkerChoiceStrategy(workerChoiceStrategy) |
a6f7f1b4 | 295 | expect( |
95c83464 | 296 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
d710242d | 297 | pool.workerChoiceStrategyContext.workerChoiceStrategy |
f06e48d8 | 298 | ).nextWorkerNodeId |
a6f7f1b4 JB |
299 | ).toBe(0) |
300 | await pool.destroy() | |
301 | pool = new DynamicThreadPool( | |
302 | min, | |
303 | max, | |
304 | './tests/worker-files/thread/testWorker.js', | |
305 | { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN } | |
306 | ) | |
38f6e859 | 307 | expect( |
95c83464 | 308 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
594bfb84 | 309 | workerChoiceStrategy |
f06e48d8 | 310 | ).nextWorkerNodeId |
b529c323 | 311 | ).toBeDefined() |
594bfb84 | 312 | pool.setWorkerChoiceStrategy(workerChoiceStrategy) |
a6f7f1b4 | 313 | expect( |
95c83464 | 314 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
d710242d | 315 | pool.workerChoiceStrategyContext.workerChoiceStrategy |
f06e48d8 | 316 | ).nextWorkerNodeId |
a6f7f1b4 JB |
317 | ).toBe(0) |
318 | // We need to clean up the resources after our test | |
319 | await pool.destroy() | |
320 | }) | |
321 | ||
e4543b14 JB |
322 | it('Verify LEAST_USED strategy default tasks usage statistics requirements', async () => { |
323 | const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED | |
10fcfaf4 JB |
324 | let pool = new FixedThreadPool( |
325 | max, | |
d710242d | 326 | './tests/worker-files/thread/testWorker.js', |
594bfb84 | 327 | { workerChoiceStrategy } |
10fcfaf4 | 328 | ) |
87de9ff5 JB |
329 | expect( |
330 | pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() | |
331 | ).toStrictEqual({ | |
932fc8be JB |
332 | runTime: { |
333 | aggregate: false, | |
334 | average: false, | |
335 | median: false | |
336 | }, | |
337 | waitTime: { | |
338 | aggregate: false, | |
339 | average: false, | |
340 | median: false | |
341 | }, | |
d44d5953 | 342 | elu: false |
86bf340d | 343 | }) |
fd7ebd49 | 344 | await pool.destroy() |
10fcfaf4 JB |
345 | pool = new DynamicThreadPool( |
346 | min, | |
347 | max, | |
d710242d | 348 | './tests/worker-files/thread/testWorker.js', |
594bfb84 | 349 | { workerChoiceStrategy } |
10fcfaf4 | 350 | ) |
87de9ff5 JB |
351 | expect( |
352 | pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() | |
353 | ).toStrictEqual({ | |
932fc8be JB |
354 | runTime: { |
355 | aggregate: false, | |
356 | average: false, | |
357 | median: false | |
358 | }, | |
359 | waitTime: { | |
360 | aggregate: false, | |
361 | average: false, | |
362 | median: false | |
363 | }, | |
d44d5953 | 364 | elu: false |
86bf340d | 365 | }) |
10fcfaf4 JB |
366 | // We need to clean up the resources after our test |
367 | await pool.destroy() | |
368 | }) | |
369 | ||
e4543b14 | 370 | it('Verify LEAST_USED strategy can be run in a fixed pool', async () => { |
b98ec2e6 JB |
371 | const pool = new FixedThreadPool( |
372 | max, | |
373 | './tests/worker-files/thread/testWorker.js', | |
e4543b14 | 374 | { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED } |
b98ec2e6 | 375 | ) |
e4543b14 | 376 | // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose` |
ee9f5295 | 377 | const promises = new Set() |
a20f0ba5 JB |
378 | const maxMultiplier = 2 |
379 | for (let i = 0; i < max * maxMultiplier; i++) { | |
ee9f5295 | 380 | promises.add(pool.execute()) |
e211bc18 JB |
381 | } |
382 | await Promise.all(promises) | |
383 | for (const workerNode of pool.workerNodes) { | |
a4e07f72 JB |
384 | expect(workerNode.workerUsage).toStrictEqual({ |
385 | tasks: { | |
386 | executed: maxMultiplier, | |
387 | executing: 0, | |
388 | queued: 0, | |
389 | failed: 0 | |
390 | }, | |
391 | runTime: { | |
932fc8be | 392 | aggregate: 0, |
a4e07f72 JB |
393 | average: 0, |
394 | median: 0, | |
395 | history: expect.any(CircularArray) | |
396 | }, | |
397 | waitTime: { | |
932fc8be | 398 | aggregate: 0, |
a4e07f72 JB |
399 | average: 0, |
400 | median: 0, | |
401 | history: expect.any(CircularArray) | |
402 | }, | |
d44d5953 | 403 | elu: undefined |
e211bc18 | 404 | }) |
a35560ba | 405 | } |
a35560ba S |
406 | // We need to clean up the resources after our test |
407 | await pool.destroy() | |
408 | }) | |
409 | ||
e4543b14 | 410 | it('Verify LEAST_USED strategy can be run in a dynamic pool', async () => { |
ff5e76e1 JB |
411 | const pool = new DynamicThreadPool( |
412 | min, | |
413 | max, | |
414 | './tests/worker-files/thread/testWorker.js', | |
e4543b14 | 415 | { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED } |
ff5e76e1 | 416 | ) |
e4543b14 | 417 | // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose` |
ee9f5295 | 418 | const promises = new Set() |
a20f0ba5 JB |
419 | const maxMultiplier = 2 |
420 | for (let i = 0; i < max * maxMultiplier; i++) { | |
ee9f5295 | 421 | promises.add(pool.execute()) |
e211bc18 JB |
422 | } |
423 | await Promise.all(promises) | |
424 | for (const workerNode of pool.workerNodes) { | |
a4e07f72 JB |
425 | expect(workerNode.workerUsage).toStrictEqual({ |
426 | tasks: { | |
427 | executed: maxMultiplier, | |
428 | executing: 0, | |
429 | queued: 0, | |
430 | failed: 0 | |
431 | }, | |
432 | runTime: { | |
932fc8be | 433 | aggregate: 0, |
a4e07f72 JB |
434 | average: 0, |
435 | median: 0, | |
436 | history: expect.any(CircularArray) | |
437 | }, | |
438 | waitTime: { | |
932fc8be | 439 | aggregate: 0, |
a4e07f72 JB |
440 | average: 0, |
441 | median: 0, | |
442 | history: expect.any(CircularArray) | |
443 | }, | |
444 | ||
d44d5953 | 445 | elu: undefined |
e211bc18 | 446 | }) |
168c526f | 447 | } |
168c526f JB |
448 | // We need to clean up the resources after our test |
449 | await pool.destroy() | |
450 | }) | |
451 | ||
e4543b14 JB |
452 | it('Verify LEAST_BUSY strategy default tasks usage statistics requirements', async () => { |
453 | const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY | |
168c526f JB |
454 | let pool = new FixedThreadPool( |
455 | max, | |
d710242d | 456 | './tests/worker-files/thread/testWorker.js', |
594bfb84 | 457 | { workerChoiceStrategy } |
168c526f | 458 | ) |
87de9ff5 JB |
459 | expect( |
460 | pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() | |
461 | ).toStrictEqual({ | |
932fc8be JB |
462 | runTime: { |
463 | aggregate: true, | |
464 | average: false, | |
465 | median: false | |
466 | }, | |
467 | waitTime: { | |
468 | aggregate: true, | |
469 | average: false, | |
470 | median: false | |
471 | }, | |
d44d5953 | 472 | elu: false |
86bf340d | 473 | }) |
168c526f JB |
474 | await pool.destroy() |
475 | pool = new DynamicThreadPool( | |
476 | min, | |
477 | max, | |
d710242d | 478 | './tests/worker-files/thread/testWorker.js', |
594bfb84 | 479 | { workerChoiceStrategy } |
168c526f | 480 | ) |
87de9ff5 JB |
481 | expect( |
482 | pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() | |
483 | ).toStrictEqual({ | |
932fc8be JB |
484 | runTime: { |
485 | aggregate: true, | |
486 | average: false, | |
487 | median: false | |
488 | }, | |
489 | waitTime: { | |
490 | aggregate: true, | |
491 | average: false, | |
492 | median: false | |
493 | }, | |
d44d5953 | 494 | elu: false |
86bf340d | 495 | }) |
168c526f JB |
496 | // We need to clean up the resources after our test |
497 | await pool.destroy() | |
498 | }) | |
499 | ||
e4543b14 | 500 | it('Verify LEAST_BUSY strategy can be run in a fixed pool', async () => { |
168c526f JB |
501 | const pool = new FixedThreadPool( |
502 | max, | |
503 | './tests/worker-files/thread/testWorker.js', | |
e4543b14 | 504 | { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY } |
168c526f | 505 | ) |
e4543b14 | 506 | // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose` |
ee9f5295 | 507 | const promises = new Set() |
a20f0ba5 JB |
508 | const maxMultiplier = 2 |
509 | for (let i = 0; i < max * maxMultiplier; i++) { | |
ee9f5295 | 510 | promises.add(pool.execute()) |
e211bc18 JB |
511 | } |
512 | await Promise.all(promises) | |
513 | for (const workerNode of pool.workerNodes) { | |
a4e07f72 JB |
514 | expect(workerNode.workerUsage).toStrictEqual({ |
515 | tasks: { | |
516 | executed: expect.any(Number), | |
517 | executing: 0, | |
518 | queued: 0, | |
519 | failed: 0 | |
520 | }, | |
521 | runTime: { | |
932fc8be | 522 | aggregate: expect.any(Number), |
a4e07f72 JB |
523 | average: 0, |
524 | median: 0, | |
525 | history: expect.any(CircularArray) | |
526 | }, | |
527 | waitTime: { | |
932fc8be | 528 | aggregate: expect.any(Number), |
a4e07f72 JB |
529 | average: 0, |
530 | median: 0, | |
531 | history: expect.any(CircularArray) | |
532 | }, | |
d44d5953 | 533 | elu: undefined |
e211bc18 | 534 | }) |
a4e07f72 JB |
535 | expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0) |
536 | expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual( | |
537 | max * maxMultiplier | |
538 | ) | |
932fc8be JB |
539 | expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThanOrEqual(0) |
540 | expect(workerNode.workerUsage.waitTime.aggregate).toBeGreaterThanOrEqual( | |
a4e07f72 JB |
541 | 0 |
542 | ) | |
168c526f | 543 | } |
168c526f JB |
544 | // We need to clean up the resources after our test |
545 | await pool.destroy() | |
546 | }) | |
547 | ||
e4543b14 | 548 | it('Verify LEAST_BUSY strategy can be run in a dynamic pool', async () => { |
168c526f JB |
549 | const pool = new DynamicThreadPool( |
550 | min, | |
551 | max, | |
552 | './tests/worker-files/thread/testWorker.js', | |
e4543b14 | 553 | { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY } |
168c526f | 554 | ) |
e4543b14 | 555 | // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose` |
ee9f5295 | 556 | const promises = new Set() |
a20f0ba5 JB |
557 | const maxMultiplier = 2 |
558 | for (let i = 0; i < max * maxMultiplier; i++) { | |
ee9f5295 | 559 | promises.add(pool.execute()) |
e211bc18 JB |
560 | } |
561 | await Promise.all(promises) | |
562 | for (const workerNode of pool.workerNodes) { | |
a4e07f72 JB |
563 | expect(workerNode.workerUsage).toStrictEqual({ |
564 | tasks: { | |
565 | executed: expect.any(Number), | |
566 | executing: 0, | |
567 | queued: 0, | |
568 | failed: 0 | |
569 | }, | |
570 | runTime: { | |
932fc8be | 571 | aggregate: expect.any(Number), |
a4e07f72 JB |
572 | average: 0, |
573 | median: 0, | |
574 | history: expect.any(CircularArray) | |
575 | }, | |
576 | waitTime: { | |
932fc8be | 577 | aggregate: expect.any(Number), |
a4e07f72 JB |
578 | average: 0, |
579 | median: 0, | |
580 | history: expect.any(CircularArray) | |
581 | }, | |
d44d5953 | 582 | elu: undefined |
e211bc18 | 583 | }) |
a4e07f72 JB |
584 | expect(workerNode.workerUsage.tasks.executed).toBeGreaterThan(0) |
585 | expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual( | |
586 | max * maxMultiplier | |
587 | ) | |
932fc8be JB |
588 | expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0) |
589 | expect(workerNode.workerUsage.waitTime.aggregate).toBeGreaterThan(0) | |
ff5e76e1 | 590 | } |
ff5e76e1 JB |
591 | // We need to clean up the resources after our test |
592 | await pool.destroy() | |
593 | }) | |
594 | ||
10fcfaf4 | 595 | it('Verify FAIR_SHARE strategy default tasks usage statistics requirements', async () => { |
594bfb84 | 596 | const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE |
10fcfaf4 JB |
597 | let pool = new FixedThreadPool( |
598 | max, | |
d710242d | 599 | './tests/worker-files/thread/testWorker.js', |
594bfb84 | 600 | { workerChoiceStrategy } |
10fcfaf4 | 601 | ) |
87de9ff5 JB |
602 | expect( |
603 | pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() | |
604 | ).toStrictEqual({ | |
932fc8be JB |
605 | runTime: { |
606 | aggregate: true, | |
607 | average: true, | |
608 | median: false | |
609 | }, | |
610 | waitTime: { | |
611 | aggregate: false, | |
612 | average: false, | |
613 | median: false | |
614 | }, | |
d44d5953 | 615 | elu: false |
86bf340d | 616 | }) |
fd7ebd49 | 617 | await pool.destroy() |
10fcfaf4 JB |
618 | pool = new DynamicThreadPool( |
619 | min, | |
620 | max, | |
d710242d | 621 | './tests/worker-files/thread/testWorker.js', |
594bfb84 | 622 | { workerChoiceStrategy } |
10fcfaf4 | 623 | ) |
87de9ff5 JB |
624 | expect( |
625 | pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() | |
626 | ).toStrictEqual({ | |
932fc8be JB |
627 | runTime: { |
628 | aggregate: true, | |
629 | average: true, | |
630 | median: false | |
631 | }, | |
632 | waitTime: { | |
633 | aggregate: false, | |
634 | average: false, | |
635 | median: false | |
636 | }, | |
d44d5953 | 637 | elu: false |
86bf340d | 638 | }) |
10fcfaf4 JB |
639 | // We need to clean up the resources after our test |
640 | await pool.destroy() | |
641 | }) | |
642 | ||
23ff945a | 643 | it('Verify FAIR_SHARE strategy can be run in a fixed pool', async () => { |
23ff945a JB |
644 | const pool = new FixedThreadPool( |
645 | max, | |
646 | './tests/worker-files/thread/testWorker.js', | |
647 | { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE } | |
648 | ) | |
649 | // TODO: Create a better test to cover `FairShareChoiceStrategy#choose` | |
ee9f5295 | 650 | const promises = new Set() |
a20f0ba5 JB |
651 | const maxMultiplier = 2 |
652 | for (let i = 0; i < max * maxMultiplier; i++) { | |
ee9f5295 | 653 | promises.add(pool.execute()) |
23ff945a | 654 | } |
e211bc18 | 655 | await Promise.all(promises) |
138d29a8 | 656 | for (const workerNode of pool.workerNodes) { |
a4e07f72 JB |
657 | expect(workerNode.workerUsage).toStrictEqual({ |
658 | tasks: { | |
659 | executed: maxMultiplier, | |
660 | executing: 0, | |
661 | queued: 0, | |
662 | failed: 0 | |
663 | }, | |
664 | runTime: { | |
932fc8be | 665 | aggregate: expect.any(Number), |
a4e07f72 JB |
666 | average: expect.any(Number), |
667 | median: 0, | |
668 | history: expect.any(CircularArray) | |
669 | }, | |
670 | waitTime: { | |
932fc8be | 671 | aggregate: 0, |
a4e07f72 JB |
672 | average: 0, |
673 | median: 0, | |
674 | history: expect.any(CircularArray) | |
675 | }, | |
d44d5953 | 676 | elu: undefined |
86bf340d | 677 | }) |
932fc8be | 678 | expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0) |
a4e07f72 | 679 | expect(workerNode.workerUsage.runTime.average).toBeGreaterThan(0) |
138d29a8 | 680 | } |
97a2abc3 | 681 | expect( |
95c83464 | 682 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
d710242d | 683 | pool.workerChoiceStrategyContext.workerChoiceStrategy |
b0d6ed8f | 684 | ).workersVirtualTaskEndTimestamp.length |
f06e48d8 | 685 | ).toBe(pool.workerNodes.length) |
23ff945a JB |
686 | // We need to clean up the resources after our test |
687 | await pool.destroy() | |
688 | }) | |
689 | ||
690 | it('Verify FAIR_SHARE strategy can be run in a dynamic pool', async () => { | |
23ff945a JB |
691 | const pool = new DynamicThreadPool( |
692 | min, | |
693 | max, | |
694 | './tests/worker-files/thread/testWorker.js', | |
695 | { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE } | |
696 | ) | |
697 | // TODO: Create a better test to cover `FairShareChoiceStrategy#choose` | |
ee9f5295 | 698 | const promises = new Set() |
f7070eee | 699 | const maxMultiplier = 2 |
804a889e | 700 | for (let i = 0; i < max * maxMultiplier; i++) { |
ee9f5295 | 701 | promises.add(pool.execute()) |
23ff945a | 702 | } |
e211bc18 | 703 | await Promise.all(promises) |
138d29a8 | 704 | for (const workerNode of pool.workerNodes) { |
a4e07f72 JB |
705 | expect(workerNode.workerUsage).toStrictEqual({ |
706 | tasks: { | |
707 | executed: maxMultiplier, | |
708 | executing: 0, | |
709 | queued: 0, | |
710 | failed: 0 | |
711 | }, | |
712 | runTime: { | |
932fc8be | 713 | aggregate: expect.any(Number), |
a4e07f72 JB |
714 | average: expect.any(Number), |
715 | median: 0, | |
716 | history: expect.any(CircularArray) | |
717 | }, | |
718 | waitTime: { | |
932fc8be | 719 | aggregate: 0, |
a4e07f72 JB |
720 | average: 0, |
721 | median: 0, | |
722 | history: expect.any(CircularArray) | |
723 | }, | |
d44d5953 | 724 | elu: undefined |
86bf340d | 725 | }) |
932fc8be | 726 | expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0) |
a4e07f72 | 727 | expect(workerNode.workerUsage.runTime.average).toBeGreaterThan(0) |
138d29a8 | 728 | } |
2b4fddb8 JB |
729 | expect( |
730 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
731 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
b0d6ed8f | 732 | ).workersVirtualTaskEndTimestamp.length |
2b4fddb8 | 733 | ).toBe(pool.workerNodes.length) |
23ff945a JB |
734 | // We need to clean up the resources after our test |
735 | await pool.destroy() | |
736 | }) | |
737 | ||
9e775f96 | 738 | it('Verify FAIR_SHARE strategy can be run in a dynamic pool with median runtime statistic', async () => { |
010d7020 JB |
739 | const pool = new DynamicThreadPool( |
740 | min, | |
741 | max, | |
742 | './tests/worker-files/thread/testWorker.js', | |
743 | { | |
744 | workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE, | |
745 | workerChoiceStrategyOptions: { | |
932fc8be | 746 | runTime: { median: true } |
010d7020 JB |
747 | } |
748 | } | |
749 | ) | |
750 | // TODO: Create a better test to cover `FairShareChoiceStrategy#choose` | |
ee9f5295 | 751 | const promises = new Set() |
010d7020 JB |
752 | const maxMultiplier = 2 |
753 | for (let i = 0; i < max * maxMultiplier; i++) { | |
ee9f5295 | 754 | promises.add(pool.execute()) |
010d7020 | 755 | } |
e211bc18 | 756 | await Promise.all(promises) |
010d7020 | 757 | for (const workerNode of pool.workerNodes) { |
a4e07f72 JB |
758 | expect(workerNode.workerUsage).toStrictEqual({ |
759 | tasks: { | |
760 | executed: maxMultiplier, | |
761 | executing: 0, | |
762 | queued: 0, | |
763 | failed: 0 | |
764 | }, | |
765 | runTime: { | |
932fc8be | 766 | aggregate: expect.any(Number), |
a4e07f72 JB |
767 | average: 0, |
768 | median: expect.any(Number), | |
769 | history: expect.any(CircularArray) | |
770 | }, | |
771 | waitTime: { | |
932fc8be | 772 | aggregate: 0, |
a4e07f72 JB |
773 | average: 0, |
774 | median: 0, | |
775 | history: expect.any(CircularArray) | |
776 | }, | |
d44d5953 | 777 | elu: undefined |
86bf340d | 778 | }) |
932fc8be | 779 | expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0) |
a4e07f72 | 780 | expect(workerNode.workerUsage.runTime.median).toBeGreaterThan(0) |
010d7020 | 781 | } |
2b4fddb8 JB |
782 | expect( |
783 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
784 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
b0d6ed8f | 785 | ).workersVirtualTaskEndTimestamp.length |
2b4fddb8 | 786 | ).toBe(pool.workerNodes.length) |
010d7020 JB |
787 | // We need to clean up the resources after our test |
788 | await pool.destroy() | |
789 | }) | |
790 | ||
a6f7f1b4 | 791 | it('Verify FAIR_SHARE strategy internals are resets after setting it', async () => { |
594bfb84 | 792 | const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE |
f0829c53 | 793 | let pool = new FixedThreadPool( |
caeb9817 JB |
794 | max, |
795 | './tests/worker-files/thread/testWorker.js' | |
796 | ) | |
797 | expect( | |
95c83464 | 798 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
594bfb84 | 799 | workerChoiceStrategy |
b0d6ed8f | 800 | ).workersVirtualTaskEndTimestamp |
08f3f44c JB |
801 | ).toBeInstanceOf(Array) |
802 | expect( | |
803 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
804 | workerChoiceStrategy | |
b0d6ed8f | 805 | ).workersVirtualTaskEndTimestamp.length |
08f3f44c | 806 | ).toBe(0) |
2b4fddb8 JB |
807 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
808 | workerChoiceStrategy | |
b0d6ed8f | 809 | ).workersVirtualTaskEndTimestamp[0] = performance.now() |
2b4fddb8 JB |
810 | expect( |
811 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
812 | workerChoiceStrategy | |
b0d6ed8f | 813 | ).workersVirtualTaskEndTimestamp.length |
2b4fddb8 | 814 | ).toBe(1) |
594bfb84 | 815 | pool.setWorkerChoiceStrategy(workerChoiceStrategy) |
08f3f44c JB |
816 | expect( |
817 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
818 | workerChoiceStrategy | |
b0d6ed8f | 819 | ).workersVirtualTaskEndTimestamp |
08f3f44c | 820 | ).toBeInstanceOf(Array) |
08f3f44c JB |
821 | expect( |
822 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
823 | workerChoiceStrategy | |
b0d6ed8f | 824 | ).workersVirtualTaskEndTimestamp.length |
2b4fddb8 | 825 | ).toBe(0) |
f0829c53 JB |
826 | await pool.destroy() |
827 | pool = new DynamicThreadPool( | |
828 | min, | |
829 | max, | |
830 | './tests/worker-files/thread/testWorker.js' | |
831 | ) | |
832 | expect( | |
95c83464 | 833 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
594bfb84 | 834 | workerChoiceStrategy |
b0d6ed8f | 835 | ).workersVirtualTaskEndTimestamp |
08f3f44c | 836 | ).toBeInstanceOf(Array) |
2b4fddb8 JB |
837 | expect( |
838 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
839 | workerChoiceStrategy | |
b0d6ed8f | 840 | ).workersVirtualTaskEndTimestamp.length |
2b4fddb8 | 841 | ).toBe(0) |
08f3f44c JB |
842 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
843 | workerChoiceStrategy | |
b0d6ed8f | 844 | ).workersVirtualTaskEndTimestamp[0] = performance.now() |
08f3f44c JB |
845 | expect( |
846 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
847 | workerChoiceStrategy | |
b0d6ed8f | 848 | ).workersVirtualTaskEndTimestamp.length |
08f3f44c | 849 | ).toBe(1) |
594bfb84 | 850 | pool.setWorkerChoiceStrategy(workerChoiceStrategy) |
08f3f44c JB |
851 | expect( |
852 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
853 | workerChoiceStrategy | |
b0d6ed8f | 854 | ).workersVirtualTaskEndTimestamp |
08f3f44c JB |
855 | ).toBeInstanceOf(Array) |
856 | expect( | |
857 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
858 | workerChoiceStrategy | |
b0d6ed8f | 859 | ).workersVirtualTaskEndTimestamp.length |
08f3f44c | 860 | ).toBe(0) |
caeb9817 JB |
861 | // We need to clean up the resources after our test |
862 | await pool.destroy() | |
863 | }) | |
864 | ||
10fcfaf4 | 865 | it('Verify WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => { |
594bfb84 | 866 | const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN |
10fcfaf4 JB |
867 | let pool = new FixedThreadPool( |
868 | max, | |
d710242d | 869 | './tests/worker-files/thread/testWorker.js', |
594bfb84 | 870 | { workerChoiceStrategy } |
10fcfaf4 | 871 | ) |
87de9ff5 JB |
872 | expect( |
873 | pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() | |
874 | ).toStrictEqual({ | |
932fc8be JB |
875 | runTime: { |
876 | aggregate: true, | |
877 | average: true, | |
878 | median: false | |
879 | }, | |
880 | waitTime: { | |
881 | aggregate: false, | |
882 | average: false, | |
883 | median: false | |
884 | }, | |
d44d5953 | 885 | elu: false |
86bf340d | 886 | }) |
fd7ebd49 | 887 | await pool.destroy() |
10fcfaf4 JB |
888 | pool = new DynamicThreadPool( |
889 | min, | |
890 | max, | |
d710242d | 891 | './tests/worker-files/thread/testWorker.js', |
594bfb84 | 892 | { workerChoiceStrategy } |
10fcfaf4 | 893 | ) |
87de9ff5 JB |
894 | expect( |
895 | pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() | |
896 | ).toStrictEqual({ | |
932fc8be JB |
897 | runTime: { |
898 | aggregate: true, | |
899 | average: true, | |
900 | median: false | |
901 | }, | |
902 | waitTime: { | |
903 | aggregate: false, | |
904 | average: false, | |
905 | median: false | |
906 | }, | |
d44d5953 | 907 | elu: false |
86bf340d | 908 | }) |
10fcfaf4 JB |
909 | // We need to clean up the resources after our test |
910 | await pool.destroy() | |
911 | }) | |
912 | ||
b3432a63 | 913 | it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => { |
b3432a63 JB |
914 | const pool = new FixedThreadPool( |
915 | max, | |
916 | './tests/worker-files/thread/testWorker.js', | |
917 | { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN } | |
918 | ) | |
919 | // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose` | |
ee9f5295 | 920 | const promises = new Set() |
a20f0ba5 JB |
921 | const maxMultiplier = 2 |
922 | for (let i = 0; i < max * maxMultiplier; i++) { | |
ee9f5295 | 923 | promises.add(pool.execute()) |
b3432a63 | 924 | } |
e211bc18 | 925 | await Promise.all(promises) |
138d29a8 | 926 | for (const workerNode of pool.workerNodes) { |
a4e07f72 JB |
927 | expect(workerNode.workerUsage).toStrictEqual({ |
928 | tasks: { | |
929 | executed: expect.any(Number), | |
930 | executing: 0, | |
931 | queued: 0, | |
932 | failed: 0 | |
933 | }, | |
934 | runTime: { | |
932fc8be | 935 | aggregate: expect.any(Number), |
a4e07f72 JB |
936 | average: expect.any(Number), |
937 | median: 0, | |
938 | history: expect.any(CircularArray) | |
939 | }, | |
940 | waitTime: { | |
932fc8be | 941 | aggregate: 0, |
a4e07f72 JB |
942 | average: 0, |
943 | median: 0, | |
944 | history: expect.any(CircularArray) | |
945 | }, | |
d44d5953 | 946 | elu: undefined |
86bf340d | 947 | }) |
a4e07f72 JB |
948 | expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0) |
949 | expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual( | |
950 | max * maxMultiplier | |
951 | ) | |
932fc8be | 952 | expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThanOrEqual(0) |
a4e07f72 | 953 | expect(workerNode.workerUsage.runTime.average).toBeGreaterThanOrEqual(0) |
138d29a8 | 954 | } |
97a2abc3 | 955 | expect( |
95c83464 | 956 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
d710242d | 957 | pool.workerChoiceStrategyContext.workerChoiceStrategy |
08f3f44c JB |
958 | ).defaultWorkerWeight |
959 | ).toBeGreaterThan(0) | |
960 | expect( | |
961 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
962 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
963 | ).workerVirtualTaskRunTime | |
964 | ).toBeGreaterThanOrEqual(0) | |
b3432a63 JB |
965 | // We need to clean up the resources after our test |
966 | await pool.destroy() | |
967 | }) | |
968 | ||
969 | it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => { | |
b3432a63 JB |
970 | const pool = new DynamicThreadPool( |
971 | min, | |
972 | max, | |
973 | './tests/worker-files/thread/testWorker.js', | |
974 | { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN } | |
975 | ) | |
976 | // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose` | |
ee9f5295 | 977 | const promises = new Set() |
138d29a8 | 978 | const maxMultiplier = 2 |
5502c07c | 979 | for (let i = 0; i < max * maxMultiplier; i++) { |
ee9f5295 | 980 | promises.add(pool.execute()) |
b3432a63 | 981 | } |
e211bc18 | 982 | await Promise.all(promises) |
138d29a8 | 983 | for (const workerNode of pool.workerNodes) { |
a4e07f72 JB |
984 | expect(workerNode.workerUsage).toStrictEqual({ |
985 | tasks: { | |
986 | executed: expect.any(Number), | |
987 | executing: 0, | |
988 | queued: 0, | |
989 | failed: 0 | |
990 | }, | |
991 | runTime: { | |
932fc8be | 992 | aggregate: expect.any(Number), |
a4e07f72 JB |
993 | average: expect.any(Number), |
994 | median: 0, | |
995 | history: expect.any(CircularArray) | |
996 | }, | |
997 | waitTime: { | |
932fc8be | 998 | aggregate: 0, |
a4e07f72 JB |
999 | average: 0, |
1000 | median: 0, | |
1001 | history: expect.any(CircularArray) | |
1002 | }, | |
d44d5953 | 1003 | elu: undefined |
86bf340d | 1004 | }) |
a4e07f72 JB |
1005 | expect(workerNode.workerUsage.tasks.executed).toBeGreaterThan(0) |
1006 | expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual( | |
1007 | max * maxMultiplier | |
1008 | ) | |
932fc8be | 1009 | expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0) |
a4e07f72 | 1010 | expect(workerNode.workerUsage.runTime.average).toBeGreaterThan(0) |
138d29a8 | 1011 | } |
2b4fddb8 JB |
1012 | expect( |
1013 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1014 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1015 | ).defaultWorkerWeight | |
1016 | ).toBeGreaterThan(0) | |
1017 | expect( | |
1018 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1019 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1020 | ).workerVirtualTaskRunTime | |
1021 | ).toBeGreaterThanOrEqual(0) | |
b3432a63 JB |
1022 | // We need to clean up the resources after our test |
1023 | await pool.destroy() | |
1024 | }) | |
1025 | ||
9e775f96 | 1026 | it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool with median runtime statistic', async () => { |
010d7020 JB |
1027 | const pool = new DynamicThreadPool( |
1028 | min, | |
1029 | max, | |
1030 | './tests/worker-files/thread/testWorker.js', | |
1031 | { | |
1032 | workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN, | |
1033 | workerChoiceStrategyOptions: { | |
932fc8be | 1034 | runTime: { median: true } |
010d7020 JB |
1035 | } |
1036 | } | |
1037 | ) | |
1038 | // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose` | |
ee9f5295 | 1039 | const promises = new Set() |
010d7020 JB |
1040 | const maxMultiplier = 2 |
1041 | for (let i = 0; i < max * maxMultiplier; i++) { | |
ee9f5295 | 1042 | promises.add(pool.execute()) |
010d7020 | 1043 | } |
e211bc18 | 1044 | await Promise.all(promises) |
010d7020 | 1045 | for (const workerNode of pool.workerNodes) { |
a4e07f72 JB |
1046 | expect(workerNode.workerUsage).toStrictEqual({ |
1047 | tasks: { | |
1048 | executed: expect.any(Number), | |
1049 | executing: 0, | |
1050 | queued: 0, | |
1051 | failed: 0 | |
1052 | }, | |
1053 | runTime: { | |
932fc8be | 1054 | aggregate: expect.any(Number), |
a4e07f72 JB |
1055 | average: 0, |
1056 | median: expect.any(Number), | |
1057 | history: expect.any(CircularArray) | |
1058 | }, | |
1059 | waitTime: { | |
932fc8be | 1060 | aggregate: 0, |
a4e07f72 JB |
1061 | average: 0, |
1062 | median: 0, | |
1063 | history: expect.any(CircularArray) | |
1064 | }, | |
d44d5953 | 1065 | elu: undefined |
86bf340d | 1066 | }) |
a4e07f72 JB |
1067 | expect(workerNode.workerUsage.tasks.executed).toBeGreaterThan(0) |
1068 | expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual( | |
1069 | max * maxMultiplier | |
1070 | ) | |
932fc8be | 1071 | expect(workerNode.workerUsage.runTime.aggregate).toBeGreaterThan(0) |
a4e07f72 | 1072 | expect(workerNode.workerUsage.runTime.median).toBeGreaterThan(0) |
010d7020 | 1073 | } |
08f3f44c JB |
1074 | expect( |
1075 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1076 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1077 | ).defaultWorkerWeight | |
1078 | ).toBeGreaterThan(0) | |
1079 | expect( | |
1080 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1081 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1082 | ).workerVirtualTaskRunTime | |
1083 | ).toBeGreaterThanOrEqual(0) | |
010d7020 JB |
1084 | // We need to clean up the resources after our test |
1085 | await pool.destroy() | |
1086 | }) | |
1087 | ||
a6f7f1b4 | 1088 | it('Verify WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => { |
594bfb84 | 1089 | const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN |
f0829c53 | 1090 | let pool = new FixedThreadPool( |
caeb9817 JB |
1091 | max, |
1092 | './tests/worker-files/thread/testWorker.js' | |
1093 | ) | |
38f6e859 | 1094 | expect( |
95c83464 | 1095 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
594bfb84 | 1096 | workerChoiceStrategy |
f06e48d8 | 1097 | ).currentWorkerNodeId |
b529c323 | 1098 | ).toBeDefined() |
38f6e859 | 1099 | expect( |
95c83464 | 1100 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
594bfb84 | 1101 | workerChoiceStrategy |
b529c323 JB |
1102 | ).defaultWorkerWeight |
1103 | ).toBeDefined() | |
caeb9817 | 1104 | expect( |
95c83464 | 1105 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
594bfb84 | 1106 | workerChoiceStrategy |
08f3f44c | 1107 | ).workerVirtualTaskRunTime |
b529c323 | 1108 | ).toBeDefined() |
594bfb84 | 1109 | pool.setWorkerChoiceStrategy(workerChoiceStrategy) |
a6f7f1b4 | 1110 | expect( |
95c83464 | 1111 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
d710242d | 1112 | pool.workerChoiceStrategyContext.workerChoiceStrategy |
f06e48d8 | 1113 | ).currentWorkerNodeId |
a6f7f1b4 JB |
1114 | ).toBe(0) |
1115 | expect( | |
95c83464 | 1116 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
d710242d | 1117 | pool.workerChoiceStrategyContext.workerChoiceStrategy |
95c83464 | 1118 | ).defaultWorkerWeight |
a6f7f1b4 | 1119 | ).toBeGreaterThan(0) |
08f3f44c JB |
1120 | expect( |
1121 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1122 | workerChoiceStrategy | |
1123 | ).workerVirtualTaskRunTime | |
1124 | ).toBe(0) | |
f0829c53 JB |
1125 | await pool.destroy() |
1126 | pool = new DynamicThreadPool( | |
1127 | min, | |
1128 | max, | |
1129 | './tests/worker-files/thread/testWorker.js' | |
1130 | ) | |
38f6e859 | 1131 | expect( |
95c83464 | 1132 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
594bfb84 | 1133 | workerChoiceStrategy |
f06e48d8 | 1134 | ).currentWorkerNodeId |
b529c323 | 1135 | ).toBeDefined() |
38f6e859 | 1136 | expect( |
95c83464 | 1137 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
594bfb84 | 1138 | workerChoiceStrategy |
b529c323 JB |
1139 | ).defaultWorkerWeight |
1140 | ).toBeDefined() | |
f0829c53 | 1141 | expect( |
95c83464 | 1142 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
594bfb84 | 1143 | workerChoiceStrategy |
08f3f44c | 1144 | ).workerVirtualTaskRunTime |
b529c323 | 1145 | ).toBeDefined() |
594bfb84 | 1146 | pool.setWorkerChoiceStrategy(workerChoiceStrategy) |
a6f7f1b4 | 1147 | expect( |
95c83464 | 1148 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
d710242d | 1149 | pool.workerChoiceStrategyContext.workerChoiceStrategy |
f06e48d8 | 1150 | ).currentWorkerNodeId |
a6f7f1b4 JB |
1151 | ).toBe(0) |
1152 | expect( | |
95c83464 | 1153 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( |
d710242d | 1154 | pool.workerChoiceStrategyContext.workerChoiceStrategy |
95c83464 | 1155 | ).defaultWorkerWeight |
a6f7f1b4 | 1156 | ).toBeGreaterThan(0) |
08f3f44c JB |
1157 | expect( |
1158 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1159 | workerChoiceStrategy | |
1160 | ).workerVirtualTaskRunTime | |
1161 | ).toBe(0) | |
caeb9817 JB |
1162 | // We need to clean up the resources after our test |
1163 | await pool.destroy() | |
1164 | }) | |
1165 | ||
e52fb978 JB |
1166 | it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => { |
1167 | const workerChoiceStrategy = | |
1168 | WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN | |
1169 | let pool = new FixedThreadPool( | |
1170 | max, | |
1171 | './tests/worker-files/thread/testWorker.js', | |
1172 | { workerChoiceStrategy } | |
1173 | ) | |
87de9ff5 JB |
1174 | expect( |
1175 | pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() | |
1176 | ).toStrictEqual({ | |
932fc8be JB |
1177 | runTime: { |
1178 | aggregate: false, | |
1179 | average: false, | |
1180 | median: false | |
1181 | }, | |
1182 | waitTime: { | |
1183 | aggregate: false, | |
1184 | average: false, | |
1185 | median: false | |
1186 | }, | |
d44d5953 | 1187 | elu: false |
e52fb978 JB |
1188 | }) |
1189 | await pool.destroy() | |
1190 | pool = new DynamicThreadPool( | |
1191 | min, | |
1192 | max, | |
1193 | './tests/worker-files/thread/testWorker.js', | |
1194 | { workerChoiceStrategy } | |
1195 | ) | |
87de9ff5 JB |
1196 | expect( |
1197 | pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() | |
1198 | ).toStrictEqual({ | |
932fc8be JB |
1199 | runTime: { |
1200 | aggregate: false, | |
1201 | average: false, | |
1202 | median: false | |
1203 | }, | |
1204 | waitTime: { | |
1205 | aggregate: false, | |
1206 | average: false, | |
1207 | median: false | |
1208 | }, | |
d44d5953 | 1209 | elu: false |
e52fb978 JB |
1210 | }) |
1211 | // We need to clean up the resources after our test | |
1212 | await pool.destroy() | |
1213 | }) | |
1214 | ||
e62e7646 JB |
1215 | it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => { |
1216 | const pool = new FixedThreadPool( | |
1217 | max, | |
1218 | './tests/worker-files/thread/testWorker.js', | |
1219 | { | |
1220 | workerChoiceStrategy: | |
1221 | WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN | |
1222 | } | |
1223 | ) | |
1224 | // TODO: Create a better test to cover `InterleavedWeightedRoundRobinWorkerChoiceStrategy#choose` | |
1225 | const promises = new Set() | |
1226 | const maxMultiplier = 2 | |
1227 | for (let i = 0; i < max * maxMultiplier; i++) { | |
1228 | promises.add(pool.execute()) | |
1229 | } | |
1230 | await Promise.all(promises) | |
1231 | for (const workerNode of pool.workerNodes) { | |
a4e07f72 JB |
1232 | expect(workerNode.workerUsage).toStrictEqual({ |
1233 | tasks: { | |
1234 | executed: maxMultiplier, | |
1235 | executing: 0, | |
1236 | queued: 0, | |
1237 | failed: 0 | |
1238 | }, | |
1239 | runTime: { | |
932fc8be | 1240 | aggregate: 0, |
a4e07f72 JB |
1241 | average: 0, |
1242 | median: 0, | |
1243 | history: expect.any(CircularArray) | |
1244 | }, | |
1245 | waitTime: { | |
932fc8be | 1246 | aggregate: 0, |
a4e07f72 JB |
1247 | average: 0, |
1248 | median: 0, | |
1249 | history: expect.any(CircularArray) | |
1250 | }, | |
d44d5953 | 1251 | elu: undefined |
e62e7646 JB |
1252 | }) |
1253 | } | |
1254 | expect( | |
1255 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1256 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1257 | ).defaultWorkerWeight | |
1258 | ).toBeGreaterThan(0) | |
1259 | expect( | |
1260 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1261 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1262 | ).currentRoundId | |
1263 | ).toBe(0) | |
1264 | expect( | |
1265 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1266 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1267 | ).currentWorkerNodeId | |
1268 | ).toBe(0) | |
1269 | expect( | |
1270 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1271 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1272 | ).roundWeights | |
1273 | ).toStrictEqual([ | |
1274 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1275 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1276 | ).defaultWorkerWeight | |
1277 | ]) | |
1278 | // We need to clean up the resources after our test | |
1279 | await pool.destroy() | |
1280 | }) | |
1281 | ||
1282 | it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => { | |
1283 | const pool = new DynamicThreadPool( | |
1284 | min, | |
1285 | max, | |
1286 | './tests/worker-files/thread/testWorker.js', | |
1287 | { | |
1288 | workerChoiceStrategy: | |
1289 | WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN | |
1290 | } | |
1291 | ) | |
1292 | // TODO: Create a better test to cover `InterleavedWeightedRoundRobinWorkerChoiceStrategy#choose` | |
1293 | const promises = new Set() | |
1294 | const maxMultiplier = 2 | |
1295 | for (let i = 0; i < max * maxMultiplier; i++) { | |
1296 | promises.add(pool.execute()) | |
1297 | } | |
1298 | await Promise.all(promises) | |
1299 | for (const workerNode of pool.workerNodes) { | |
a4e07f72 JB |
1300 | expect(workerNode.workerUsage).toStrictEqual({ |
1301 | tasks: { | |
1302 | executed: maxMultiplier, | |
1303 | executing: 0, | |
1304 | queued: 0, | |
1305 | failed: 0 | |
1306 | }, | |
1307 | runTime: { | |
932fc8be | 1308 | aggregate: 0, |
a4e07f72 JB |
1309 | average: 0, |
1310 | median: 0, | |
1311 | history: expect.any(CircularArray) | |
1312 | }, | |
1313 | waitTime: { | |
932fc8be | 1314 | aggregate: 0, |
a4e07f72 JB |
1315 | average: 0, |
1316 | median: 0, | |
1317 | history: expect.any(CircularArray) | |
1318 | }, | |
d44d5953 | 1319 | elu: undefined |
e62e7646 JB |
1320 | }) |
1321 | } | |
1322 | expect( | |
1323 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1324 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1325 | ).defaultWorkerWeight | |
1326 | ).toBeGreaterThan(0) | |
1327 | expect( | |
1328 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1329 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1330 | ).currentRoundId | |
1331 | ).toBe(0) | |
1332 | expect( | |
1333 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1334 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1335 | ).currentWorkerNodeId | |
1336 | ).toBe(0) | |
1337 | expect( | |
1338 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1339 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1340 | ).roundWeights | |
1341 | ).toStrictEqual([ | |
1342 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1343 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1344 | ).defaultWorkerWeight | |
1345 | ]) | |
1346 | // We need to clean up the resources after our test | |
1347 | await pool.destroy() | |
1348 | }) | |
1349 | ||
8c3ec188 JB |
1350 | it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => { |
1351 | const workerChoiceStrategy = | |
1352 | WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN | |
1353 | let pool = new FixedThreadPool( | |
1354 | max, | |
1355 | './tests/worker-files/thread/testWorker.js' | |
1356 | ) | |
1357 | expect( | |
1358 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1359 | workerChoiceStrategy | |
1360 | ).currentRoundId | |
1361 | ).toBeDefined() | |
1362 | expect( | |
1363 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1364 | workerChoiceStrategy | |
1365 | ).currentWorkerNodeId | |
1366 | ).toBeDefined() | |
1367 | expect( | |
1368 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1369 | workerChoiceStrategy | |
1370 | ).defaultWorkerWeight | |
1371 | ).toBeDefined() | |
1372 | expect( | |
1373 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1374 | workerChoiceStrategy | |
1375 | ).roundWeights | |
1376 | ).toBeDefined() | |
1377 | pool.setWorkerChoiceStrategy(workerChoiceStrategy) | |
1378 | expect( | |
1379 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1380 | workerChoiceStrategy | |
1381 | ).currentRoundId | |
1382 | ).toBe(0) | |
1383 | expect( | |
1384 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1385 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1386 | ).currentWorkerNodeId | |
1387 | ).toBe(0) | |
1388 | expect( | |
1389 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1390 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1391 | ).defaultWorkerWeight | |
1392 | ).toBeGreaterThan(0) | |
1393 | expect( | |
1394 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1395 | workerChoiceStrategy | |
1396 | ).roundWeights | |
1397 | ).toStrictEqual([ | |
1398 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1399 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1400 | ).defaultWorkerWeight | |
1401 | ]) | |
1402 | await pool.destroy() | |
1403 | pool = new DynamicThreadPool( | |
1404 | min, | |
1405 | max, | |
1406 | './tests/worker-files/thread/testWorker.js' | |
1407 | ) | |
1408 | expect( | |
1409 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1410 | workerChoiceStrategy | |
1411 | ).currentRoundId | |
1412 | ).toBeDefined() | |
1413 | expect( | |
1414 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1415 | workerChoiceStrategy | |
1416 | ).currentWorkerNodeId | |
1417 | ).toBeDefined() | |
1418 | expect( | |
1419 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1420 | workerChoiceStrategy | |
1421 | ).defaultWorkerWeight | |
1422 | ).toBeDefined() | |
1423 | expect( | |
1424 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1425 | workerChoiceStrategy | |
1426 | ).roundWeights | |
1427 | ).toBeDefined() | |
1428 | pool.setWorkerChoiceStrategy(workerChoiceStrategy) | |
1429 | expect( | |
1430 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1431 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1432 | ).currentWorkerNodeId | |
1433 | ).toBe(0) | |
1434 | expect( | |
1435 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1436 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1437 | ).defaultWorkerWeight | |
1438 | ).toBeGreaterThan(0) | |
1439 | expect( | |
1440 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1441 | workerChoiceStrategy | |
1442 | ).roundWeights | |
1443 | ).toStrictEqual([ | |
1444 | pool.workerChoiceStrategyContext.workerChoiceStrategies.get( | |
1445 | pool.workerChoiceStrategyContext.workerChoiceStrategy | |
1446 | ).defaultWorkerWeight | |
1447 | ]) | |
1448 | // We need to clean up the resources after our test | |
1449 | await pool.destroy() | |
1450 | }) | |
1451 | ||
89b09b26 | 1452 | it('Verify unknown strategy throw error', () => { |
a35560ba S |
1453 | expect( |
1454 | () => | |
1455 | new DynamicThreadPool( | |
1456 | min, | |
1457 | max, | |
1458 | './tests/worker-files/thread/testWorker.js', | |
1927ee67 | 1459 | { workerChoiceStrategy: 'UNKNOWN_STRATEGY' } |
a35560ba | 1460 | ) |
d4aeae5a | 1461 | ).toThrowError("Invalid worker choice strategy 'UNKNOWN_STRATEGY'") |
a35560ba S |
1462 | }) |
1463 | }) |