perf: drastically reduce worker nodes array lookups
[poolifier.git] / tests / pools / selection-strategies / worker-choice-strategy-context.test.js
1 const { expect } = require('expect')
2 const sinon = require('sinon')
3 const {
4 FixedThreadPool,
5 DynamicThreadPool,
6 WorkerChoiceStrategies
7 } = require('../../../lib')
8 const {
9 WorkerChoiceStrategyContext
10 } = require('../../../lib/pools/selection-strategies/worker-choice-strategy-context')
11 const {
12 RoundRobinWorkerChoiceStrategy
13 } = require('../../../lib/pools/selection-strategies/round-robin-worker-choice-strategy')
14 const {
15 LeastUsedWorkerChoiceStrategy
16 } = require('../../../lib/pools/selection-strategies/least-used-worker-choice-strategy')
17 const {
18 LeastBusyWorkerChoiceStrategy
19 } = require('../../../lib/pools/selection-strategies/least-busy-worker-choice-strategy')
20 const {
21 LeastEluWorkerChoiceStrategy
22 } = require('../../../lib/pools/selection-strategies/least-elu-worker-choice-strategy')
23 const {
24 FairShareWorkerChoiceStrategy
25 } = require('../../../lib/pools/selection-strategies/fair-share-worker-choice-strategy')
26 const {
27 WeightedRoundRobinWorkerChoiceStrategy
28 } = require('../../../lib/pools/selection-strategies/weighted-round-robin-worker-choice-strategy')
29 const {
30 InterleavedWeightedRoundRobinWorkerChoiceStrategy
31 } = require('../../../lib/pools/selection-strategies/interleaved-weighted-round-robin-worker-choice-strategy')
32
33 describe('Worker choice strategy context test suite', () => {
34 const min = 1
35 const max = 3
36 let fixedPool, dynamicPool
37
38 before(() => {
39 fixedPool = new FixedThreadPool(
40 max,
41 './tests/worker-files/thread/testWorker.js'
42 )
43 dynamicPool = new DynamicThreadPool(
44 min,
45 max,
46 './tests/worker-files/thread/testWorker.js'
47 )
48 })
49
50 afterEach(() => {
51 sinon.restore()
52 })
53
54 after(async () => {
55 await fixedPool.destroy()
56 await dynamicPool.destroy()
57 })
58
59 it('Verify that constructor() initializes the context with all the available worker choice strategies', () => {
60 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
61 fixedPool
62 )
63 expect(workerChoiceStrategyContext.workerChoiceStrategies.size).toBe(
64 Object.keys(WorkerChoiceStrategies).length
65 )
66 })
67
68 it('Verify that execute() return the worker chosen by the strategy with fixed pool', () => {
69 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
70 fixedPool
71 )
72 const WorkerChoiceStrategyStub = sinon.createStubInstance(
73 RoundRobinWorkerChoiceStrategy,
74 {
75 choose: sinon.stub().returns(0)
76 }
77 )
78 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
79 WorkerChoiceStrategies.ROUND_ROBIN
80 )
81 workerChoiceStrategyContext.workerChoiceStrategies.set(
82 workerChoiceStrategyContext.workerChoiceStrategy,
83 WorkerChoiceStrategyStub
84 )
85 const chosenWorkerKey = workerChoiceStrategyContext.execute()
86 expect(
87 workerChoiceStrategyContext.workerChoiceStrategies.get(
88 workerChoiceStrategyContext.workerChoiceStrategy
89 ).choose.calledOnce
90 ).toBe(true)
91 expect(chosenWorkerKey).toBe(0)
92 })
93
94 it('Verify that execute() throws error if null or undefined is returned', () => {
95 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
96 fixedPool
97 )
98 const WorkerChoiceStrategyUndefinedStub = sinon.createStubInstance(
99 RoundRobinWorkerChoiceStrategy,
100 {
101 choose: sinon.stub().returns(undefined)
102 }
103 )
104 const WorkerChoiceStrategyNullStub = sinon.createStubInstance(
105 RoundRobinWorkerChoiceStrategy,
106 {
107 choose: sinon.stub().returns(null)
108 }
109 )
110 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
111 WorkerChoiceStrategies.ROUND_ROBIN
112 )
113 workerChoiceStrategyContext.workerChoiceStrategies.set(
114 workerChoiceStrategyContext.workerChoiceStrategy,
115 WorkerChoiceStrategyUndefinedStub
116 )
117 expect(() => workerChoiceStrategyContext.execute()).toThrowError(
118 new TypeError('Worker node key chosen is null or undefined')
119 )
120 workerChoiceStrategyContext.workerChoiceStrategies.set(
121 workerChoiceStrategyContext.workerChoiceStrategy,
122 WorkerChoiceStrategyNullStub
123 )
124 expect(() => workerChoiceStrategyContext.execute()).toThrowError(
125 new TypeError('Worker node key chosen is null or undefined')
126 )
127 })
128
129 it('Verify that execute() return the worker chosen by the strategy with dynamic pool', () => {
130 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
131 dynamicPool
132 )
133 const WorkerChoiceStrategyStub = sinon.createStubInstance(
134 RoundRobinWorkerChoiceStrategy,
135 {
136 choose: sinon.stub().returns(0)
137 }
138 )
139 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
140 WorkerChoiceStrategies.ROUND_ROBIN
141 )
142 workerChoiceStrategyContext.workerChoiceStrategies.set(
143 workerChoiceStrategyContext.workerChoiceStrategy,
144 WorkerChoiceStrategyStub
145 )
146 const chosenWorkerKey = workerChoiceStrategyContext.execute()
147 expect(
148 workerChoiceStrategyContext.workerChoiceStrategies.get(
149 workerChoiceStrategyContext.workerChoiceStrategy
150 ).choose.calledOnce
151 ).toBe(true)
152 expect(chosenWorkerKey).toBe(0)
153 })
154
155 it('Verify that setWorkerChoiceStrategy() works with ROUND_ROBIN and fixed pool', () => {
156 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
157 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
158 fixedPool
159 )
160 expect(
161 workerChoiceStrategyContext.workerChoiceStrategies.get(
162 workerChoiceStrategy
163 )
164 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
165 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
166 workerChoiceStrategy
167 )
168 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
169 expect(
170 workerChoiceStrategyContext.workerChoiceStrategies.get(
171 workerChoiceStrategy
172 )
173 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
174 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
175 workerChoiceStrategy
176 )
177 })
178
179 it('Verify that setWorkerChoiceStrategy() works with ROUND_ROBIN and dynamic pool', () => {
180 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
181 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
182 dynamicPool
183 )
184 expect(
185 workerChoiceStrategyContext.workerChoiceStrategies.get(
186 workerChoiceStrategy
187 )
188 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
189 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
190 workerChoiceStrategy
191 )
192 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
193 expect(
194 workerChoiceStrategyContext.workerChoiceStrategies.get(
195 workerChoiceStrategy
196 )
197 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
198 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
199 workerChoiceStrategy
200 )
201 })
202
203 it('Verify that setWorkerChoiceStrategy() works with LEAST_USED and fixed pool', () => {
204 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
205 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
206 fixedPool
207 )
208 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
209 expect(
210 workerChoiceStrategyContext.workerChoiceStrategies.get(
211 workerChoiceStrategy
212 )
213 ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
214 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
215 workerChoiceStrategy
216 )
217 })
218
219 it('Verify that setWorkerChoiceStrategy() works with LEAST_USED and dynamic pool', () => {
220 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
221 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
222 dynamicPool
223 )
224 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
225 expect(
226 workerChoiceStrategyContext.workerChoiceStrategies.get(
227 workerChoiceStrategy
228 )
229 ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
230 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
231 workerChoiceStrategy
232 )
233 })
234
235 it('Verify that setWorkerChoiceStrategy() works with LEAST_BUSY and fixed pool', () => {
236 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
237 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
238 fixedPool
239 )
240 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
241 expect(
242 workerChoiceStrategyContext.workerChoiceStrategies.get(
243 workerChoiceStrategy
244 )
245 ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
246 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
247 workerChoiceStrategy
248 )
249 })
250
251 it('Verify that setWorkerChoiceStrategy() works with LEAST_BUSY and dynamic pool', () => {
252 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
253 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
254 dynamicPool
255 )
256 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
257 expect(
258 workerChoiceStrategyContext.workerChoiceStrategies.get(
259 workerChoiceStrategy
260 )
261 ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
262 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
263 workerChoiceStrategy
264 )
265 })
266
267 it('Verify that setWorkerChoiceStrategy() works with LEAST_ELU and fixed pool', () => {
268 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
269 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
270 fixedPool
271 )
272 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
273 expect(
274 workerChoiceStrategyContext.workerChoiceStrategies.get(
275 workerChoiceStrategy
276 )
277 ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
278 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
279 workerChoiceStrategy
280 )
281 })
282
283 it('Verify that setWorkerChoiceStrategy() works with LEAST_ELU and dynamic pool', () => {
284 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
285 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
286 dynamicPool
287 )
288 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
289 expect(
290 workerChoiceStrategyContext.workerChoiceStrategies.get(
291 workerChoiceStrategy
292 )
293 ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
294 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
295 workerChoiceStrategy
296 )
297 })
298
299 it('Verify that setWorkerChoiceStrategy() works with FAIR_SHARE and fixed pool', () => {
300 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
301 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
302 fixedPool
303 )
304 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
305 expect(
306 workerChoiceStrategyContext.workerChoiceStrategies.get(
307 workerChoiceStrategy
308 )
309 ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
310 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
311 workerChoiceStrategy
312 )
313 })
314
315 it('Verify that setWorkerChoiceStrategy() works with FAIR_SHARE and dynamic pool', () => {
316 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
317 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
318 dynamicPool
319 )
320 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
321 expect(
322 workerChoiceStrategyContext.workerChoiceStrategies.get(
323 workerChoiceStrategy
324 )
325 ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
326 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
327 workerChoiceStrategy
328 )
329 })
330
331 it('Verify that setWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and fixed pool', () => {
332 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
333 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
334 fixedPool
335 )
336 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
337 expect(
338 workerChoiceStrategyContext.workerChoiceStrategies.get(
339 workerChoiceStrategy
340 )
341 ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
342 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
343 workerChoiceStrategy
344 )
345 })
346
347 it('Verify that setWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
348 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
349 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
350 dynamicPool
351 )
352 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
353 expect(
354 workerChoiceStrategyContext.workerChoiceStrategies.get(
355 workerChoiceStrategy
356 )
357 ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
358 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
359 workerChoiceStrategy
360 )
361 })
362
363 it('Verify that setWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and fixed pool', () => {
364 const workerChoiceStrategy =
365 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
366 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
367 fixedPool
368 )
369 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
370 expect(
371 workerChoiceStrategyContext.workerChoiceStrategies.get(
372 workerChoiceStrategy
373 )
374 ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
375 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
376 workerChoiceStrategy
377 )
378 })
379
380 it('Verify that setWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
381 const workerChoiceStrategy =
382 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
383 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
384 dynamicPool
385 )
386 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
387 expect(
388 workerChoiceStrategyContext.workerChoiceStrategies.get(
389 workerChoiceStrategy
390 )
391 ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
392 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
393 workerChoiceStrategy
394 )
395 })
396
397 it('Verify that worker choice strategy options enable median runtime pool statistics', () => {
398 const wwrWorkerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
399 let workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
400 fixedPool,
401 wwrWorkerChoiceStrategy,
402 {
403 runTime: { median: true }
404 }
405 )
406 expect(
407 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
408 .average
409 ).toBe(false)
410 expect(
411 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
412 ).toBe(true)
413 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
414 dynamicPool,
415 wwrWorkerChoiceStrategy,
416 {
417 runTime: { median: true }
418 }
419 )
420 expect(
421 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
422 .average
423 ).toBe(false)
424 expect(
425 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
426 ).toBe(true)
427 const fsWorkerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
428 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
429 fixedPool,
430 fsWorkerChoiceStrategy,
431 {
432 runTime: { median: true }
433 }
434 )
435 expect(
436 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
437 .average
438 ).toBe(false)
439 expect(
440 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
441 ).toBe(true)
442 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
443 dynamicPool,
444 fsWorkerChoiceStrategy,
445 {
446 runTime: { median: true }
447 }
448 )
449 expect(
450 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
451 .average
452 ).toBe(false)
453 expect(
454 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
455 ).toBe(true)
456 })
457 })