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