1 import { expect } from 'expect'
2 import { createStubInstance, restore, stub } from 'sinon'
7 } from '../../../lib/index.cjs'
8 import { WorkerChoiceStrategyContext } from '../../../lib/pools/selection-strategies/worker-choice-strategy-context.cjs'
9 import { RoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/round-robin-worker-choice-strategy.cjs'
10 import { LeastUsedWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-used-worker-choice-strategy.cjs'
11 import { LeastBusyWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-busy-worker-choice-strategy.cjs'
12 import { LeastEluWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-elu-worker-choice-strategy.cjs'
13 import { FairShareWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/fair-share-worker-choice-strategy.cjs'
14 import { WeightedRoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/weighted-round-robin-worker-choice-strategy.cjs'
15 import { InterleavedWeightedRoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/interleaved-weighted-round-robin-worker-choice-strategy.cjs'
17 describe('Worker choice strategy context test suite', () => {
20 let fixedPool, dynamicPool
23 fixedPool = new FixedThreadPool(
25 './tests/worker-files/thread/testWorker.mjs'
27 dynamicPool = new DynamicThreadPool(
30 './tests/worker-files/thread/testWorker.mjs'
39 await fixedPool.destroy()
40 await dynamicPool.destroy()
43 it('Verify that constructor() initializes the context with all the available worker choice strategies', () => {
44 let workerChoiceStrategyContext = new WorkerChoiceStrategyContext(fixedPool)
45 expect(workerChoiceStrategyContext.workerChoiceStrategies.size).toBe(
46 Object.keys(WorkerChoiceStrategies).length
48 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(dynamicPool)
49 expect(workerChoiceStrategyContext.workerChoiceStrategies.size).toBe(
50 Object.keys(WorkerChoiceStrategies).length
54 it('Verify that constructor() initializes the context with retries attribute properly set', () => {
55 let workerChoiceStrategyContext = new WorkerChoiceStrategyContext(fixedPool)
56 expect(workerChoiceStrategyContext.retries).toBe(fixedPool.info.maxSize * 2)
57 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(dynamicPool)
58 expect(workerChoiceStrategyContext.retries).toBe(
59 dynamicPool.info.maxSize * 2
63 it('Verify that execute() return the worker node key chosen by the strategy with fixed pool', () => {
64 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
67 const workerChoiceStrategyStub = createStubInstance(
68 RoundRobinWorkerChoiceStrategy,
70 hasPoolWorkerNodesReady: stub().returns(true),
71 choose: stub().returns(0)
74 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
75 WorkerChoiceStrategies.ROUND_ROBIN
77 workerChoiceStrategyContext.workerChoiceStrategies.set(
78 workerChoiceStrategyContext.workerChoiceStrategy,
79 workerChoiceStrategyStub
81 const chosenWorkerKey = workerChoiceStrategyContext.execute()
83 workerChoiceStrategyContext.workerChoiceStrategies.get(
84 workerChoiceStrategyContext.workerChoiceStrategy
87 expect(chosenWorkerKey).toBe(0)
90 it('Verify that execute() throws error if null or undefined is returned after retries', () => {
91 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
94 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
95 WorkerChoiceStrategies.ROUND_ROBIN
97 const workerChoiceStrategyUndefinedStub = createStubInstance(
98 RoundRobinWorkerChoiceStrategy,
100 hasPoolWorkerNodesReady: stub().returns(true),
101 choose: stub().returns(undefined)
104 workerChoiceStrategyContext.workerChoiceStrategies.set(
105 workerChoiceStrategyContext.workerChoiceStrategy,
106 workerChoiceStrategyUndefinedStub
108 expect(() => workerChoiceStrategyContext.execute()).toThrow(
110 `Worker node key chosen is null or undefined after ${workerChoiceStrategyContext.retries} retries`
113 const workerChoiceStrategyNullStub = createStubInstance(
114 RoundRobinWorkerChoiceStrategy,
116 hasPoolWorkerNodesReady: stub().returns(true),
117 choose: stub().returns(null)
120 workerChoiceStrategyContext.workerChoiceStrategies.set(
121 workerChoiceStrategyContext.workerChoiceStrategy,
122 workerChoiceStrategyNullStub
124 expect(() => workerChoiceStrategyContext.execute()).toThrow(
126 `Worker node key chosen is null or undefined after ${workerChoiceStrategyContext.retries} retries`
131 it('Verify that execute() retry until a worker node is ready and chosen', () => {
132 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
135 const workerChoiceStrategyStub = createStubInstance(
136 RoundRobinWorkerChoiceStrategy,
138 hasPoolWorkerNodesReady: stub()
150 choose: stub().returns(1)
153 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
154 WorkerChoiceStrategies.ROUND_ROBIN
156 workerChoiceStrategyContext.workerChoiceStrategies.set(
157 workerChoiceStrategyContext.workerChoiceStrategy,
158 workerChoiceStrategyStub
160 const chosenWorkerKey = workerChoiceStrategyContext.execute()
162 workerChoiceStrategyContext.workerChoiceStrategies.get(
163 workerChoiceStrategyContext.workerChoiceStrategy
164 ).hasPoolWorkerNodesReady.callCount
167 workerChoiceStrategyContext.workerChoiceStrategies.get(
168 workerChoiceStrategyContext.workerChoiceStrategy
171 expect(chosenWorkerKey).toBe(1)
174 it('Verify that execute() throws error if worker choice strategy recursion reach the maximum depth', () => {
175 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
178 const workerChoiceStrategyStub = createStubInstance(
179 RoundRobinWorkerChoiceStrategy,
181 hasPoolWorkerNodesReady: stub().returns(false),
182 choose: stub().returns(0)
185 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
186 WorkerChoiceStrategies.ROUND_ROBIN
188 workerChoiceStrategyContext.workerChoiceStrategies.set(
189 workerChoiceStrategyContext.workerChoiceStrategy,
190 workerChoiceStrategyStub
192 expect(() => workerChoiceStrategyContext.execute()).toThrow(
193 new RangeError('Maximum call stack size exceeded')
197 it('Verify that execute() return the worker node key chosen by the strategy with dynamic pool', () => {
198 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
201 const workerChoiceStrategyStub = createStubInstance(
202 RoundRobinWorkerChoiceStrategy,
204 hasPoolWorkerNodesReady: stub().returns(true),
205 choose: stub().returns(0)
208 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
209 WorkerChoiceStrategies.ROUND_ROBIN
211 workerChoiceStrategyContext.workerChoiceStrategies.set(
212 workerChoiceStrategyContext.workerChoiceStrategy,
213 workerChoiceStrategyStub
215 const chosenWorkerKey = workerChoiceStrategyContext.execute()
217 workerChoiceStrategyContext.workerChoiceStrategies.get(
218 workerChoiceStrategyContext.workerChoiceStrategy
221 expect(chosenWorkerKey).toBe(0)
224 it('Verify that setWorkerChoiceStrategy() works with ROUND_ROBIN and fixed pool', () => {
225 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
226 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
230 workerChoiceStrategyContext.workerChoiceStrategies.get(
233 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
234 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
237 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
239 workerChoiceStrategyContext.workerChoiceStrategies.get(
242 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
243 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
248 it('Verify that setWorkerChoiceStrategy() works with ROUND_ROBIN and dynamic pool', () => {
249 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
250 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
254 workerChoiceStrategyContext.workerChoiceStrategies.get(
257 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
258 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
261 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
263 workerChoiceStrategyContext.workerChoiceStrategies.get(
266 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
267 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
272 it('Verify that setWorkerChoiceStrategy() works with LEAST_USED and fixed pool', () => {
273 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
274 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
277 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
279 workerChoiceStrategyContext.workerChoiceStrategies.get(
282 ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
283 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
288 it('Verify that setWorkerChoiceStrategy() works with LEAST_USED and dynamic pool', () => {
289 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
290 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
293 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
295 workerChoiceStrategyContext.workerChoiceStrategies.get(
298 ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
299 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
304 it('Verify that setWorkerChoiceStrategy() works with LEAST_BUSY and fixed pool', () => {
305 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
306 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
309 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
311 workerChoiceStrategyContext.workerChoiceStrategies.get(
314 ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
315 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
320 it('Verify that setWorkerChoiceStrategy() works with LEAST_BUSY and dynamic pool', () => {
321 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
322 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
325 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
327 workerChoiceStrategyContext.workerChoiceStrategies.get(
330 ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
331 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
336 it('Verify that setWorkerChoiceStrategy() works with LEAST_ELU and fixed pool', () => {
337 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
338 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
341 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
343 workerChoiceStrategyContext.workerChoiceStrategies.get(
346 ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
347 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
352 it('Verify that setWorkerChoiceStrategy() works with LEAST_ELU and dynamic pool', () => {
353 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
354 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
357 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
359 workerChoiceStrategyContext.workerChoiceStrategies.get(
362 ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
363 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
368 it('Verify that setWorkerChoiceStrategy() works with FAIR_SHARE and fixed pool', () => {
369 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
370 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
373 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
375 workerChoiceStrategyContext.workerChoiceStrategies.get(
378 ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
379 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
384 it('Verify that setWorkerChoiceStrategy() works with FAIR_SHARE and dynamic pool', () => {
385 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
386 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
389 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
391 workerChoiceStrategyContext.workerChoiceStrategies.get(
394 ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
395 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
400 it('Verify that setWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and fixed pool', () => {
401 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
402 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
405 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
407 workerChoiceStrategyContext.workerChoiceStrategies.get(
410 ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
411 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
416 it('Verify that setWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
417 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
418 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
421 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
423 workerChoiceStrategyContext.workerChoiceStrategies.get(
426 ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
427 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
432 it('Verify that setWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and fixed pool', () => {
433 const workerChoiceStrategy =
434 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
435 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
438 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
440 workerChoiceStrategyContext.workerChoiceStrategies.get(
443 ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
444 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
449 it('Verify that setWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
450 const workerChoiceStrategy =
451 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
452 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
455 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
457 workerChoiceStrategyContext.workerChoiceStrategies.get(
460 ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
461 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
466 it('Verify that worker choice strategy options enable median runtime pool statistics', () => {
467 const wwrWorkerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
468 let workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
470 wwrWorkerChoiceStrategy,
472 runTime: { median: true }
476 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
480 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
482 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
484 wwrWorkerChoiceStrategy,
486 runTime: { median: true }
490 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
494 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
496 const fsWorkerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
497 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
499 fsWorkerChoiceStrategy,
501 runTime: { median: true }
505 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
509 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
511 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
513 fsWorkerChoiceStrategy,
515 runTime: { median: true }
519 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
523 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median