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