X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=tests%2Fpools%2Fselection-strategies%2Fselection-strategies.test.js;h=392aafcf3a58a79f6744a37268b01415243a2b97;hb=a4958de2101f06e7096b83adbca82fcfd532a721;hp=abef7e5baa30ee510bd0137ff38e220c37f5dbab;hpb=2806c2ae7279d0e184fd18a52b6bc03bfab085b5;p=poolifier.git diff --git a/tests/pools/selection-strategies/selection-strategies.test.js b/tests/pools/selection-strategies/selection-strategies.test.js index abef7e5b..392aafcf 100644 --- a/tests/pools/selection-strategies/selection-strategies.test.js +++ b/tests/pools/selection-strategies/selection-strategies.test.js @@ -4,7 +4,7 @@ const { DynamicThreadPool, FixedThreadPool, FixedClusterPool -} = require('../../../lib/index') +} = require('../../../lib') describe('Selection strategies test suite', () => { const min = 0 @@ -33,55 +33,115 @@ describe('Selection strategies test suite', () => { await pool.destroy() }) - it('Verify ROUND_ROBIN strategy is taken at pool creation', async () => { - const pool = new FixedThreadPool( - max, - './tests/worker-files/thread/testWorker.js', - { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN } - ) - expect(pool.opts.workerChoiceStrategy).toBe( - WorkerChoiceStrategies.ROUND_ROBIN - ) - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.nextWorkerId - ).toBe(0) - // We need to clean up the resources after our test - await pool.destroy() + it('Verify available strategies are taken at pool creation', async () => { + for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) { + const pool = new FixedThreadPool( + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy) + expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe( + workerChoiceStrategy + ) + await pool.destroy() + } }) - it('Verify ROUND_ROBIN strategy can be set after pool creation', async () => { - const pool = new DynamicThreadPool( - min, + it('Verify available strategies can be set after pool creation', async () => { + for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) { + const pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js' + ) + pool.setWorkerChoiceStrategy(workerChoiceStrategy) + expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy) + expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe( + workerChoiceStrategy + ) + await pool.destroy() + } + }) + + it('Verify available strategies default internals at pool creation', async () => { + const pool = new FixedThreadPool( max, './tests/worker-files/thread/testWorker.js' ) - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.ROUND_ROBIN) - expect(pool.opts.workerChoiceStrategy).toBe( - WorkerChoiceStrategies.ROUND_ROBIN - ) - // We need to clean up the resources after our test + for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) { + if (workerChoiceStrategy === WorkerChoiceStrategies.ROUND_ROBIN) { + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).nextWorkerNodeId + ).toBe(0) + } else if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) { + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskTimestamp + ).toBeInstanceOf(Array) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskTimestamp.length + ).toBe(0) + } else if ( + workerChoiceStrategy === WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN + ) { + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).currentWorkerNodeId + ).toBe(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).defaultWorkerWeight + ).toBeGreaterThan(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workerVirtualTaskRunTime + ).toBe(0) + } + } await pool.destroy() }) it('Verify ROUND_ROBIN strategy default tasks usage statistics requirements', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN let pool = new FixedThreadPool( max, - './tests/worker-files/thread/testWorker.js' + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } ) - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.ROUND_ROBIN) expect( pool.workerChoiceStrategyContext.getRequiredStatistics().runTime ).toBe(false) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime + ).toBe(false) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime + ).toBe(false) await pool.destroy() pool = new DynamicThreadPool( min, max, - './tests/worker-files/thread/testWorker.js' + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } ) - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.ROUND_ROBIN) expect( pool.workerChoiceStrategyContext.getRequiredStatistics().runTime ).toBe(false) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime + ).toBe(false) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime + ).toBe(false) // We need to clean up the resources after our test await pool.destroy() }) @@ -92,15 +152,11 @@ describe('Selection strategies test suite', () => { './tests/worker-files/thread/testWorker.js', { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN } ) - expect(pool.opts.workerChoiceStrategy).toBe( - WorkerChoiceStrategies.ROUND_ROBIN - ) // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose` - const promises = [] - for (let i = 0; i < max * 2; i++) { - promises.push(pool.execute()) + const maxMultiplier = 2 + for (let i = 0; i < max * maxMultiplier; i++) { + await pool.execute() } - await Promise.all(promises) // We need to clean up the resources after our test await pool.destroy() }) @@ -112,51 +168,58 @@ describe('Selection strategies test suite', () => { './tests/worker-files/thread/testWorker.js', { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN } ) - expect(pool.opts.workerChoiceStrategy).toBe( - WorkerChoiceStrategies.ROUND_ROBIN - ) // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose` - const promises = [] - for (let i = 0; i < max * 2; i++) { - promises.push(pool.execute()) + const maxMultiplier = 2 + for (let i = 0; i < max * maxMultiplier; i++) { + await pool.execute() } - await Promise.all(promises) // We need to clean up the resources after our test await pool.destroy() }) it('Verify ROUND_ROBIN strategy runtime behavior', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN let pool = new FixedClusterPool( max, - './tests/worker-files/cluster/testWorker.js' + './tests/worker-files/cluster/testWorker.js', + { workerChoiceStrategy } ) let results = new Set() for (let i = 0; i < max; i++) { - results.add(pool.chooseWorker()[1].id) + results.add(pool.chooseWorkerNode()[1].worker.id) } expect(results.size).toBe(max) await pool.destroy() - pool = new FixedThreadPool(max, './tests/worker-files/thread/testWorker.js') + pool = new FixedThreadPool( + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) results = new Set() for (let i = 0; i < max; i++) { - results.add(pool.chooseWorker()[1].threadId) + results.add(pool.chooseWorkerNode()[1].worker.threadId) } expect(results.size).toBe(max) await pool.destroy() }) it('Verify ROUND_ROBIN strategy internals are resets after setting it', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN let pool = new FixedThreadPool( max, './tests/worker-files/thread/testWorker.js', { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN } ) expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.nextWorkerId - ).toBeUndefined() - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.ROUND_ROBIN) + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).nextWorkerNodeId + ).toBeDefined() + pool.setWorkerChoiceStrategy(workerChoiceStrategy) expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.nextWorkerId + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).nextWorkerNodeId ).toBe(0) await pool.destroy() pool = new DynamicThreadPool( @@ -166,63 +229,52 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN } ) expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerChoiceStrategy - .nextWorkerId - ).toBeUndefined() - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.ROUND_ROBIN) + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).nextWorkerNodeId + ).toBeDefined() + pool.setWorkerChoiceStrategy(workerChoiceStrategy) expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerChoiceStrategy - .nextWorkerId + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).nextWorkerNodeId ).toBe(0) // We need to clean up the resources after our test await pool.destroy() }) - it('Verify LESS_USED strategy is taken at pool creation', async () => { - const pool = new FixedThreadPool( - max, - './tests/worker-files/thread/testWorker.js', - { workerChoiceStrategy: WorkerChoiceStrategies.LESS_USED } - ) - expect(pool.opts.workerChoiceStrategy).toBe( - WorkerChoiceStrategies.LESS_USED - ) - // We need to clean up the resources after our test - await pool.destroy() - }) - - it('Verify LESS_USED strategy can be set after pool creation', async () => { - const pool = new FixedThreadPool( - max, - './tests/worker-files/thread/testWorker.js' - ) - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.LESS_USED) - expect(pool.opts.workerChoiceStrategy).toBe( - WorkerChoiceStrategies.LESS_USED - ) - // We need to clean up the resources after our test - await pool.destroy() - }) - it('Verify LESS_USED strategy default tasks usage statistics requirements', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.LESS_USED let pool = new FixedThreadPool( max, - './tests/worker-files/thread/testWorker.js' + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } ) - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.LESS_USED) expect( pool.workerChoiceStrategyContext.getRequiredStatistics().runTime ).toBe(false) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime + ).toBe(false) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime + ).toBe(false) await pool.destroy() pool = new DynamicThreadPool( min, max, - './tests/worker-files/thread/testWorker.js' + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } ) - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.LESS_USED) expect( pool.workerChoiceStrategyContext.getRequiredStatistics().runTime ).toBe(false) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime + ).toBe(false) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime + ).toBe(false) // We need to clean up the resources after our test await pool.destroy() }) @@ -234,11 +286,10 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.LESS_USED } ) // TODO: Create a better test to cover `LessUsedWorkerChoiceStrategy#choose` - const promises = [] - for (let i = 0; i < max * 2; i++) { - promises.push(pool.execute()) + const maxMultiplier = 2 + for (let i = 0; i < max * maxMultiplier; i++) { + await pool.execute() } - await Promise.all(promises) // We need to clean up the resources after our test await pool.destroy() }) @@ -251,60 +302,46 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.LESS_USED } ) // TODO: Create a better test to cover `LessUsedWorkerChoiceStrategy#choose` - const promises = [] - for (let i = 0; i < max * 2; i++) { - promises.push(pool.execute()) + const maxMultiplier = 2 + for (let i = 0; i < max * maxMultiplier; i++) { + await pool.execute() } - await Promise.all(promises) - // We need to clean up the resources after our test - await pool.destroy() - }) - - it('Verify LESS_BUSY strategy is taken at pool creation', async () => { - const pool = new FixedThreadPool( - max, - './tests/worker-files/thread/testWorker.js', - { workerChoiceStrategy: WorkerChoiceStrategies.LESS_BUSY } - ) - expect(pool.opts.workerChoiceStrategy).toBe( - WorkerChoiceStrategies.LESS_BUSY - ) - // We need to clean up the resources after our test - await pool.destroy() - }) - - it('Verify LESS_BUSY strategy can be set after pool creation', async () => { - const pool = new FixedThreadPool( - max, - './tests/worker-files/thread/testWorker.js' - ) - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.LESS_BUSY) - expect(pool.opts.workerChoiceStrategy).toBe( - WorkerChoiceStrategies.LESS_BUSY - ) // We need to clean up the resources after our test await pool.destroy() }) it('Verify LESS_BUSY strategy default tasks usage statistics requirements', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.LESS_BUSY let pool = new FixedThreadPool( max, - './tests/worker-files/thread/testWorker.js' + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } ) - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.LESS_BUSY) expect( pool.workerChoiceStrategyContext.getRequiredStatistics().runTime ).toBe(true) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime + ).toBe(false) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime + ).toBe(false) await pool.destroy() pool = new DynamicThreadPool( min, max, - './tests/worker-files/thread/testWorker.js' + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } ) - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.LESS_BUSY) expect( pool.workerChoiceStrategyContext.getRequiredStatistics().runTime ).toBe(true) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime + ).toBe(false) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime + ).toBe(false) // We need to clean up the resources after our test await pool.destroy() }) @@ -316,11 +353,10 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.LESS_BUSY } ) // TODO: Create a better test to cover `LessBusyWorkerChoiceStrategy#choose` - const promises = [] - for (let i = 0; i < max * 2; i++) { - promises.push(pool.execute()) + const maxMultiplier = 2 + for (let i = 0; i < max * maxMultiplier; i++) { + await pool.execute() } - await Promise.all(promises) // We need to clean up the resources after our test await pool.destroy() }) @@ -333,72 +369,46 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.LESS_BUSY } ) // TODO: Create a better test to cover `LessBusyWorkerChoiceStrategy#choose` - const promises = [] - for (let i = 0; i < max * 2; i++) { - promises.push(pool.execute()) - } - await Promise.all(promises) - // We need to clean up the resources after our test - await pool.destroy() - }) - - it('Verify FAIR_SHARE strategy is taken at pool creation', async () => { - const pool = new FixedThreadPool( - max, - './tests/worker-files/thread/testWorker.js', - { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE } - ) - expect(pool.opts.workerChoiceStrategy).toBe( - WorkerChoiceStrategies.FAIR_SHARE - ) - for (const workerKey of pool.workerChoiceStrategyContext.workerChoiceStrategy.workerLastVirtualTaskTimestamp.keys()) { - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerLastVirtualTaskTimestamp.get( - workerKey - ).start - ).toBe(0) - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerLastVirtualTaskTimestamp.get( - workerKey - ).end - ).toBe(0) + const maxMultiplier = 2 + for (let i = 0; i < max * maxMultiplier; i++) { + await pool.execute() } // We need to clean up the resources after our test await pool.destroy() }) - it('Verify FAIR_SHARE strategy can be set after pool creation', async () => { - const pool = new FixedThreadPool( - max, - './tests/worker-files/thread/testWorker.js' - ) - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.FAIR_SHARE) - expect(pool.opts.workerChoiceStrategy).toBe( - WorkerChoiceStrategies.FAIR_SHARE - ) - // We need to clean up the resources after our test - await pool.destroy() - }) - it('Verify FAIR_SHARE strategy default tasks usage statistics requirements', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE let pool = new FixedThreadPool( max, - './tests/worker-files/thread/testWorker.js' + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } ) - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.FAIR_SHARE) expect( pool.workerChoiceStrategyContext.getRequiredStatistics().runTime ).toBe(true) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime + ).toBe(true) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime + ).toBe(false) await pool.destroy() pool = new DynamicThreadPool( min, max, - './tests/worker-files/thread/testWorker.js' + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } ) - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.FAIR_SHARE) expect( pool.workerChoiceStrategyContext.getRequiredStatistics().runTime ).toBe(true) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime + ).toBe(true) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime + ).toBe(false) // We need to clean up the resources after our test await pool.destroy() }) @@ -410,15 +420,21 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE } ) // TODO: Create a better test to cover `FairShareChoiceStrategy#choose` - const promises = [] - for (let i = 0; i < max * 2; i++) { - promises.push(pool.execute()) + const maxMultiplier = 2 + for (let i = 0; i < max * maxMultiplier; i++) { + await pool.execute() + } + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage.avgRunTime).toBeDefined() + expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0) + expect(workerNode.tasksUsage.medRunTime).toBeDefined() + expect(workerNode.tasksUsage.medRunTime).toBe(0) } - await Promise.all(promises) expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy - .workerLastVirtualTaskTimestamp.size - ).toBe(pool.workers.length) + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).workersVirtualTaskTimestamp.length + ).toBe(pool.workerNodes.length) // We need to clean up the resources after our test await pool.destroy() }) @@ -431,134 +447,163 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE } ) // TODO: Create a better test to cover `FairShareChoiceStrategy#choose` - const promises = [] - const maxMultiplier = 50 + const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - promises.push(pool.execute()) + await pool.execute() } - await Promise.all(promises) - if (process.platform !== 'win32') { - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy - .workerChoiceStrategy.workerLastVirtualTaskTimestamp.size - ).toBe(pool.workers.length) + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage.avgRunTime).toBeDefined() + expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0) + expect(workerNode.tasksUsage.medRunTime).toBeDefined() + expect(workerNode.tasksUsage.medRunTime).toBe(0) } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).workersVirtualTaskTimestamp.length + ).toBe(pool.workerNodes.length) // We need to clean up the resources after our test await pool.destroy() }) - it('Verify FAIR_SHARE strategy internals are resets after setting it', async () => { - let pool = new FixedThreadPool( - max, - './tests/worker-files/thread/testWorker.js' - ) - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy - .workerLastVirtualTaskTimestamp - ).toBeUndefined() - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.FAIR_SHARE) - for (const workerKey of pool.workerChoiceStrategyContext.workerChoiceStrategy.workerLastVirtualTaskTimestamp.keys()) { - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerLastVirtualTaskTimestamp.get( - workerKey - ).start - ).toBe(0) - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerLastVirtualTaskTimestamp.get( - workerKey - ).end - ).toBe(0) - } - await pool.destroy() - pool = new DynamicThreadPool( + it('Verify FAIR_SHARE strategy can be run in a dynamic pool with median run time statistic', async () => { + const pool = new DynamicThreadPool( min, max, - './tests/worker-files/thread/testWorker.js' + './tests/worker-files/thread/testWorker.js', + { + workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE, + workerChoiceStrategyOptions: { + medRunTime: true + } + } ) - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerChoiceStrategy - .workerLastVirtualTaskTimestamp - ).toBeUndefined() - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.FAIR_SHARE) - for (const workerKey of pool.workerChoiceStrategyContext.workerChoiceStrategy.workerChoiceStrategy.workerLastVirtualTaskTimestamp.keys()) { - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerChoiceStrategy.workerLastVirtualTaskTimestamp.get( - workerKey - ).start - ).toBe(0) - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerChoiceStrategy.workerLastVirtualTaskTimestamp.get( - workerKey - ).end - ).toBe(0) + // TODO: Create a better test to cover `FairShareChoiceStrategy#choose` + const maxMultiplier = 2 + for (let i = 0; i < max * maxMultiplier; i++) { + await pool.execute() + } + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage.avgRunTime).toBeDefined() + expect(workerNode.tasksUsage.avgRunTime).toBe(0) + expect(workerNode.tasksUsage.medRunTime).toBeDefined() + expect(workerNode.tasksUsage.medRunTime).toBeGreaterThanOrEqual(0) } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).workersVirtualTaskTimestamp.length + ).toBe(pool.workerNodes.length) // We need to clean up the resources after our test await pool.destroy() }) - it('Verify WEIGHTED_ROUND_ROBIN strategy is taken at pool creation', async () => { - const pool = new FixedThreadPool( + it('Verify FAIR_SHARE strategy internals are resets after setting it', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE + let pool = new FixedThreadPool( max, - './tests/worker-files/thread/testWorker.js', - { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN } - ) - expect(pool.opts.workerChoiceStrategy).toBe( - WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN + './tests/worker-files/thread/testWorker.js' ) expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.currentWorkerId - ).toBe(0) + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskTimestamp + ).toBeInstanceOf(Array) expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.defaultWorkerWeight - ).toBeGreaterThan(0) - for (const workerKey of pool.workerChoiceStrategyContext.workerChoiceStrategy.workersTaskRunTime.keys()) { - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workersTaskRunTime.get( - workerKey - ).weight - ).toBeGreaterThan(0) - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workersTaskRunTime.get( - workerKey - ).runTime - ).toBe(0) - } - // We need to clean up the resources after our test + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskTimestamp.length + ).toBe(0) + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskTimestamp[0] = 0 + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskTimestamp.length + ).toBe(1) + pool.setWorkerChoiceStrategy(workerChoiceStrategy) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskTimestamp + ).toBeInstanceOf(Array) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskTimestamp.length + ).toBe(0) await pool.destroy() - }) - - it('Verify WEIGHTED_ROUND_ROBIN strategy can be set after pool creation', async () => { - const pool = new FixedThreadPool( + pool = new DynamicThreadPool( + min, max, './tests/worker-files/thread/testWorker.js' ) - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN) - expect(pool.opts.workerChoiceStrategy).toBe( - WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN - ) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskTimestamp + ).toBeInstanceOf(Array) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskTimestamp.length + ).toBe(0) + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskTimestamp[0] = 0 + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskTimestamp.length + ).toBe(1) + pool.setWorkerChoiceStrategy(workerChoiceStrategy) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskTimestamp + ).toBeInstanceOf(Array) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskTimestamp.length + ).toBe(0) // We need to clean up the resources after our test await pool.destroy() }) it('Verify WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN let pool = new FixedThreadPool( max, - './tests/worker-files/thread/testWorker.js' + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } ) - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN) expect( pool.workerChoiceStrategyContext.getRequiredStatistics().runTime ).toBe(true) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime + ).toBe(true) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime + ).toBe(false) await pool.destroy() pool = new DynamicThreadPool( min, max, - './tests/worker-files/thread/testWorker.js' + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } ) - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN) expect( pool.workerChoiceStrategyContext.getRequiredStatistics().runTime ).toBe(true) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime + ).toBe(true) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime + ).toBe(false) // We need to clean up the resources after our test await pool.destroy() }) @@ -570,15 +615,26 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN } ) // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose` - const promises = [] - for (let i = 0; i < max * 2; i++) { - promises.push(pool.execute()) + const maxMultiplier = 2 + for (let i = 0; i < max * maxMultiplier; i++) { + await pool.execute() + } + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage.avgRunTime).toBeDefined() + expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0) + expect(workerNode.tasksUsage.medRunTime).toBeDefined() + expect(workerNode.tasksUsage.medRunTime).toBe(0) } - await Promise.all(promises) expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workersTaskRunTime - .size - ).toBe(pool.workers.length) + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).defaultWorkerWeight + ).toBeGreaterThan(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).workerVirtualTaskRunTime + ).toBeGreaterThanOrEqual(0) // We need to clean up the resources after our test await pool.destroy() }) @@ -591,52 +647,104 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN } ) // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose` - const promises = [] - const maxMultiplier = - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerChoiceStrategy - .defaultWorkerWeight * 2 + const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - promises.push(pool.execute()) + await pool.execute() } - await Promise.all(promises) - if (process.platform !== 'win32') { - expect( + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage.avgRunTime).toBeDefined() + expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0) + expect(workerNode.tasksUsage.medRunTime).toBeDefined() + expect(workerNode.tasksUsage.medRunTime).toBe(0) + } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( pool.workerChoiceStrategyContext.workerChoiceStrategy - .workerChoiceStrategy.workersTaskRunTime.size - ).toBe(pool.workers.length) + ).defaultWorkerWeight + ).toBeGreaterThan(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).workerVirtualTaskRunTime + ).toBeGreaterThanOrEqual(0) + // We need to clean up the resources after our test + await pool.destroy() + }) + + it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool with median run time statistic', async () => { + const pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js', + { + workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN, + workerChoiceStrategyOptions: { + medRunTime: true + } + } + ) + // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose` + const maxMultiplier = 2 + for (let i = 0; i < max * maxMultiplier; i++) { + await pool.execute() } + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage.avgRunTime).toBeDefined() + expect(workerNode.tasksUsage.avgRunTime).toBe(0) + expect(workerNode.tasksUsage.medRunTime).toBeDefined() + expect(workerNode.tasksUsage.medRunTime).toBeGreaterThanOrEqual(0) + } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).defaultWorkerWeight + ).toBeGreaterThan(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).workerVirtualTaskRunTime + ).toBeGreaterThanOrEqual(0) // We need to clean up the resources after our test await pool.destroy() }) it('Verify WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN let pool = new FixedThreadPool( max, './tests/worker-files/thread/testWorker.js' ) expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.currentWorkerId - ).toBeUndefined() + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).currentWorkerNodeId + ).toBeDefined() expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.defaultWorkerWeight - ).toBeUndefined() + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).defaultWorkerWeight + ).toBeDefined() expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workersTaskRunTime - ).toBeUndefined() - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN) + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workerVirtualTaskRunTime + ).toBeDefined() + pool.setWorkerChoiceStrategy(workerChoiceStrategy) expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.currentWorkerId + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).currentWorkerNodeId ).toBe(0) expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.defaultWorkerWeight + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).defaultWorkerWeight ).toBeGreaterThan(0) - for (const workerKey of pool.workerChoiceStrategyContext.workerChoiceStrategy.workersTaskRunTime.keys()) { - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workersTaskRunTime.get( - workerKey - ).runTime - ).toBe(0) - } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workerVirtualTaskRunTime + ).toBe(0) await pool.destroy() pool = new DynamicThreadPool( min, @@ -644,38 +752,41 @@ describe('Selection strategies test suite', () => { './tests/worker-files/thread/testWorker.js' ) expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerChoiceStrategy - .currentWorkerId - ).toBeUndefined() + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).currentWorkerNodeId + ).toBeDefined() expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerChoiceStrategy - .defaultWorkerWeight - ).toBeUndefined() + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).defaultWorkerWeight + ).toBeDefined() expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerChoiceStrategy - .workersTaskRunTime - ).toBeUndefined() - pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN) + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workerVirtualTaskRunTime + ).toBeDefined() + pool.setWorkerChoiceStrategy(workerChoiceStrategy) expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerChoiceStrategy - .currentWorkerId + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).currentWorkerNodeId ).toBe(0) expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerChoiceStrategy - .defaultWorkerWeight + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).defaultWorkerWeight ).toBeGreaterThan(0) - for (const workerKey of pool.workerChoiceStrategyContext.workerChoiceStrategy.workerChoiceStrategy.workersTaskRunTime.keys()) { - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategy.workerChoiceStrategy.workersTaskRunTime.get( - workerKey - ).runTime - ).toBe(0) - } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workerVirtualTaskRunTime + ).toBe(0) // We need to clean up the resources after our test await pool.destroy() }) - it('Verify unknown strategies throw error', () => { + it('Verify unknown strategy throw error', () => { expect( () => new DynamicThreadPool( @@ -684,8 +795,6 @@ describe('Selection strategies test suite', () => { './tests/worker-files/thread/testWorker.js', { workerChoiceStrategy: 'UNKNOWN_STRATEGY' } ) - ).toThrowError( - new Error("Worker choice strategy 'UNKNOWN_STRATEGY' not found") - ) + ).toThrowError("Invalid worker choice strategy 'UNKNOWN_STRATEGY'") }) })