Fixes to worker selection strategies
[poolifier.git] / tests / pools / selection-strategies / selection-strategies.test.js
1 const { expect } = require('expect')
2 const {
3 WorkerChoiceStrategies,
4 DynamicThreadPool,
5 FixedThreadPool
6 } = require('../../../lib/index')
7
8 describe('Selection strategies test suite', () => {
9 const min = 0
10 const max = 3
11
12 it('Verify that WorkerChoiceStrategies enumeration provides string values', () => {
13 expect(WorkerChoiceStrategies.ROUND_ROBIN).toBe('ROUND_ROBIN')
14 expect(WorkerChoiceStrategies.LESS_RECENTLY_USED).toBe('LESS_RECENTLY_USED')
15 expect(WorkerChoiceStrategies.FAIR_SHARE).toBe('FAIR_SHARE')
16 expect(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN).toBe(
17 'WEIGHTED_ROUND_ROBIN'
18 )
19 })
20
21 it('Verify ROUND_ROBIN strategy is the default at pool creation', async () => {
22 const pool = new DynamicThreadPool(
23 min,
24 max,
25 './tests/worker-files/thread/testWorker.js'
26 )
27 expect(pool.opts.workerChoiceStrategy).toBe(
28 WorkerChoiceStrategies.ROUND_ROBIN
29 )
30 // We need to clean up the resources after our test
31 await pool.destroy()
32 })
33
34 it('Verify ROUND_ROBIN strategy is taken at pool creation', async () => {
35 const pool = new FixedThreadPool(
36 max,
37 './tests/worker-files/thread/testWorker.js',
38 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
39 )
40 expect(pool.opts.workerChoiceStrategy).toBe(
41 WorkerChoiceStrategies.ROUND_ROBIN
42 )
43 expect(
44 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy().nextWorkerIndex
45 ).toBe(0)
46 // We need to clean up the resources after our test
47 await pool.destroy()
48 })
49
50 it('Verify ROUND_ROBIN strategy can be set after pool creation', async () => {
51 const pool = new DynamicThreadPool(
52 min,
53 max,
54 './tests/worker-files/thread/testWorker.js'
55 )
56 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.ROUND_ROBIN)
57 expect(pool.opts.workerChoiceStrategy).toBe(
58 WorkerChoiceStrategies.ROUND_ROBIN
59 )
60 // We need to clean up the resources after our test
61 await pool.destroy()
62 })
63
64 it('Verify ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
65 let pool = new FixedThreadPool(
66 max,
67 './tests/worker-files/thread/testWorker.js'
68 )
69 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.ROUND_ROBIN)
70 expect(
71 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
72 .requiredStatistics.runTime
73 ).toBe(false)
74 await pool.destroy()
75 pool = new DynamicThreadPool(
76 min,
77 max,
78 './tests/worker-files/thread/testWorker.js'
79 )
80 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.ROUND_ROBIN)
81 expect(
82 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
83 .requiredStatistics.runTime
84 ).toBe(false)
85 // We need to clean up the resources after our test
86 await pool.destroy()
87 })
88
89 it('Verify ROUND_ROBIN strategy can be run in a fixed pool', async () => {
90 const pool = new FixedThreadPool(
91 max,
92 './tests/worker-files/thread/testWorker.js',
93 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
94 )
95 expect(pool.opts.workerChoiceStrategy).toBe(
96 WorkerChoiceStrategies.ROUND_ROBIN
97 )
98 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
99 const promises = []
100 for (let i = 0; i < max * 2; i++) {
101 promises.push(pool.execute())
102 }
103 await Promise.all(promises)
104 // We need to clean up the resources after our test
105 await pool.destroy()
106 })
107
108 it('Verify ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
109 const pool = new DynamicThreadPool(
110 min,
111 max,
112 './tests/worker-files/thread/testWorker.js',
113 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
114 )
115 expect(pool.opts.workerChoiceStrategy).toBe(
116 WorkerChoiceStrategies.ROUND_ROBIN
117 )
118 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
119 const promises = []
120 for (let i = 0; i < max * 2; i++) {
121 promises.push(pool.execute())
122 }
123 await Promise.all(promises)
124 // We need to clean up the resources after our test
125 await pool.destroy()
126 })
127
128 it('Verify ROUND_ROBIN strategy internals are resets after setting it', async () => {
129 let pool = new FixedThreadPool(
130 max,
131 './tests/worker-files/thread/testWorker.js',
132 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
133 )
134 expect(
135 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy().nextWorkerIndex
136 ).toBeUndefined()
137 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.ROUND_ROBIN)
138 expect(
139 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy().nextWorkerIndex
140 ).toBe(0)
141 await pool.destroy()
142 pool = new DynamicThreadPool(
143 min,
144 max,
145 './tests/worker-files/thread/testWorker.js',
146 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
147 )
148 expect(
149 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
150 .workerChoiceStrategy.nextWorkerIndex
151 ).toBeUndefined()
152 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.ROUND_ROBIN)
153 expect(
154 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
155 .workerChoiceStrategy.nextWorkerIndex
156 ).toBe(0)
157 // We need to clean up the resources after our test
158 await pool.destroy()
159 })
160
161 it('Verify LESS_RECENTLY_USED strategy is taken at pool creation', async () => {
162 const pool = new FixedThreadPool(
163 max,
164 './tests/worker-files/thread/testWorker.js',
165 { workerChoiceStrategy: WorkerChoiceStrategies.LESS_RECENTLY_USED }
166 )
167 expect(pool.opts.workerChoiceStrategy).toBe(
168 WorkerChoiceStrategies.LESS_RECENTLY_USED
169 )
170 // We need to clean up the resources after our test
171 await pool.destroy()
172 })
173
174 it('Verify LESS_RECENTLY_USED strategy can be set after pool creation', async () => {
175 const pool = new FixedThreadPool(
176 max,
177 './tests/worker-files/thread/testWorker.js'
178 )
179 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.LESS_RECENTLY_USED)
180 expect(pool.opts.workerChoiceStrategy).toBe(
181 WorkerChoiceStrategies.LESS_RECENTLY_USED
182 )
183 // We need to clean up the resources after our test
184 await pool.destroy()
185 })
186
187 it('Verify LESS_RECENTLY_USED strategy default tasks usage statistics requirements', async () => {
188 let pool = new FixedThreadPool(
189 max,
190 './tests/worker-files/thread/testWorker.js'
191 )
192 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.LESS_RECENTLY_USED)
193 expect(
194 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
195 .requiredStatistics.runTime
196 ).toBe(false)
197 await pool.destroy()
198 pool = new DynamicThreadPool(
199 min,
200 max,
201 './tests/worker-files/thread/testWorker.js'
202 )
203 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.LESS_RECENTLY_USED)
204 expect(
205 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
206 .requiredStatistics.runTime
207 ).toBe(false)
208 // We need to clean up the resources after our test
209 await pool.destroy()
210 })
211
212 it('Verify LESS_RECENTLY_USED strategy can be run in a fixed pool', async () => {
213 const pool = new FixedThreadPool(
214 max,
215 './tests/worker-files/thread/testWorker.js',
216 { workerChoiceStrategy: WorkerChoiceStrategies.LESS_RECENTLY_USED }
217 )
218 // TODO: Create a better test to cover `LessRecentlyUsedWorkerChoiceStrategy#choose`
219 const promises = []
220 for (let i = 0; i < max * 2; i++) {
221 promises.push(pool.execute())
222 }
223 await Promise.all(promises)
224 // We need to clean up the resources after our test
225 await pool.destroy()
226 })
227
228 it('Verify LESS_RECENTLY_USED strategy can be run in a dynamic pool', async () => {
229 const pool = new DynamicThreadPool(
230 min,
231 max,
232 './tests/worker-files/thread/testWorker.js',
233 { workerChoiceStrategy: WorkerChoiceStrategies.LESS_RECENTLY_USED }
234 )
235 // TODO: Create a better test to cover `LessRecentlyUsedWorkerChoiceStrategy#choose`
236 const promises = []
237 for (let i = 0; i < max * 2; i++) {
238 promises.push(pool.execute())
239 }
240 await Promise.all(promises)
241 // We need to clean up the resources after our test
242 await pool.destroy()
243 })
244
245 it('Verify FAIR_SHARE strategy is taken at pool creation', async () => {
246 const pool = new FixedThreadPool(
247 max,
248 './tests/worker-files/thread/testWorker.js',
249 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
250 )
251 expect(pool.opts.workerChoiceStrategy).toBe(
252 WorkerChoiceStrategies.FAIR_SHARE
253 )
254 for (const worker of pool.workerChoiceStrategyContext
255 .getWorkerChoiceStrategy()
256 .workerLastVirtualTaskTimestamp.keys()) {
257 expect(
258 pool.workerChoiceStrategyContext
259 .getWorkerChoiceStrategy()
260 .workerLastVirtualTaskTimestamp.get(worker).start
261 ).toBe(0)
262 expect(
263 pool.workerChoiceStrategyContext
264 .getWorkerChoiceStrategy()
265 .workerLastVirtualTaskTimestamp.get(worker).end
266 ).toBe(0)
267 }
268 // We need to clean up the resources after our test
269 await pool.destroy()
270 })
271
272 it('Verify FAIR_SHARE strategy can be set after pool creation', async () => {
273 const pool = new FixedThreadPool(
274 max,
275 './tests/worker-files/thread/testWorker.js'
276 )
277 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.FAIR_SHARE)
278 expect(pool.opts.workerChoiceStrategy).toBe(
279 WorkerChoiceStrategies.FAIR_SHARE
280 )
281 // We need to clean up the resources after our test
282 await pool.destroy()
283 })
284
285 it('Verify FAIR_SHARE strategy default tasks usage statistics requirements', async () => {
286 let pool = new FixedThreadPool(
287 max,
288 './tests/worker-files/thread/testWorker.js'
289 )
290 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.FAIR_SHARE)
291 expect(
292 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
293 .requiredStatistics.runTime
294 ).toBe(true)
295 await pool.destroy()
296 pool = new DynamicThreadPool(
297 min,
298 max,
299 './tests/worker-files/thread/testWorker.js'
300 )
301 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.FAIR_SHARE)
302 expect(
303 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
304 .requiredStatistics.runTime
305 ).toBe(true)
306 // We need to clean up the resources after our test
307 await pool.destroy()
308 })
309
310 it('Verify FAIR_SHARE strategy can be run in a fixed pool', async () => {
311 const pool = new FixedThreadPool(
312 max,
313 './tests/worker-files/thread/testWorker.js',
314 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
315 )
316 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
317 const promises = []
318 for (let i = 0; i < max * 2; i++) {
319 promises.push(pool.execute())
320 }
321 await Promise.all(promises)
322 // We need to clean up the resources after our test
323 await pool.destroy()
324 })
325
326 it('Verify FAIR_SHARE strategy can be run in a dynamic pool', async () => {
327 const pool = new DynamicThreadPool(
328 min,
329 max,
330 './tests/worker-files/thread/testWorker.js',
331 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
332 )
333 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
334 const promises = []
335 for (let i = 0; i < max * 2; i++) {
336 promises.push(pool.execute())
337 }
338 await Promise.all(promises)
339 // We need to clean up the resources after our test
340 await pool.destroy()
341 })
342
343 it('Verify FAIR_SHARE strategy internals are resets after setting it', async () => {
344 let pool = new FixedThreadPool(
345 max,
346 './tests/worker-files/thread/testWorker.js'
347 )
348 expect(
349 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
350 .workerLastVirtualTaskTimestamp
351 ).toBeUndefined()
352 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.FAIR_SHARE)
353 for (const worker of pool.workerChoiceStrategyContext
354 .getWorkerChoiceStrategy()
355 .workerLastVirtualTaskTimestamp.keys()) {
356 expect(
357 pool.workerChoiceStrategyContext
358 .getWorkerChoiceStrategy()
359 .workerLastVirtualTaskTimestamp.get(worker).start
360 ).toBe(0)
361 expect(
362 pool.workerChoiceStrategyContext
363 .getWorkerChoiceStrategy()
364 .workerLastVirtualTaskTimestamp.get(worker).end
365 ).toBe(0)
366 }
367 await pool.destroy()
368 pool = new DynamicThreadPool(
369 min,
370 max,
371 './tests/worker-files/thread/testWorker.js'
372 )
373 expect(
374 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
375 .workerChoiceStrategy.workerLastVirtualTaskTimestamp
376 ).toBeUndefined()
377 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.FAIR_SHARE)
378 for (const worker of pool.workerChoiceStrategyContext
379 .getWorkerChoiceStrategy()
380 .workerChoiceStrategy.workerLastVirtualTaskTimestamp.keys()) {
381 expect(
382 pool.workerChoiceStrategyContext
383 .getWorkerChoiceStrategy()
384 .workerChoiceStrategy.workerLastVirtualTaskTimestamp.get(worker).start
385 ).toBe(0)
386 expect(
387 pool.workerChoiceStrategyContext
388 .getWorkerChoiceStrategy()
389 .workerChoiceStrategy.workerLastVirtualTaskTimestamp.get(worker).end
390 ).toBe(0)
391 }
392 // We need to clean up the resources after our test
393 await pool.destroy()
394 })
395
396 it('Verify WEIGHTED_ROUND_ROBIN strategy is taken at pool creation', async () => {
397 const pool = new FixedThreadPool(
398 max,
399 './tests/worker-files/thread/testWorker.js',
400 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
401 )
402 expect(pool.opts.workerChoiceStrategy).toBe(
403 WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
404 )
405 expect(
406 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
407 .currentWorkerIndex
408 ).toBe(0)
409 expect(
410 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
411 .defaultWorkerWeight
412 ).toBeGreaterThan(0)
413 for (const worker of pool.workerChoiceStrategyContext
414 .getWorkerChoiceStrategy()
415 .workersTaskRunTime.keys()) {
416 expect(
417 pool.workerChoiceStrategyContext
418 .getWorkerChoiceStrategy()
419 .workersTaskRunTime.get(worker).weight
420 ).toBeGreaterThan(0)
421 expect(
422 pool.workerChoiceStrategyContext
423 .getWorkerChoiceStrategy()
424 .workersTaskRunTime.get(worker).runTime
425 ).toBe(0)
426 }
427 // We need to clean up the resources after our test
428 await pool.destroy()
429 })
430
431 it('Verify WEIGHTED_ROUND_ROBIN strategy can be set after pool creation', async () => {
432 const pool = new FixedThreadPool(
433 max,
434 './tests/worker-files/thread/testWorker.js'
435 )
436 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN)
437 expect(pool.opts.workerChoiceStrategy).toBe(
438 WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
439 )
440 // We need to clean up the resources after our test
441 await pool.destroy()
442 })
443
444 it('Verify WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
445 let pool = new FixedThreadPool(
446 max,
447 './tests/worker-files/thread/testWorker.js'
448 )
449 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN)
450 expect(
451 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
452 .requiredStatistics.runTime
453 ).toBe(true)
454 await pool.destroy()
455 pool = new DynamicThreadPool(
456 min,
457 max,
458 './tests/worker-files/thread/testWorker.js'
459 )
460 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN)
461 expect(
462 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
463 .requiredStatistics.runTime
464 ).toBe(true)
465 // We need to clean up the resources after our test
466 await pool.destroy()
467 })
468
469 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
470 const pool = new FixedThreadPool(
471 max,
472 './tests/worker-files/thread/testWorker.js',
473 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
474 )
475 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
476 const promises = []
477 for (let i = 0; i < max * 2; i++) {
478 promises.push(pool.execute())
479 }
480 await Promise.all(promises)
481 // We need to clean up the resources after our test
482 await pool.destroy()
483 })
484
485 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
486 const pool = new DynamicThreadPool(
487 min,
488 max,
489 './tests/worker-files/thread/testWorker.js',
490 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
491 )
492 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
493 const promises = []
494 for (let i = 0; i < max * 2; i++) {
495 promises.push(pool.execute())
496 }
497 await Promise.all(promises)
498 // We need to clean up the resources after our test
499 await pool.destroy()
500 })
501
502 it('Verify WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
503 let pool = new FixedThreadPool(
504 max,
505 './tests/worker-files/thread/testWorker.js'
506 )
507 expect(
508 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
509 .currentWorkerIndex
510 ).toBeUndefined()
511 expect(
512 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
513 .defaultWorkerWeight
514 ).toBeUndefined()
515 expect(
516 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
517 .workersTaskRunTime
518 ).toBeUndefined()
519 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN)
520 expect(
521 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
522 .currentWorkerIndex
523 ).toBe(0)
524 expect(
525 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
526 .defaultWorkerWeight
527 ).toBeGreaterThan(0)
528 for (const worker of pool.workerChoiceStrategyContext
529 .getWorkerChoiceStrategy()
530 .workersTaskRunTime.keys()) {
531 expect(
532 pool.workerChoiceStrategyContext
533 .getWorkerChoiceStrategy()
534 .workersTaskRunTime.get(worker).runTime
535 ).toBe(0)
536 }
537 await pool.destroy()
538 pool = new DynamicThreadPool(
539 min,
540 max,
541 './tests/worker-files/thread/testWorker.js'
542 )
543 expect(
544 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
545 .workerChoiceStrategy.currentWorkerIndex
546 ).toBeUndefined()
547 expect(
548 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
549 .workerChoiceStrategy.defaultWorkerWeight
550 ).toBeUndefined()
551 expect(
552 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
553 .workerChoiceStrategy.workersTaskRunTime
554 ).toBeUndefined()
555 pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN)
556 expect(
557 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
558 .workerChoiceStrategy.currentWorkerIndex
559 ).toBe(0)
560 expect(
561 pool.workerChoiceStrategyContext.getWorkerChoiceStrategy()
562 .workerChoiceStrategy.defaultWorkerWeight
563 ).toBeGreaterThan(0)
564 for (const worker of pool.workerChoiceStrategyContext
565 .getWorkerChoiceStrategy()
566 .workerChoiceStrategy.workersTaskRunTime.keys()) {
567 expect(
568 pool.workerChoiceStrategyContext
569 .getWorkerChoiceStrategy()
570 .workerChoiceStrategy.workersTaskRunTime.get(worker).runTime
571 ).toBe(0)
572 }
573 // We need to clean up the resources after our test
574 await pool.destroy()
575 })
576
577 it('Verify unknown strategies throw error', () => {
578 expect(
579 () =>
580 new DynamicThreadPool(
581 min,
582 max,
583 './tests/worker-files/thread/testWorker.js',
584 { workerChoiceStrategy: 'UNKNOWN_STRATEGY' }
585 )
586 ).toThrowError(
587 new Error("Worker choice strategy 'UNKNOWN_STRATEGY' not found")
588 )
589 })
590 })