X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=tests%2Fpools%2Fselection-strategies%2Fselection-strategies.test.js;h=eb30d0e17753e668ea5d9df34a8cbc734dd85210;hb=9458090a8699203af83383930938b2a6ac53e29d;hp=b5d5903226b9979d0b455548a603eb4df3608512;hpb=a20f0ba5aa9c6946254aa197286ad9b70b6a0319;p=poolifier.git diff --git a/tests/pools/selection-strategies/selection-strategies.test.js b/tests/pools/selection-strategies/selection-strategies.test.js index b5d59032..eb30d0e1 100644 --- a/tests/pools/selection-strategies/selection-strategies.test.js +++ b/tests/pools/selection-strategies/selection-strategies.test.js @@ -4,7 +4,8 @@ const { DynamicThreadPool, FixedThreadPool, FixedClusterPool -} = require('../../../lib/index') +} = require('../../../lib') +const { CircularArray } = require('../../../lib/circular-array') describe('Selection strategies test suite', () => { const min = 0 @@ -12,12 +13,15 @@ describe('Selection strategies test suite', () => { it('Verify that WorkerChoiceStrategies enumeration provides string values', () => { expect(WorkerChoiceStrategies.ROUND_ROBIN).toBe('ROUND_ROBIN') - expect(WorkerChoiceStrategies.LESS_USED).toBe('LESS_USED') - expect(WorkerChoiceStrategies.LESS_BUSY).toBe('LESS_BUSY') + expect(WorkerChoiceStrategies.LEAST_USED).toBe('LEAST_USED') + expect(WorkerChoiceStrategies.LEAST_BUSY).toBe('LEAST_BUSY') expect(WorkerChoiceStrategies.FAIR_SHARE).toBe('FAIR_SHARE') expect(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN).toBe( 'WEIGHTED_ROUND_ROBIN' ) + expect(WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN).toBe( + 'INTERLEAVED_WEIGHTED_ROUND_ROBIN' + ) }) it('Verify ROUND_ROBIN strategy is the default at pool creation', async () => { @@ -77,20 +81,16 @@ describe('Selection strategies test suite', () => { ).nextWorkerNodeId ).toBe(0) } else if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) { - for (const workerNodeKey of pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(workerChoiceStrategy) - .workerLastVirtualTaskTimestamp.keys()) { - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(workerChoiceStrategy) - .workerLastVirtualTaskTimestamp.get(workerNodeKey).start - ).toBe(0) - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(workerChoiceStrategy) - .workerLastVirtualTaskTimestamp.get(workerNodeKey).end - ).toBe(0) - } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskEndTimestamp + ).toBeInstanceOf(Array) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskEndTimestamp.length + ).toBe(0) } else if ( workerChoiceStrategy === WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN ) { @@ -104,20 +104,11 @@ describe('Selection strategies test suite', () => { workerChoiceStrategy ).defaultWorkerWeight ).toBeGreaterThan(0) - for (const workerNodeKey of pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(workerChoiceStrategy) - .workersTaskRunTime.keys()) { - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(workerChoiceStrategy) - .workersTaskRunTime.get(workerNodeKey).weight - ).toBeGreaterThan(0) - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(workerChoiceStrategy) - .workersTaskRunTime.get(workerNodeKey).runTime - ).toBe(0) - } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workerVirtualTaskRunTime + ).toBe(0) } } await pool.destroy() @@ -131,14 +122,15 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().runTime - ).toBe(false) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime - ).toBe(false) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime - ).toBe(false) + pool.workerChoiceStrategyContext.getRequiredStatistics() + ).toStrictEqual({ + runTime: false, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false + }) await pool.destroy() pool = new DynamicThreadPool( min, @@ -147,14 +139,15 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().runTime - ).toBe(false) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime - ).toBe(false) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime - ).toBe(false) + pool.workerChoiceStrategyContext.getRequiredStatistics() + ).toStrictEqual({ + runTime: false, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false + }) // We need to clean up the resources after our test await pool.destroy() }) @@ -166,12 +159,32 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN } ) // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose` - const promises = [] + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - promises.push(pool.execute()) + promises.add(pool.execute()) } await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage).toStrictEqual({ + run: maxMultiplier, + running: 0, + runTime: 0, + runTimeHistory: expect.any(CircularArray), + avgRunTime: 0, + medRunTime: 0, + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) + } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + WorkerChoiceStrategies.ROUND_ROBIN + ).nextWorkerNodeId + ).toBe(0) // We need to clean up the resources after our test await pool.destroy() }) @@ -184,12 +197,32 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN } ) // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose` - const promises = [] + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - promises.push(pool.execute()) + promises.add(pool.execute()) } await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage).toStrictEqual({ + run: maxMultiplier, + running: 0, + runTime: 0, + runTimeHistory: expect.any(CircularArray), + avgRunTime: 0, + medRunTime: 0, + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) + } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + WorkerChoiceStrategies.ROUND_ROBIN + ).nextWorkerNodeId + ).toBe(0) // We need to clean up the resources after our test await pool.destroy() }) @@ -203,7 +236,7 @@ describe('Selection strategies test suite', () => { ) let results = new Set() for (let i = 0; i < max; i++) { - results.add(pool.chooseWorkerNode()[1].worker.id) + results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.id) } expect(results.size).toBe(max) await pool.destroy() @@ -214,7 +247,7 @@ describe('Selection strategies test suite', () => { ) results = new Set() for (let i = 0; i < max; i++) { - results.add(pool.chooseWorkerNode()[1].worker.threadId) + results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.threadId) } expect(results.size).toBe(max) await pool.destroy() @@ -260,22 +293,23 @@ describe('Selection strategies test suite', () => { await pool.destroy() }) - it('Verify LESS_USED strategy default tasks usage statistics requirements', async () => { - const workerChoiceStrategy = WorkerChoiceStrategies.LESS_USED + it('Verify LEAST_USED strategy default tasks usage statistics requirements', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED let pool = new FixedThreadPool( max, './tests/worker-files/thread/testWorker.js', { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().runTime - ).toBe(false) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime - ).toBe(false) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime - ).toBe(false) + pool.workerChoiceStrategyContext.getRequiredStatistics() + ).toStrictEqual({ + runTime: false, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false + }) await pool.destroy() pool = new DynamicThreadPool( min, @@ -284,69 +318,101 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().runTime - ).toBe(false) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime - ).toBe(false) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime - ).toBe(false) + pool.workerChoiceStrategyContext.getRequiredStatistics() + ).toStrictEqual({ + runTime: false, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false + }) // We need to clean up the resources after our test await pool.destroy() }) - it('Verify LESS_USED strategy can be run in a fixed pool', async () => { + it('Verify LEAST_USED strategy can be run in a fixed pool', async () => { const pool = new FixedThreadPool( max, './tests/worker-files/thread/testWorker.js', - { workerChoiceStrategy: WorkerChoiceStrategies.LESS_USED } + { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED } ) - // TODO: Create a better test to cover `LessUsedWorkerChoiceStrategy#choose` - const promises = [] + // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - promises.push(pool.execute()) + promises.add(pool.execute()) } await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage).toStrictEqual({ + run: maxMultiplier, + running: 0, + runTime: 0, + runTimeHistory: expect.any(CircularArray), + avgRunTime: 0, + medRunTime: 0, + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) + } // We need to clean up the resources after our test await pool.destroy() }) - it('Verify LESS_USED strategy can be run in a dynamic pool', async () => { + it('Verify LEAST_USED strategy can be run in a dynamic pool', async () => { const pool = new DynamicThreadPool( min, max, './tests/worker-files/thread/testWorker.js', - { workerChoiceStrategy: WorkerChoiceStrategies.LESS_USED } + { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED } ) - // TODO: Create a better test to cover `LessUsedWorkerChoiceStrategy#choose` - const promises = [] + // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - promises.push(pool.execute()) + promises.add(pool.execute()) } await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage).toStrictEqual({ + run: maxMultiplier, + running: 0, + runTime: 0, + runTimeHistory: expect.any(CircularArray), + avgRunTime: 0, + medRunTime: 0, + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) + } // 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 + it('Verify LEAST_BUSY strategy default tasks usage statistics requirements', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY let pool = new FixedThreadPool( max, './tests/worker-files/thread/testWorker.js', { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().runTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime - ).toBe(false) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime - ).toBe(false) + pool.workerChoiceStrategyContext.getRequiredStatistics() + ).toStrictEqual({ + runTime: true, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false + }) await pool.destroy() pool = new DynamicThreadPool( min, @@ -355,49 +421,86 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().runTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime - ).toBe(false) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime - ).toBe(false) + pool.workerChoiceStrategyContext.getRequiredStatistics() + ).toStrictEqual({ + runTime: true, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false + }) // We need to clean up the resources after our test await pool.destroy() }) - it('Verify LESS_BUSY strategy can be run in a fixed pool', async () => { + it('Verify LEAST_BUSY strategy can be run in a fixed pool', async () => { const pool = new FixedThreadPool( max, './tests/worker-files/thread/testWorker.js', - { workerChoiceStrategy: WorkerChoiceStrategies.LESS_BUSY } + { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY } ) - // TODO: Create a better test to cover `LessBusyWorkerChoiceStrategy#choose` - const promises = [] + // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - promises.push(pool.execute()) + promises.add(pool.execute()) } await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage).toStrictEqual({ + run: expect.any(Number), + running: 0, + runTime: expect.any(Number), + runTimeHistory: expect.any(CircularArray), + avgRunTime: 0, + medRunTime: 0, + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) + expect(workerNode.tasksUsage.run).toBeGreaterThan(0) + expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier) + expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0) + } // We need to clean up the resources after our test await pool.destroy() }) - it('Verify LESS_BUSY strategy can be run in a dynamic pool', async () => { + it('Verify LEAST_BUSY strategy can be run in a dynamic pool', async () => { const pool = new DynamicThreadPool( min, max, './tests/worker-files/thread/testWorker.js', - { workerChoiceStrategy: WorkerChoiceStrategies.LESS_BUSY } + { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY } ) - // TODO: Create a better test to cover `LessBusyWorkerChoiceStrategy#choose` - const promises = [] + // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - promises.push(pool.execute()) + promises.add(pool.execute()) } await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage).toStrictEqual({ + run: expect.any(Number), + running: 0, + runTime: expect.any(Number), + runTimeHistory: expect.any(CircularArray), + avgRunTime: 0, + medRunTime: 0, + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) + expect(workerNode.tasksUsage.run).toBeGreaterThan(0) + expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier) + expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0) + } // We need to clean up the resources after our test await pool.destroy() }) @@ -410,14 +513,15 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().runTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime - ).toBe(false) + pool.workerChoiceStrategyContext.getRequiredStatistics() + ).toStrictEqual({ + runTime: true, + avgRunTime: true, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false + }) await pool.destroy() pool = new DynamicThreadPool( min, @@ -426,14 +530,15 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().runTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime - ).toBe(false) + pool.workerChoiceStrategyContext.getRequiredStatistics() + ).toStrictEqual({ + runTime: true, + avgRunTime: true, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false + }) // We need to clean up the resources after our test await pool.destroy() }) @@ -445,16 +550,33 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE } ) // TODO: Create a better test to cover `FairShareChoiceStrategy#choose` - const promises = [] + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - promises.push(pool.execute()) + promises.add(pool.execute()) } await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage).toStrictEqual({ + run: maxMultiplier, + running: 0, + runTime: expect.any(Number), + runTimeHistory: expect.any(CircularArray), + avgRunTime: expect.any(Number), + medRunTime: 0, + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) + expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0) + expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThan(0) + } expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( pool.workerChoiceStrategyContext.workerChoiceStrategy - ).workerLastVirtualTaskTimestamp.size + ).workersVirtualTaskEndTimestamp.length ).toBe(pool.workerNodes.length) // We need to clean up the resources after our test await pool.destroy() @@ -468,24 +590,39 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE } ) // TODO: Create a better test to cover `FairShareChoiceStrategy#choose` - const promises = [] + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - promises.push(pool.execute()) + promises.add(pool.execute()) } await Promise.all(promises) - // if (process.platform !== 'win32') { - // expect( - // pool.workerChoiceStrategyContext.workerChoiceStrategies.get( - // pool.workerChoiceStrategyContext.workerChoiceStrategy - // ).workerLastVirtualTaskTimestamp.size - // ).toBe(pool.workerNodes.length) - // } + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage).toStrictEqual({ + run: maxMultiplier, + running: 0, + runTime: expect.any(Number), + runTimeHistory: expect.any(CircularArray), + avgRunTime: expect.any(Number), + medRunTime: 0, + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) + expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0) + expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThan(0) + } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).workersVirtualTaskEndTimestamp.length + ).toBe(pool.workerNodes.length) // We need to clean up the resources after our test await pool.destroy() }) - it('Verify FAIR_SHARE strategy can be run in a dynamic pool with median run time statistic', async () => { + it('Verify FAIR_SHARE strategy can be run in a dynamic pool with median runtime statistic', async () => { const pool = new DynamicThreadPool( min, max, @@ -498,25 +635,34 @@ describe('Selection strategies test suite', () => { } ) // TODO: Create a better test to cover `FairShareChoiceStrategy#choose` - const promises = [] + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - promises.push(pool.execute()) + promises.add(pool.execute()) } await Promise.all(promises) 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).toStrictEqual({ + run: maxMultiplier, + running: 0, + runTime: expect.any(Number), + runTimeHistory: expect.any(CircularArray), + avgRunTime: 0, + medRunTime: expect.any(Number), + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) + expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0) expect(workerNode.tasksUsage.medRunTime).toBeGreaterThan(0) } - // if (process.platform !== 'win32') { - // expect( - // pool.workerChoiceStrategyContext.workerChoiceStrategies.get( - // pool.workerChoiceStrategyContext.workerChoiceStrategy - // ).workerLastVirtualTaskTimestamp.size - // ).toBe(pool.workerNodes.length) - // } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).workersVirtualTaskEndTimestamp.length + ).toBe(pool.workerNodes.length) // We need to clean up the resources after our test await pool.destroy() }) @@ -530,23 +676,32 @@ describe('Selection strategies test suite', () => { expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( workerChoiceStrategy - ).workerLastVirtualTaskTimestamp - ).toBeDefined() + ).workersVirtualTaskEndTimestamp + ).toBeInstanceOf(Array) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskEndTimestamp.length + ).toBe(0) + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskEndTimestamp[0] = performance.now() + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskEndTimestamp.length + ).toBe(1) pool.setWorkerChoiceStrategy(workerChoiceStrategy) - for (const workerNodeKey of pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(pool.workerChoiceStrategyContext.workerChoiceStrategy) - .workerLastVirtualTaskTimestamp.keys()) { - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(pool.workerChoiceStrategyContext.workerChoiceStrategy) - .workerLastVirtualTaskTimestamp.get(workerNodeKey).start - ).toBe(0) - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(pool.workerChoiceStrategyContext.workerChoiceStrategy) - .workerLastVirtualTaskTimestamp.get(workerNodeKey).end - ).toBe(0) - } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskEndTimestamp + ).toBeInstanceOf(Array) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskEndTimestamp.length + ).toBe(0) await pool.destroy() pool = new DynamicThreadPool( min, @@ -556,23 +711,32 @@ describe('Selection strategies test suite', () => { expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( workerChoiceStrategy - ).workerLastVirtualTaskTimestamp - ).toBeDefined() + ).workersVirtualTaskEndTimestamp + ).toBeInstanceOf(Array) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskEndTimestamp.length + ).toBe(0) + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskEndTimestamp[0] = performance.now() + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskEndTimestamp.length + ).toBe(1) pool.setWorkerChoiceStrategy(workerChoiceStrategy) - for (const workerNodeKey of pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(pool.workerChoiceStrategyContext.workerChoiceStrategy) - .workerLastVirtualTaskTimestamp.keys()) { - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(pool.workerChoiceStrategyContext.workerChoiceStrategy) - .workerLastVirtualTaskTimestamp.get(workerNodeKey).start - ).toBe(0) - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(pool.workerChoiceStrategyContext.workerChoiceStrategy) - .workerLastVirtualTaskTimestamp.get(workerNodeKey).end - ).toBe(0) - } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskEndTimestamp + ).toBeInstanceOf(Array) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workersVirtualTaskEndTimestamp.length + ).toBe(0) // We need to clean up the resources after our test await pool.destroy() }) @@ -585,14 +749,15 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().runTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime - ).toBe(false) + pool.workerChoiceStrategyContext.getRequiredStatistics() + ).toStrictEqual({ + runTime: true, + avgRunTime: true, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false + }) await pool.destroy() pool = new DynamicThreadPool( min, @@ -601,14 +766,15 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().runTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime - ).toBe(false) + pool.workerChoiceStrategyContext.getRequiredStatistics() + ).toStrictEqual({ + runTime: true, + avgRunTime: true, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false + }) // We need to clean up the resources after our test await pool.destroy() }) @@ -620,17 +786,41 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN } ) // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose` - const promises = [] + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - promises.push(pool.execute()) + promises.add(pool.execute()) } await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage).toStrictEqual({ + run: expect.any(Number), + running: 0, + runTime: expect.any(Number), + runTimeHistory: expect.any(CircularArray), + avgRunTime: expect.any(Number), + medRunTime: 0, + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) + expect(workerNode.tasksUsage.run).toBeGreaterThanOrEqual(0) + expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier) + expect(workerNode.tasksUsage.runTime).toBeGreaterThanOrEqual(0) + expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0) + } expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( pool.workerChoiceStrategyContext.workerChoiceStrategy - ).workersTaskRunTime.size - ).toBe(pool.workerNodes.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() }) @@ -643,27 +833,46 @@ 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.workerChoiceStrategies.get( - pool.workerChoiceStrategyContext.workerChoiceStrategy - ).defaultWorkerWeight * 50 + const promises = new Set() + const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - promises.push(pool.execute()) + promises.add(pool.execute()) } await Promise.all(promises) - if (process.platform !== 'win32') { - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategies.get( - pool.workerChoiceStrategyContext.workerChoiceStrategy - ).workersTaskRunTime.size - ).toBe(pool.workerNodes.length) + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage).toStrictEqual({ + run: expect.any(Number), + running: 0, + runTime: expect.any(Number), + runTimeHistory: expect.any(CircularArray), + avgRunTime: expect.any(Number), + medRunTime: 0, + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) + expect(workerNode.tasksUsage.run).toBeGreaterThan(0) + expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier) + expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0) + expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThan(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 can be run in a dynamic pool with median run time statistic', async () => { + it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool with median runtime statistic', async () => { const pool = new DynamicThreadPool( min, max, @@ -676,25 +885,41 @@ describe('Selection strategies test suite', () => { } ) // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose` - const promises = [] + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - promises.push(pool.execute()) + promises.add(pool.execute()) } await Promise.all(promises) 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).toStrictEqual({ + run: expect.any(Number), + running: 0, + runTime: expect.any(Number), + runTimeHistory: expect.any(CircularArray), + avgRunTime: 0, + medRunTime: expect.any(Number), + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) + expect(workerNode.tasksUsage.run).toBeGreaterThan(0) + expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier) + expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0) expect(workerNode.tasksUsage.medRunTime).toBeGreaterThan(0) } - // if (process.platform !== 'win32') { - // expect( - // pool.workerChoiceStrategyContext.workerChoiceStrategies.get( - // pool.workerChoiceStrategyContext.workerChoiceStrategy - // ).workersTaskRunTime.size - // ).toBe(pool.workerNodes.length) - // } + 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() }) @@ -718,7 +943,7 @@ describe('Selection strategies test suite', () => { expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( workerChoiceStrategy - ).workersTaskRunTime + ).workerVirtualTaskRunTime ).toBeDefined() pool.setWorkerChoiceStrategy(workerChoiceStrategy) expect( @@ -731,15 +956,11 @@ describe('Selection strategies test suite', () => { pool.workerChoiceStrategyContext.workerChoiceStrategy ).defaultWorkerWeight ).toBeGreaterThan(0) - for (const workerNodeKey of pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(pool.workerChoiceStrategyContext.workerChoiceStrategy) - .workersTaskRunTime.keys()) { - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(pool.workerChoiceStrategyContext.workerChoiceStrategy) - .workersTaskRunTime.get(workerNodeKey).runTime - ).toBe(0) - } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).workerVirtualTaskRunTime + ).toBe(0) await pool.destroy() pool = new DynamicThreadPool( min, @@ -759,7 +980,7 @@ describe('Selection strategies test suite', () => { expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( workerChoiceStrategy - ).workersTaskRunTime + ).workerVirtualTaskRunTime ).toBeDefined() pool.setWorkerChoiceStrategy(workerChoiceStrategy) expect( @@ -772,15 +993,169 @@ describe('Selection strategies test suite', () => { pool.workerChoiceStrategyContext.workerChoiceStrategy ).defaultWorkerWeight ).toBeGreaterThan(0) - for (const workerNodeKey of pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(pool.workerChoiceStrategyContext.workerChoiceStrategy) - .workersTaskRunTime.keys()) { - expect( - pool.workerChoiceStrategyContext.workerChoiceStrategies - .get(pool.workerChoiceStrategyContext.workerChoiceStrategy) - .workersTaskRunTime.get(workerNodeKey).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 INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => { + const workerChoiceStrategy = + WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN + let pool = new FixedThreadPool( + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics() + ).toStrictEqual({ + runTime: false, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false + }) + await pool.destroy() + pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics() + ).toStrictEqual({ + runTime: false, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false + }) + // We need to clean up the resources after our test + await pool.destroy() + }) + + it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => { + const pool = new FixedThreadPool( + max, + './tests/worker-files/thread/testWorker.js', + { + workerChoiceStrategy: + WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN + } + ) + // TODO: Create a better test to cover `InterleavedWeightedRoundRobinWorkerChoiceStrategy#choose` + const promises = new Set() + const maxMultiplier = 2 + for (let i = 0; i < max * maxMultiplier; i++) { + promises.add(pool.execute()) + } + await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage).toStrictEqual({ + run: maxMultiplier, + running: 0, + runTime: 0, + runTimeHistory: expect.any(CircularArray), + avgRunTime: 0, + medRunTime: 0, + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) + } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).defaultWorkerWeight + ).toBeGreaterThan(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).currentRoundId + ).toBe(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).currentWorkerNodeId + ).toBe(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).roundWeights + ).toStrictEqual([ + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).defaultWorkerWeight + ]) + // We need to clean up the resources after our test + await pool.destroy() + }) + + it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => { + const pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js', + { + workerChoiceStrategy: + WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN + } + ) + // TODO: Create a better test to cover `InterleavedWeightedRoundRobinWorkerChoiceStrategy#choose` + const promises = new Set() + const maxMultiplier = 2 + for (let i = 0; i < max * maxMultiplier; i++) { + promises.add(pool.execute()) + } + await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.tasksUsage).toStrictEqual({ + run: maxMultiplier, + running: 0, + runTime: 0, + runTimeHistory: expect.any(CircularArray), + avgRunTime: 0, + medRunTime: 0, + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).defaultWorkerWeight + ).toBeGreaterThan(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).currentRoundId + ).toBe(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).currentWorkerNodeId + ).toBe(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).roundWeights + ).toStrictEqual([ + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).defaultWorkerWeight + ]) // We need to clean up the resources after our test await pool.destroy() })