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