1 import { expect } from 'expect'
2 import { createStubInstance, restore, stub } from 'sinon'
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'
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 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
47 expect(workerChoiceStrategyContext.workerChoiceStrategies.size).toBe(
48 Object.keys(WorkerChoiceStrategies).length
52 it('Verify that execute() return the worker node key chosen by the strategy with fixed pool', () => {
53 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
56 const workerChoiceStrategyStub = createStubInstance(
57 RoundRobinWorkerChoiceStrategy,
59 hasPoolWorkerNodesReady: stub().returns(true),
60 choose: stub().returns(0)
63 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
64 WorkerChoiceStrategies.ROUND_ROBIN
66 workerChoiceStrategyContext.workerChoiceStrategies.set(
67 workerChoiceStrategyContext.workerChoiceStrategy,
68 workerChoiceStrategyStub
70 const chosenWorkerKey = workerChoiceStrategyContext.execute()
72 workerChoiceStrategyContext.workerChoiceStrategies.get(
73 workerChoiceStrategyContext.workerChoiceStrategy
76 expect(chosenWorkerKey).toBe(0)
79 it('Verify that execute() throws error if null or undefined is returned after retries', () => {
80 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
83 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
84 WorkerChoiceStrategies.ROUND_ROBIN
86 const workerChoiceStrategyUndefinedStub = createStubInstance(
87 RoundRobinWorkerChoiceStrategy,
89 hasPoolWorkerNodesReady: stub().returns(true),
90 choose: stub().returns(undefined)
93 workerChoiceStrategyContext.workerChoiceStrategies.set(
94 workerChoiceStrategyContext.workerChoiceStrategy,
95 workerChoiceStrategyUndefinedStub
97 expect(() => workerChoiceStrategyContext.execute()).toThrow(
99 `Worker node key chosen is null or undefined after ${fixedPool.info.maxSize} retries`
102 const workerChoiceStrategyNullStub = createStubInstance(
103 RoundRobinWorkerChoiceStrategy,
105 hasPoolWorkerNodesReady: stub().returns(true),
106 choose: stub().returns(null)
109 workerChoiceStrategyContext.workerChoiceStrategies.set(
110 workerChoiceStrategyContext.workerChoiceStrategy,
111 workerChoiceStrategyNullStub
113 expect(() => workerChoiceStrategyContext.execute()).toThrow(
115 `Worker node key chosen is null or undefined after ${fixedPool.info.maxSize} retries`
120 it('Verify that execute() retry until a worker node is ready and chosen', () => {
121 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
124 const workerChoiceStrategyStub = createStubInstance(
125 RoundRobinWorkerChoiceStrategy,
127 hasPoolWorkerNodesReady: stub()
139 choose: stub().returns(1)
142 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
143 WorkerChoiceStrategies.ROUND_ROBIN
145 workerChoiceStrategyContext.workerChoiceStrategies.set(
146 workerChoiceStrategyContext.workerChoiceStrategy,
147 workerChoiceStrategyStub
149 const chosenWorkerKey = workerChoiceStrategyContext.execute()
151 workerChoiceStrategyContext.workerChoiceStrategies.get(
152 workerChoiceStrategyContext.workerChoiceStrategy
153 ).hasPoolWorkerNodesReady.callCount
156 workerChoiceStrategyContext.workerChoiceStrategies.get(
157 workerChoiceStrategyContext.workerChoiceStrategy
160 expect(chosenWorkerKey).toBe(1)
163 it('Verify that execute() throws error if worker choice strategy recursion reach the maximum depth', () => {
164 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
167 const workerChoiceStrategyStub = createStubInstance(
168 RoundRobinWorkerChoiceStrategy,
170 hasPoolWorkerNodesReady: stub().returns(false),
171 choose: stub().returns(0)
174 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
175 WorkerChoiceStrategies.ROUND_ROBIN
177 workerChoiceStrategyContext.workerChoiceStrategies.set(
178 workerChoiceStrategyContext.workerChoiceStrategy,
179 workerChoiceStrategyStub
181 expect(() => workerChoiceStrategyContext.execute()).toThrow(
182 new RangeError('Maximum call stack size exceeded')
186 it('Verify that execute() return the worker node key chosen by the strategy with dynamic pool', () => {
187 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
190 const workerChoiceStrategyStub = createStubInstance(
191 RoundRobinWorkerChoiceStrategy,
193 hasPoolWorkerNodesReady: stub().returns(true),
194 choose: stub().returns(0)
197 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
198 WorkerChoiceStrategies.ROUND_ROBIN
200 workerChoiceStrategyContext.workerChoiceStrategies.set(
201 workerChoiceStrategyContext.workerChoiceStrategy,
202 workerChoiceStrategyStub
204 const chosenWorkerKey = workerChoiceStrategyContext.execute()
206 workerChoiceStrategyContext.workerChoiceStrategies.get(
207 workerChoiceStrategyContext.workerChoiceStrategy
210 expect(chosenWorkerKey).toBe(0)
213 it('Verify that setWorkerChoiceStrategy() works with ROUND_ROBIN and fixed pool', () => {
214 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
215 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
219 workerChoiceStrategyContext.workerChoiceStrategies.get(
222 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
223 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
226 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
228 workerChoiceStrategyContext.workerChoiceStrategies.get(
231 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
232 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
237 it('Verify that setWorkerChoiceStrategy() works with ROUND_ROBIN and dynamic pool', () => {
238 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
239 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
243 workerChoiceStrategyContext.workerChoiceStrategies.get(
246 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
247 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
250 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
252 workerChoiceStrategyContext.workerChoiceStrategies.get(
255 ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
256 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
261 it('Verify that setWorkerChoiceStrategy() works with LEAST_USED and fixed pool', () => {
262 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
263 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
266 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
268 workerChoiceStrategyContext.workerChoiceStrategies.get(
271 ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
272 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
277 it('Verify that setWorkerChoiceStrategy() works with LEAST_USED and dynamic pool', () => {
278 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
279 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
282 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
284 workerChoiceStrategyContext.workerChoiceStrategies.get(
287 ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
288 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
293 it('Verify that setWorkerChoiceStrategy() works with LEAST_BUSY and fixed pool', () => {
294 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
295 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
298 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
300 workerChoiceStrategyContext.workerChoiceStrategies.get(
303 ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
304 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
309 it('Verify that setWorkerChoiceStrategy() works with LEAST_BUSY and dynamic pool', () => {
310 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
311 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
314 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
316 workerChoiceStrategyContext.workerChoiceStrategies.get(
319 ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
320 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
325 it('Verify that setWorkerChoiceStrategy() works with LEAST_ELU and fixed pool', () => {
326 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
327 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
330 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
332 workerChoiceStrategyContext.workerChoiceStrategies.get(
335 ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
336 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
341 it('Verify that setWorkerChoiceStrategy() works with LEAST_ELU and dynamic pool', () => {
342 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
343 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
346 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
348 workerChoiceStrategyContext.workerChoiceStrategies.get(
351 ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
352 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
357 it('Verify that setWorkerChoiceStrategy() works with FAIR_SHARE and fixed pool', () => {
358 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
359 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
362 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
364 workerChoiceStrategyContext.workerChoiceStrategies.get(
367 ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
368 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
373 it('Verify that setWorkerChoiceStrategy() works with FAIR_SHARE and dynamic pool', () => {
374 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
375 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
378 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
380 workerChoiceStrategyContext.workerChoiceStrategies.get(
383 ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
384 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
389 it('Verify that setWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and fixed pool', () => {
390 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
391 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
394 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
396 workerChoiceStrategyContext.workerChoiceStrategies.get(
399 ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
400 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
405 it('Verify that setWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
406 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
407 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
410 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
412 workerChoiceStrategyContext.workerChoiceStrategies.get(
415 ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
416 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
421 it('Verify that setWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and fixed pool', () => {
422 const workerChoiceStrategy =
423 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
424 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
427 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
429 workerChoiceStrategyContext.workerChoiceStrategies.get(
432 ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
433 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
438 it('Verify that setWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
439 const workerChoiceStrategy =
440 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
441 const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
444 workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
446 workerChoiceStrategyContext.workerChoiceStrategies.get(
449 ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
450 expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
455 it('Verify that worker choice strategy options enable median runtime pool statistics', () => {
456 const wwrWorkerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
457 let workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
459 wwrWorkerChoiceStrategy,
461 runTime: { median: true }
465 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
469 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
471 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
473 wwrWorkerChoiceStrategy,
475 runTime: { median: true }
479 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
483 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
485 const fsWorkerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
486 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
488 fsWorkerChoiceStrategy,
490 runTime: { median: true }
494 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
498 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
500 workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
502 fsWorkerChoiceStrategy,
504 runTime: { median: true }
508 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
512 workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median