X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;ds=sidebyside;f=tests%2Fpools%2Fselection-strategies%2Fselection-strategies.test.js;h=6671b57781b1f51c15bdf0717f3c96cafcf0ca76;hb=3d6dd312a7825521cce506ebb7443bae36a111e6;hp=19ac5ea3a9a930c67996efd31fb5a683aa856685;hpb=ee9f52959d4996f56712791e73994dfbeb0370f0;p=poolifier.git diff --git a/tests/pools/selection-strategies/selection-strategies.test.js b/tests/pools/selection-strategies/selection-strategies.test.js index 19ac5ea3..6671b577 100644 --- a/tests/pools/selection-strategies/selection-strategies.test.js +++ b/tests/pools/selection-strategies/selection-strategies.test.js @@ -1,9 +1,9 @@ const { expect } = require('expect') const { - WorkerChoiceStrategies, DynamicThreadPool, + FixedClusterPool, FixedThreadPool, - FixedClusterPool + WorkerChoiceStrategies } = require('../../../lib') const { CircularArray } = require('../../../lib/circular-array') @@ -15,10 +15,14 @@ describe('Selection strategies test suite', () => { expect(WorkerChoiceStrategies.ROUND_ROBIN).toBe('ROUND_ROBIN') expect(WorkerChoiceStrategies.LEAST_USED).toBe('LEAST_USED') expect(WorkerChoiceStrategies.LEAST_BUSY).toBe('LEAST_BUSY') + expect(WorkerChoiceStrategies.LEAST_ELU).toBe('LEAST_ELU') 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 () => { @@ -75,7 +79,7 @@ describe('Selection strategies test suite', () => { expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( workerChoiceStrategy - ).nextWorkerNodeId + ).nextWorkerNodeKey ).toBe(0) } else if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) { expect( @@ -94,7 +98,7 @@ describe('Selection strategies test suite', () => { expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( workerChoiceStrategy - ).currentWorkerNodeId + ).nextWorkerNodeKey ).toBe(0) expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -111,7 +115,31 @@ describe('Selection strategies test suite', () => { await pool.destroy() }) - it('Verify ROUND_ROBIN strategy default tasks usage statistics requirements', async () => { + it('Verify ROUND_ROBIN strategy default policy', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN + let pool = new FixedThreadPool( + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({ + useDynamicWorker: true + }) + await pool.destroy() + pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({ + useDynamicWorker: true + }) + // We need to clean up the resources after our test + await pool.destroy() + }) + + it('Verify ROUND_ROBIN strategy default tasks statistics requirements', async () => { const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN let pool = new FixedThreadPool( max, @@ -119,14 +147,23 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics() + pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() ).toStrictEqual({ - runTime: false, - avgRunTime: false, - medRunTime: false, - waitTime: false, - avgWaitTime: false, - medWaitTime: false + runTime: { + aggregate: false, + average: false, + median: false + }, + waitTime: { + aggregate: false, + average: false, + median: false + }, + elu: { + aggregate: false, + average: false, + median: false + } }) await pool.destroy() pool = new DynamicThreadPool( @@ -136,14 +173,23 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics() + pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() ).toStrictEqual({ - runTime: false, - avgRunTime: false, - medRunTime: false, - waitTime: false, - avgWaitTime: false, - medWaitTime: false + runTime: { + aggregate: false, + average: false, + median: false + }, + waitTime: { + aggregate: false, + average: false, + median: false + }, + elu: { + aggregate: false, + average: false, + median: false + } }) // We need to clean up the resources after our test await pool.destroy() @@ -163,22 +209,35 @@ describe('Selection strategies test suite', () => { } await Promise.all(promises) for (const workerNode of pool.workerNodes) { - expect(workerNode.tasksUsage).toStrictEqual({ - run: expect.any(Number), - 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(workerNode.usage).toStrictEqual({ + tasks: { + executed: maxMultiplier, + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: { + history: expect.any(CircularArray) + }, + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: { + history: expect.any(CircularArray) + }, + active: { + history: expect.any(CircularArray) + } + } }) - expect(workerNode.tasksUsage.run).toBeGreaterThan(0) - expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier) } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + WorkerChoiceStrategies.ROUND_ROBIN + ).nextWorkerNodeKey + ).toBe(0) // We need to clean up the resources after our test await pool.destroy() }) @@ -198,22 +257,35 @@ describe('Selection strategies test suite', () => { } await Promise.all(promises) for (const workerNode of pool.workerNodes) { - expect(workerNode.tasksUsage).toStrictEqual({ - run: expect.any(Number), - 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(workerNode.usage).toStrictEqual({ + tasks: { + executed: maxMultiplier, + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: { + history: expect.any(CircularArray) + }, + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: { + history: expect.any(CircularArray) + }, + active: { + history: expect.any(CircularArray) + } + } }) - expect(workerNode.tasksUsage.run).toBeGreaterThan(0) - expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier) } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + WorkerChoiceStrategies.ROUND_ROBIN + ).nextWorkerNodeKey + ).toBe(0) // We need to clean up the resources after our test await pool.destroy() }) @@ -254,13 +326,13 @@ describe('Selection strategies test suite', () => { expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( workerChoiceStrategy - ).nextWorkerNodeId + ).nextWorkerNodeKey ).toBeDefined() pool.setWorkerChoiceStrategy(workerChoiceStrategy) expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( pool.workerChoiceStrategyContext.workerChoiceStrategy - ).nextWorkerNodeId + ).nextWorkerNodeKey ).toBe(0) await pool.destroy() pool = new DynamicThreadPool( @@ -272,19 +344,43 @@ describe('Selection strategies test suite', () => { expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( workerChoiceStrategy - ).nextWorkerNodeId + ).nextWorkerNodeKey ).toBeDefined() pool.setWorkerChoiceStrategy(workerChoiceStrategy) expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( pool.workerChoiceStrategyContext.workerChoiceStrategy - ).nextWorkerNodeId + ).nextWorkerNodeKey ).toBe(0) // We need to clean up the resources after our test await pool.destroy() }) - it('Verify LEAST_USED strategy default tasks usage statistics requirements', async () => { + it('Verify LEAST_USED strategy default policy', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED + let pool = new FixedThreadPool( + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({ + useDynamicWorker: false + }) + await pool.destroy() + pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({ + useDynamicWorker: false + }) + // We need to clean up the resources after our test + await pool.destroy() + }) + + it('Verify LEAST_USED strategy default tasks statistics requirements', async () => { const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED let pool = new FixedThreadPool( max, @@ -292,14 +388,23 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics() + pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() ).toStrictEqual({ - runTime: false, - avgRunTime: false, - medRunTime: false, - waitTime: false, - avgWaitTime: false, - medWaitTime: false + runTime: { + aggregate: false, + average: false, + median: false + }, + waitTime: { + aggregate: false, + average: false, + median: false + }, + elu: { + aggregate: false, + average: false, + median: false + } }) await pool.destroy() pool = new DynamicThreadPool( @@ -309,14 +414,23 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics() + pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() ).toStrictEqual({ - runTime: false, - avgRunTime: false, - medRunTime: false, - waitTime: false, - avgWaitTime: false, - medWaitTime: false + runTime: { + aggregate: false, + average: false, + median: false + }, + waitTime: { + aggregate: false, + average: false, + median: false + }, + elu: { + aggregate: false, + average: false, + median: false + } }) // We need to clean up the resources after our test await pool.destroy() @@ -336,21 +450,33 @@ describe('Selection strategies test suite', () => { } await Promise.all(promises) for (const workerNode of pool.workerNodes) { - expect(workerNode.tasksUsage).toStrictEqual({ - run: expect.any(Number), - 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(workerNode.usage).toStrictEqual({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: { + history: expect.any(CircularArray) + }, + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: { + history: expect.any(CircularArray) + }, + active: { + history: expect.any(CircularArray) + } + } }) - expect(workerNode.tasksUsage.run).toBeGreaterThan(0) - expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier) + expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) } // We need to clean up the resources after our test await pool.destroy() @@ -371,27 +497,63 @@ describe('Selection strategies test suite', () => { } await Promise.all(promises) for (const workerNode of pool.workerNodes) { - expect(workerNode.tasksUsage).toStrictEqual({ - run: expect.any(Number), - 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(workerNode.usage).toStrictEqual({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: { + history: expect.any(CircularArray) + }, + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: { + history: expect.any(CircularArray) + }, + active: { + history: expect.any(CircularArray) + } + } }) - expect(workerNode.tasksUsage.run).toBeGreaterThan(0) - expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier) + expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) } // We need to clean up the resources after our test await pool.destroy() }) - it('Verify LEAST_BUSY strategy default tasks usage statistics requirements', async () => { + it('Verify LEAST_BUSY strategy default policy', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY + let pool = new FixedThreadPool( + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({ + useDynamicWorker: false + }) + await pool.destroy() + pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({ + useDynamicWorker: false + }) + // We need to clean up the resources after our test + await pool.destroy() + }) + + it('Verify LEAST_BUSY strategy default tasks statistics requirements', async () => { const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY let pool = new FixedThreadPool( max, @@ -399,14 +561,23 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics() + pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() ).toStrictEqual({ - runTime: true, - avgRunTime: false, - medRunTime: false, - waitTime: false, - avgWaitTime: false, - medWaitTime: false + runTime: { + aggregate: true, + average: false, + median: false + }, + waitTime: { + aggregate: true, + average: false, + median: false + }, + elu: { + aggregate: false, + average: false, + median: false + } }) await pool.destroy() pool = new DynamicThreadPool( @@ -416,14 +587,23 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics() + pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() ).toStrictEqual({ - runTime: true, - avgRunTime: false, - medRunTime: false, - waitTime: false, - avgWaitTime: false, - medWaitTime: false + runTime: { + aggregate: true, + average: false, + median: false + }, + waitTime: { + aggregate: true, + average: false, + median: false + }, + elu: { + aggregate: false, + average: false, + median: false + } }) // We need to clean up the resources after our test await pool.destroy() @@ -443,22 +623,43 @@ describe('Selection strategies test suite', () => { } 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.usage).toMatchObject({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: { + history: expect.any(CircularArray) + }, + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: { + history: expect.any(CircularArray) + }, + active: { + history: expect.any(CircularArray) + } + } }) - expect(workerNode.tasksUsage.run).toBeGreaterThan(0) - expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier) - expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0) + expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) + if (workerNode.usage.runTime.aggregate == null) { + expect(workerNode.usage.runTime.aggregate).toBeUndefined() + } else { + expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0) + } + if (workerNode.usage.waitTime.aggregate == null) { + expect(workerNode.usage.waitTime.aggregate).toBeUndefined() + } else { + expect(workerNode.usage.waitTime.aggregate).toBeGreaterThan(0) + } } // We need to clean up the resources after our test await pool.destroy() @@ -479,28 +680,258 @@ describe('Selection strategies test suite', () => { } 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.usage).toMatchObject({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: { + history: expect.any(CircularArray) + }, + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: { + history: expect.any(CircularArray) + }, + active: { + history: expect.any(CircularArray) + } + } }) - expect(workerNode.tasksUsage.run).toBeGreaterThan(0) - expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier) - expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0) + expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) + if (workerNode.usage.runTime.aggregate == null) { + expect(workerNode.usage.runTime.aggregate).toBeUndefined() + } else { + expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0) + } + if (workerNode.usage.waitTime.aggregate == null) { + expect(workerNode.usage.waitTime.aggregate).toBeUndefined() + } else { + expect(workerNode.usage.waitTime.aggregate).toBeGreaterThan(0) + } } // We need to clean up the resources after our test await pool.destroy() }) - it('Verify FAIR_SHARE strategy default tasks usage statistics requirements', async () => { + it('Verify LEAST_ELU strategy default policy', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU + let pool = new FixedThreadPool( + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({ + useDynamicWorker: false + }) + await pool.destroy() + pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({ + useDynamicWorker: false + }) + // We need to clean up the resources after our test + await pool.destroy() + }) + + it('Verify LEAST_ELU strategy default tasks statistics requirements', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU + let pool = new FixedThreadPool( + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect( + pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() + ).toStrictEqual({ + runTime: { + aggregate: false, + average: false, + median: false + }, + waitTime: { + aggregate: false, + average: false, + median: false + }, + elu: { + aggregate: true, + average: false, + median: false + } + }) + await pool.destroy() + pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect( + pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() + ).toStrictEqual({ + runTime: { + aggregate: false, + average: false, + median: false + }, + waitTime: { + aggregate: false, + average: false, + median: false + }, + elu: { + aggregate: true, + average: false, + median: false + } + }) + // We need to clean up the resources after our test + await pool.destroy() + }) + + it('Verify LEAST_ELU strategy can be run in a fixed pool', async () => { + const pool = new FixedThreadPool( + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_ELU } + ) + // TODO: Create a better test to cover `LeastEluWorkerChoiceStrategy#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.usage).toMatchObject({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: { + history: expect.any(CircularArray) + }, + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: expect.objectContaining({ + history: expect.any(CircularArray) + }), + active: expect.objectContaining({ + history: expect.any(CircularArray) + }) + } + }) + expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) + if (workerNode.usage.elu.utilization == null) { + expect(workerNode.usage.elu.utilization).toBeUndefined() + } else { + expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1) + } + } + // We need to clean up the resources after our test + await pool.destroy() + }) + + it('Verify LEAST_ELU strategy can be run in a dynamic pool', async () => { + const pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_ELU } + ) + // TODO: Create a better test to cover `LeastEluWorkerChoiceStrategy#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.usage).toMatchObject({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: { + history: expect.any(CircularArray) + }, + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: expect.objectContaining({ + history: expect.any(CircularArray) + }), + active: expect.objectContaining({ + history: expect.any(CircularArray) + }) + } + }) + expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) + if (workerNode.usage.elu.utilization == null) { + expect(workerNode.usage.elu.utilization).toBeUndefined() + } else { + expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1) + } + } + // We need to clean up the resources after our test + await pool.destroy() + }) + + it('Verify FAIR_SHARE strategy default policy', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE + let pool = new FixedThreadPool( + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({ + useDynamicWorker: false + }) + await pool.destroy() + pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({ + useDynamicWorker: false + }) + // We need to clean up the resources after our test + await pool.destroy() + }) + + it('Verify FAIR_SHARE strategy default tasks statistics requirements', async () => { const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE let pool = new FixedThreadPool( max, @@ -508,14 +939,23 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics() + pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() ).toStrictEqual({ - runTime: true, - avgRunTime: true, - medRunTime: false, - waitTime: false, - avgWaitTime: false, - medWaitTime: false + runTime: { + aggregate: true, + average: true, + median: false + }, + waitTime: { + aggregate: false, + average: false, + median: false + }, + elu: { + aggregate: true, + average: true, + median: false + } }) await pool.destroy() pool = new DynamicThreadPool( @@ -525,14 +965,23 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics() + pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() ).toStrictEqual({ - runTime: true, - avgRunTime: true, - medRunTime: false, - waitTime: false, - avgWaitTime: false, - medWaitTime: false + runTime: { + aggregate: true, + average: true, + median: false + }, + waitTime: { + aggregate: false, + average: false, + median: false + }, + elu: { + aggregate: true, + average: true, + median: false + } }) // We need to clean up the resources after our test await pool.destroy() @@ -552,23 +1001,49 @@ describe('Selection strategies test suite', () => { } 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.usage).toMatchObject({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: expect.objectContaining({ + history: expect.any(CircularArray) + }), + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: expect.objectContaining({ + history: expect.any(CircularArray) + }), + active: expect.objectContaining({ + history: expect.any(CircularArray) + }) + } }) - 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(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) + if (workerNode.usage.runTime.aggregate == null) { + expect(workerNode.usage.runTime.aggregate).toBeUndefined() + } else { + expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0) + } + if (workerNode.usage.runTime.average == null) { + expect(workerNode.usage.runTime.average).toBeUndefined() + } else { + expect(workerNode.usage.runTime.average).toBeGreaterThan(0) + } + if (workerNode.usage.elu.utilization == null) { + expect(workerNode.usage.elu.utilization).toBeUndefined() + } else { + expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1) + } } expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -594,23 +1069,49 @@ describe('Selection strategies test suite', () => { } 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.usage).toMatchObject({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: expect.objectContaining({ + history: expect.any(CircularArray) + }), + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: expect.objectContaining({ + history: expect.any(CircularArray) + }), + active: expect.objectContaining({ + history: expect.any(CircularArray) + }) + } }) - 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(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) + if (workerNode.usage.runTime.aggregate == null) { + expect(workerNode.usage.runTime.aggregate).toBeUndefined() + } else { + expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0) + } + if (workerNode.usage.runTime.average == null) { + expect(workerNode.usage.runTime.average).toBeUndefined() + } else { + expect(workerNode.usage.runTime.average).toBeGreaterThan(0) + } + if (workerNode.usage.elu.utilization == null) { + expect(workerNode.usage.elu.utilization).toBeUndefined() + } else { + expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1) + } } expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -629,7 +1130,7 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE, workerChoiceStrategyOptions: { - medRunTime: true + runTime: { median: true } } } ) @@ -641,23 +1142,49 @@ describe('Selection strategies test suite', () => { } 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: expect.any(Number), - waitTime: 0, - waitTimeHistory: expect.any(CircularArray), - avgWaitTime: 0, - medWaitTime: 0, - error: 0 + expect(workerNode.usage).toMatchObject({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: expect.objectContaining({ + history: expect.any(CircularArray) + }), + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: expect.objectContaining({ + history: expect.any(CircularArray) + }), + active: expect.objectContaining({ + history: expect.any(CircularArray) + }) + } }) - 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) + expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) + if (workerNode.usage.runTime.aggregate == null) { + expect(workerNode.usage.runTime.aggregate).toBeUndefined() + } else { + expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0) + } + if (workerNode.usage.runTime.median == null) { + expect(workerNode.usage.runTime.median).toBeUndefined() + } else { + expect(workerNode.usage.runTime.median).toBeGreaterThan(0) + } + if (workerNode.usage.elu.utilization == null) { + expect(workerNode.usage.elu.utilization).toBeUndefined() + } else { + expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1) + } } expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -742,7 +1269,31 @@ describe('Selection strategies test suite', () => { await pool.destroy() }) - it('Verify WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => { + it('Verify WEIGHTED_ROUND_ROBIN strategy default policy', async () => { + const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN + let pool = new FixedThreadPool( + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({ + useDynamicWorker: true + }) + await pool.destroy() + pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({ + useDynamicWorker: true + }) + // We need to clean up the resources after our test + await pool.destroy() + }) + + it('Verify WEIGHTED_ROUND_ROBIN strategy default tasks statistics requirements', async () => { const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN let pool = new FixedThreadPool( max, @@ -750,14 +1301,23 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics() + pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() ).toStrictEqual({ - runTime: true, - avgRunTime: true, - medRunTime: false, - waitTime: false, - avgWaitTime: false, - medWaitTime: false + runTime: { + aggregate: true, + average: true, + median: false + }, + waitTime: { + aggregate: false, + average: false, + median: false + }, + elu: { + aggregate: false, + average: false, + median: false + } }) await pool.destroy() pool = new DynamicThreadPool( @@ -767,14 +1327,23 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy } ) expect( - pool.workerChoiceStrategyContext.getRequiredStatistics() + pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() ).toStrictEqual({ - runTime: true, - avgRunTime: true, - medRunTime: false, - waitTime: false, - avgWaitTime: false, - medWaitTime: false + runTime: { + aggregate: true, + average: true, + median: false + }, + waitTime: { + aggregate: false, + average: false, + median: false + }, + elu: { + aggregate: false, + average: false, + median: false + } }) // We need to clean up the resources after our test await pool.destroy() @@ -794,23 +1363,43 @@ describe('Selection strategies test suite', () => { } 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.usage).toStrictEqual({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: expect.objectContaining({ + history: expect.any(CircularArray) + }), + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: { + history: expect.any(CircularArray) + }, + active: { + history: expect.any(CircularArray) + } + } }) - 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(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) + if (workerNode.usage.runTime.aggregate == null) { + expect(workerNode.usage.runTime.aggregate).toBeUndefined() + } else { + expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0) + } + if (workerNode.usage.runTime.average == null) { + expect(workerNode.usage.runTime.average).toBeUndefined() + } else { + expect(workerNode.usage.runTime.average).toBeGreaterThan(0) + } } expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -841,23 +1430,39 @@ describe('Selection strategies test suite', () => { } 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.usage).toStrictEqual({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: { + aggregate: expect.any(Number), + maximum: expect.any(Number), + minimum: expect.any(Number), + average: expect.any(Number), + history: expect.any(CircularArray) + }, + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: { + history: expect.any(CircularArray) + }, + active: { + history: expect.any(CircularArray) + } + } }) - 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(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) + expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0) + expect(workerNode.usage.runTime.average).toBeGreaterThan(0) } expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -881,7 +1486,7 @@ describe('Selection strategies test suite', () => { { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN, workerChoiceStrategyOptions: { - medRunTime: true + runTime: { median: true } } } ) @@ -893,23 +1498,39 @@ describe('Selection strategies test suite', () => { } 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: expect.any(Number), - waitTime: 0, - waitTimeHistory: expect.any(CircularArray), - avgWaitTime: 0, - medWaitTime: 0, - error: 0 + expect(workerNode.usage).toStrictEqual({ + tasks: { + executed: expect.any(Number), + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: { + aggregate: expect.any(Number), + maximum: expect.any(Number), + minimum: expect.any(Number), + median: expect.any(Number), + history: expect.any(CircularArray) + }, + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: { + history: expect.any(CircularArray) + }, + active: { + history: expect.any(CircularArray) + } + } }) - 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) + expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual( + max * maxMultiplier + ) + expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0) + expect(workerNode.usage.runTime.median).toBeGreaterThan(0) } expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -934,7 +1555,7 @@ describe('Selection strategies test suite', () => { expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( workerChoiceStrategy - ).currentWorkerNodeId + ).nextWorkerNodeKey ).toBeDefined() expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -950,7 +1571,7 @@ describe('Selection strategies test suite', () => { expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( pool.workerChoiceStrategyContext.workerChoiceStrategy - ).currentWorkerNodeId + ).nextWorkerNodeKey ).toBe(0) expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -971,7 +1592,7 @@ describe('Selection strategies test suite', () => { expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( workerChoiceStrategy - ).currentWorkerNodeId + ).nextWorkerNodeKey ).toBeDefined() expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -987,7 +1608,7 @@ describe('Selection strategies test suite', () => { expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( pool.workerChoiceStrategyContext.workerChoiceStrategy - ).currentWorkerNodeId + ).nextWorkerNodeKey ).toBe(0) expect( pool.workerChoiceStrategyContext.workerChoiceStrategies.get( @@ -1003,6 +1624,329 @@ describe('Selection strategies test suite', () => { await pool.destroy() }) + it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy default policy', async () => { + const workerChoiceStrategy = + WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN + let pool = new FixedThreadPool( + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({ + useDynamicWorker: true + }) + await pool.destroy() + pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({ + useDynamicWorker: true + }) + // We need to clean up the resources after our test + await pool.destroy() + }) + + it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy default tasks 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.getTaskStatisticsRequirements() + ).toStrictEqual({ + runTime: { + aggregate: false, + average: false, + median: false + }, + waitTime: { + aggregate: false, + average: false, + median: false + }, + elu: { + aggregate: false, + average: false, + median: false + } + }) + await pool.destroy() + pool = new DynamicThreadPool( + min, + max, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy } + ) + expect( + pool.workerChoiceStrategyContext.getTaskStatisticsRequirements() + ).toStrictEqual({ + runTime: { + aggregate: false, + average: false, + median: false + }, + waitTime: { + aggregate: false, + average: false, + median: false + }, + elu: { + aggregate: false, + average: false, + median: 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.usage).toStrictEqual({ + tasks: { + executed: maxMultiplier, + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: { + history: expect.any(CircularArray) + }, + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: { + history: expect.any(CircularArray) + }, + active: { + history: expect.any(CircularArray) + } + } + }) + } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).defaultWorkerWeight + ).toBeGreaterThan(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).roundId + ).toBe(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).nextWorkerNodeKey + ).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.usage).toStrictEqual({ + tasks: { + executed: maxMultiplier, + executing: 0, + queued: 0, + maxQueued: 0, + failed: 0 + }, + runTime: { + history: expect.any(CircularArray) + }, + waitTime: { + history: expect.any(CircularArray) + }, + elu: { + idle: { + history: expect.any(CircularArray) + }, + active: { + history: expect.any(CircularArray) + } + } + }) + } + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).defaultWorkerWeight + ).toBeGreaterThan(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).roundId + ).toBe(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).nextWorkerNodeKey + ).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 + ).roundId + ).toBeDefined() + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).nextWorkerNodeKey + ).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 + ).roundId + ).toBe(0) + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + pool.workerChoiceStrategyContext.workerChoiceStrategy + ).nextWorkerNodeKey + ).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 + ).roundId + ).toBeDefined() + expect( + pool.workerChoiceStrategyContext.workerChoiceStrategies.get( + workerChoiceStrategy + ).nextWorkerNodeKey + ).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 + ).nextWorkerNodeKey + ).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( () =>