X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=tests%2Fpools%2Fselection-strategies%2Fselection-strategies.test.js;h=24a35810bb3d3327b07cf48b38317dfba9b51a20;hb=cdb517b318cd4f26b4895988ae8444d50945ddde;hp=11a9aab3caa2001b799d5a8e48526481cef7da19;hpb=20dcad1a0f8c1c6fdc0623aed98a998e8a31f185;p=poolifier.git diff --git a/tests/pools/selection-strategies/selection-strategies.test.js b/tests/pools/selection-strategies/selection-strategies.test.js index 11a9aab3..24a35810 100644 --- a/tests/pools/selection-strategies/selection-strategies.test.js +++ b/tests/pools/selection-strategies/selection-strategies.test.js @@ -5,6 +5,7 @@ const { FixedThreadPool, FixedClusterPool } = require('../../../lib') +const { CircularArray } = require('../../../lib/circular-array') describe('Selection strategies test suite', () => { const min = 0 @@ -12,12 +13,16 @@ 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_ELU).toBe('LEAST_ELU') + 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 () => { @@ -117,15 +122,15 @@ describe('Selection strategies test suite', () => { './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) + expect(pool.workerChoiceStrategyContext.getTaskStatistics()).toStrictEqual({ + runTime: false, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false, + elu: false + }) await pool.destroy() pool = new DynamicThreadPool( min, @@ -133,15 +138,15 @@ describe('Selection strategies test suite', () => { './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) + expect(pool.workerChoiceStrategyContext.getTaskStatistics()).toStrictEqual({ + runTime: false, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false, + elu: false + }) // We need to clean up the resources after our test await pool.destroy() }) @@ -153,10 +158,40 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN } ) // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - await pool.execute() + promises.add(pool.execute()) } + await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.workerUsage).toStrictEqual({ + tasks: { + executed: maxMultiplier, + executing: 0, + queued: 0, + failed: 0 + }, + runTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + waitTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + elu: undefined + }) + } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + WorkerChoiceStrategies.ROUND_ROBIN + ).nextWorkerNodeId + ).toBe(0) // We need to clean up the resources after our test await pool.destroy() }) @@ -169,10 +204,40 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN } ) // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - await pool.execute() + promises.add(pool.execute()) + } + await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.workerUsage).toStrictEqual({ + tasks: { + executed: maxMultiplier, + executing: 0, + queued: 0, + failed: 0 + }, + runTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + waitTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + elu: undefined + }) } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + WorkerChoiceStrategies.ROUND_ROBIN + ).nextWorkerNodeId + ).toBe(0) // We need to clean up the resources after our test await pool.destroy() }) @@ -243,22 +308,22 @@ 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) + expect(pool.workerChoiceStrategyContext.getTaskStatistics()).toStrictEqual({ + runTime: false, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false, + elu: false + }) await pool.destroy() pool = new DynamicThreadPool( min, @@ -266,66 +331,117 @@ describe('Selection strategies test suite', () => { './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) + expect(pool.workerChoiceStrategyContext.getTaskStatistics()).toStrictEqual({ + runTime: false, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false, + elu: 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` + // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - await pool.execute() + promises.add(pool.execute()) + } + await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.workerUsage).toStrictEqual({ + tasks: { + executed: maxMultiplier, + executing: 0, + queued: 0, + failed: 0 + }, + runTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + waitTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + elu: undefined + }) } // 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` + // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - await pool.execute() + promises.add(pool.execute()) + } + await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.workerUsage).toStrictEqual({ + tasks: { + executed: maxMultiplier, + executing: 0, + queued: 0, + failed: 0 + }, + runTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + waitTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + + elu: undefined + }) } // 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) + expect(pool.workerChoiceStrategyContext.getTaskStatistics()).toStrictEqual({ + runTime: true, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false, + elu: false + }) await pool.destroy() pool = new DynamicThreadPool( min, @@ -333,45 +449,107 @@ describe('Selection strategies test suite', () => { './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) + expect(pool.workerChoiceStrategyContext.getTaskStatistics()).toStrictEqual({ + runTime: true, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false, + elu: 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` + // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - await pool.execute() + promises.add(pool.execute()) + } + await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.workerUsage).toStrictEqual({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + failed: 0 + }, + runTime: { + aggregation: expect.any(Number), + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + waitTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + elu: undefined + }) + expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0) + expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) + expect(workerNode.workerUsage.runTime.aggregation).toBeGreaterThanOrEqual( + 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` + // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - await pool.execute() + promises.add(pool.execute()) + } + await Promise.all(promises) + for (const workerNode of pool.workerNodes) { + expect(workerNode.workerUsage).toStrictEqual({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + failed: 0 + }, + runTime: { + aggregation: expect.any(Number), + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + waitTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + elu: undefined + }) + expect(workerNode.workerUsage.tasks.executed).toBeGreaterThan(0) + expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) + expect(workerNode.workerUsage.runTime.aggregation).toBeGreaterThan(0) } // We need to clean up the resources after our test await pool.destroy() @@ -384,15 +562,15 @@ describe('Selection strategies test suite', () => { './tests/worker-files/thread/testWorker.js', { workerChoiceStrategy } ) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().runTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime - ).toBe(false) + expect(pool.workerChoiceStrategyContext.getTaskStatistics()).toStrictEqual({ + runTime: true, + avgRunTime: true, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false, + elu: false + }) await pool.destroy() pool = new DynamicThreadPool( min, @@ -400,15 +578,15 @@ describe('Selection strategies test suite', () => { './tests/worker-files/thread/testWorker.js', { workerChoiceStrategy } ) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().runTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime - ).toBe(false) + expect(pool.workerChoiceStrategyContext.getTaskStatistics()).toStrictEqual({ + runTime: true, + avgRunTime: true, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false, + elu: false + }) // We need to clean up the resources after our test await pool.destroy() }) @@ -420,15 +598,36 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE } ) // TODO: Create a better test to cover `FairShareChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - await 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).toBeGreaterThanOrEqual(0) - expect(workerNode.tasksUsage.medRunTime).toBeDefined() - expect(workerNode.tasksUsage.medRunTime).toBe(0) + expect(workerNode.workerUsage).toStrictEqual({ + tasks: { + executed: maxMultiplier, + executing: 0, + queued: 0, + failed: 0 + }, + runTime: { + aggregation: expect.any(Number), + average: expect.any(Number), + median: 0, + history: expect.any(CircularArray) + }, + waitTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + elu: undefined + }) + expect(workerNode.workerUsage.runTime.aggregation).toBeGreaterThan(0) + expect(workerNode.workerUsage.runTime.average).toBeGreaterThan(0) } expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -447,15 +646,36 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE } ) // TODO: Create a better test to cover `FairShareChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - await 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).toBeGreaterThanOrEqual(0) - expect(workerNode.tasksUsage.medRunTime).toBeDefined() - expect(workerNode.tasksUsage.medRunTime).toBe(0) + expect(workerNode.workerUsage).toStrictEqual({ + tasks: { + executed: maxMultiplier, + executing: 0, + queued: 0, + failed: 0 + }, + runTime: { + aggregation: expect.any(Number), + average: expect.any(Number), + median: 0, + history: expect.any(CircularArray) + }, + waitTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + elu: undefined + }) + expect(workerNode.workerUsage.runTime.aggregation).toBeGreaterThan(0) + expect(workerNode.workerUsage.runTime.average).toBeGreaterThan(0) } expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -466,7 +686,7 @@ describe('Selection strategies test suite', () => { 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, @@ -479,15 +699,36 @@ describe('Selection strategies test suite', () => { } ) // TODO: Create a better test to cover `FairShareChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - await 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.medRunTime).toBeGreaterThanOrEqual(0) + expect(workerNode.workerUsage).toStrictEqual({ + tasks: { + executed: maxMultiplier, + executing: 0, + queued: 0, + failed: 0 + }, + runTime: { + aggregation: expect.any(Number), + average: 0, + median: expect.any(Number), + history: expect.any(CircularArray) + }, + waitTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + elu: undefined + }) + expect(workerNode.workerUsage.runTime.aggregation).toBeGreaterThan(0) + expect(workerNode.workerUsage.runTime.median).toBeGreaterThan(0) } expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -579,15 +820,15 @@ describe('Selection strategies test suite', () => { './tests/worker-files/thread/testWorker.js', { workerChoiceStrategy } ) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().runTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime - ).toBe(false) + expect(pool.workerChoiceStrategyContext.getTaskStatistics()).toStrictEqual({ + runTime: true, + avgRunTime: true, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false, + elu: false + }) await pool.destroy() pool = new DynamicThreadPool( min, @@ -595,15 +836,15 @@ describe('Selection strategies test suite', () => { './tests/worker-files/thread/testWorker.js', { workerChoiceStrategy } ) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().runTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().avgRunTime - ).toBe(true) - expect( - pool.workerChoiceStrategyContext.getRequiredStatistics().medRunTime - ).toBe(false) + expect(pool.workerChoiceStrategyContext.getTaskStatistics()).toStrictEqual({ + runTime: true, + avgRunTime: true, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false, + elu: false + }) // We need to clean up the resources after our test await pool.destroy() }) @@ -615,15 +856,42 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN } ) // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - await 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).toBeGreaterThanOrEqual(0) - expect(workerNode.tasksUsage.medRunTime).toBeDefined() - expect(workerNode.tasksUsage.medRunTime).toBe(0) + expect(workerNode.workerUsage).toStrictEqual({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + failed: 0 + }, + runTime: { + aggregation: expect.any(Number), + average: expect.any(Number), + median: 0, + history: expect.any(CircularArray) + }, + waitTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + elu: undefined + }) + expect(workerNode.workerUsage.tasks.executed).toBeGreaterThanOrEqual(0) + expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) + expect(workerNode.workerUsage.runTime.aggregation).toBeGreaterThanOrEqual( + 0 + ) + expect(workerNode.workerUsage.runTime.average).toBeGreaterThanOrEqual(0) } expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -647,15 +915,40 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN } ) // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - await 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).toBeGreaterThanOrEqual(0) - expect(workerNode.tasksUsage.medRunTime).toBeDefined() - expect(workerNode.tasksUsage.medRunTime).toBe(0) + expect(workerNode.workerUsage).toStrictEqual({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + failed: 0 + }, + runTime: { + aggregation: expect.any(Number), + average: expect.any(Number), + median: 0, + history: expect.any(CircularArray) + }, + waitTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + elu: undefined + }) + expect(workerNode.workerUsage.tasks.executed).toBeGreaterThan(0) + expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) + expect(workerNode.workerUsage.runTime.aggregation).toBeGreaterThan(0) + expect(workerNode.workerUsage.runTime.average).toBeGreaterThan(0) } expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -671,7 +964,7 @@ describe('Selection strategies test suite', () => { 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, @@ -684,15 +977,40 @@ describe('Selection strategies test suite', () => { } ) // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose` + const promises = new Set() const maxMultiplier = 2 for (let i = 0; i < max * maxMultiplier; i++) { - await 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.medRunTime).toBeGreaterThanOrEqual(0) + expect(workerNode.workerUsage).toStrictEqual({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + failed: 0 + }, + runTime: { + aggregation: expect.any(Number), + average: 0, + median: expect.any(Number), + history: expect.any(CircularArray) + }, + waitTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + elu: undefined + }) + expect(workerNode.workerUsage.tasks.executed).toBeGreaterThan(0) + expect(workerNode.workerUsage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) + expect(workerNode.workerUsage.runTime.aggregation).toBeGreaterThan(0) + expect(workerNode.workerUsage.runTime.median).toBeGreaterThan(0) } expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -786,6 +1104,280 @@ describe('Selection strategies test suite', () => { 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.getTaskStatistics()).toStrictEqual({ + runTime: false, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false, + elu: false + }) + await pool.destroy() + pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.workerChoiceStrategyContext.getTaskStatistics()).toStrictEqual({ + runTime: false, + avgRunTime: false, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false, + elu: 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.workerUsage).toStrictEqual({ + tasks: { + executed: maxMultiplier, + executing: 0, + queued: 0, + failed: 0 + }, + runTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + waitTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + elu: undefined + }) + } + 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.workerUsage).toStrictEqual({ + tasks: { + executed: maxMultiplier, + executing: 0, + queued: 0, + failed: 0 + }, + runTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + waitTime: { + aggregation: 0, + average: 0, + median: 0, + history: expect.any(CircularArray) + }, + elu: undefined + }) + } + 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 internals are resets after setting it', async () => { + const workerChoiceStrategy = + WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN + let pool = new FixedThreadPool( + max, + './tests/worker-files/thread/testWorker.js' + ) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).currentRoundId + ).toBeDefined() + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).currentWorkerNodeId + ).toBeDefined() + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).defaultWorkerWeight + ).toBeDefined() + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).roundWeights + ).toBeDefined() + pool.setWorkerChoiceStrategy(workerChoiceStrategy) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).currentRoundId + ).toBe(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).currentWorkerNodeId + ).toBe(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).defaultWorkerWeight + ).toBeGreaterThan(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).roundWeights + ).toStrictEqual([ + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).defaultWorkerWeight + ]) + await pool.destroy() + pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js' + ) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).currentRoundId + ).toBeDefined() + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).currentWorkerNodeId + ).toBeDefined() + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).defaultWorkerWeight + ).toBeDefined() + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).roundWeights + ).toBeDefined() + pool.setWorkerChoiceStrategy(workerChoiceStrategy) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).currentWorkerNodeId + ).toBe(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).defaultWorkerWeight + ).toBeGreaterThan(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + 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 unknown strategy throw error', () => { expect( () =>