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