X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=tests%2Fpools%2Fabstract%2Fabstract-pool.test.js;h=19cb3eb849406a726832537c0a7a1a36c0d1cf19;hb=ee9f52959d4996f56712791e73994dfbeb0370f0;hp=1a5a75a08df6ab03d84d7fa0aff06f617f59a797;hpb=594bfb844b5eb66cf533715a6ddad1799b1f5f8f;p=poolifier.git diff --git a/tests/pools/abstract/abstract-pool.test.js b/tests/pools/abstract/abstract-pool.test.js index 1a5a75a0..19cb3eb8 100644 --- a/tests/pools/abstract/abstract-pool.test.js +++ b/tests/pools/abstract/abstract-pool.test.js @@ -1,21 +1,20 @@ const { expect } = require('expect') const { + DynamicClusterPool, DynamicThreadPool, FixedClusterPool, FixedThreadPool, PoolEvents, WorkerChoiceStrategies -} = require('../../../lib/index') +} = require('../../../lib') const { CircularArray } = require('../../../lib/circular-array') +const { Queue } = require('../../../lib/queue') describe('Abstract pool test suite', () => { const numberOfWorkers = 1 - const workerNotFoundInPoolError = new Error( - 'Worker could not be found in the pool worker nodes' - ) class StubPoolWithRemoveAllWorker extends FixedThreadPool { removeAllWorker () { - this.workers = [] + this.workerNodes = [] this.promiseResponseMap.clear() } } @@ -35,7 +34,7 @@ describe('Abstract pool test suite', () => { errorHandler: e => console.error(e) } ) - ).toThrowError(new Error('Cannot start a pool from a worker!')) + ).toThrowError('Cannot start a pool from a worker!') }) it('Verify that filePath is checked', () => { @@ -52,9 +51,7 @@ describe('Abstract pool test suite', () => { it('Verify that numberOfWorkers is checked', () => { expect(() => new FixedThreadPool()).toThrowError( - new Error( - 'Cannot instantiate a pool without specifying the number of workers' - ) + 'Cannot instantiate a pool without specifying the number of workers' ) }) @@ -75,7 +72,7 @@ describe('Abstract pool test suite', () => { new FixedThreadPool(0.25, './tests/worker-files/thread/testWorker.js') ).toThrowError( new TypeError( - 'Cannot instantiate a pool with a non integer number of workers' + 'Cannot instantiate a pool with a non safe integer number of workers' ) ) }) @@ -88,11 +85,13 @@ describe('Abstract pool test suite', () => { expect(pool.opts.enableEvents).toBe(true) expect(pool.emitter).toBeDefined() expect(pool.opts.enableTasksQueue).toBe(false) + expect(pool.opts.tasksQueueOptions).toBeUndefined() expect(pool.opts.workerChoiceStrategy).toBe( WorkerChoiceStrategies.ROUND_ROBIN ) expect(pool.opts.workerChoiceStrategyOptions).toStrictEqual({ - medRunTime: false + medRunTime: false, + medWaitTime: false }) expect(pool.opts.messageHandler).toBeUndefined() expect(pool.opts.errorHandler).toBeUndefined() @@ -104,10 +103,14 @@ describe('Abstract pool test suite', () => { numberOfWorkers, './tests/worker-files/thread/testWorker.js', { - workerChoiceStrategy: WorkerChoiceStrategies.LESS_USED, - workerChoiceStrategyOptions: { medRunTime: true }, + workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED, + workerChoiceStrategyOptions: { + medRunTime: true, + weights: { 0: 300 } + }, enableEvents: false, enableTasksQueue: true, + tasksQueueOptions: { concurrency: 2 }, messageHandler: testHandler, errorHandler: testHandler, onlineHandler: testHandler, @@ -117,11 +120,13 @@ describe('Abstract pool test suite', () => { expect(pool.opts.enableEvents).toBe(false) expect(pool.emitter).toBeUndefined() expect(pool.opts.enableTasksQueue).toBe(true) + expect(pool.opts.tasksQueueOptions).toStrictEqual({ concurrency: 2 }) expect(pool.opts.workerChoiceStrategy).toBe( - WorkerChoiceStrategies.LESS_USED + WorkerChoiceStrategies.LEAST_USED ) expect(pool.opts.workerChoiceStrategyOptions).toStrictEqual({ - medRunTime: true + medRunTime: true, + weights: { 0: 300 } }) expect(pool.opts.messageHandler).toStrictEqual(testHandler) expect(pool.opts.errorHandler).toStrictEqual(testHandler) @@ -130,7 +135,143 @@ describe('Abstract pool test suite', () => { await pool.destroy() }) - it('Simulate worker not found during getWorkerTasksUsage', async () => { + it('Verify that pool options are validated', async () => { + expect( + () => + new FixedThreadPool( + numberOfWorkers, + './tests/worker-files/thread/testWorker.js', + { + enableTasksQueue: true, + tasksQueueOptions: { concurrency: 0 } + } + ) + ).toThrowError("Invalid worker tasks concurrency '0'") + expect( + () => + new FixedThreadPool( + numberOfWorkers, + './tests/worker-files/thread/testWorker.js', + { + workerChoiceStrategy: 'invalidStrategy' + } + ) + ).toThrowError("Invalid worker choice strategy 'invalidStrategy'") + expect( + () => + new FixedThreadPool( + numberOfWorkers, + './tests/worker-files/thread/testWorker.js', + { + workerChoiceStrategyOptions: { weights: {} } + } + ) + ).toThrowError( + 'Invalid worker choice strategy options: must have a weight for each worker node' + ) + }) + + it('Verify that worker choice strategy options can be set', async () => { + const pool = new FixedThreadPool( + numberOfWorkers, + './tests/worker-files/thread/testWorker.js', + { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE } + ) + expect(pool.opts.workerChoiceStrategyOptions).toStrictEqual({ + medRunTime: false, + medWaitTime: false + }) + for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext + .workerChoiceStrategies) { + expect(workerChoiceStrategy.opts).toStrictEqual({ + medRunTime: false, + medWaitTime: false + }) + } + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics() + ).toStrictEqual({ + runTime: true, + avgRunTime: true, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false + }) + pool.setWorkerChoiceStrategyOptions({ medRunTime: true }) + expect(pool.opts.workerChoiceStrategyOptions).toStrictEqual({ + medRunTime: true + }) + for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext + .workerChoiceStrategies) { + expect(workerChoiceStrategy.opts).toStrictEqual({ medRunTime: true }) + } + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics() + ).toStrictEqual({ + runTime: true, + avgRunTime: false, + medRunTime: true, + waitTime: false, + avgWaitTime: false, + medWaitTime: false + }) + pool.setWorkerChoiceStrategyOptions({ medRunTime: false }) + expect(pool.opts.workerChoiceStrategyOptions).toStrictEqual({ + medRunTime: false + }) + for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext + .workerChoiceStrategies) { + expect(workerChoiceStrategy.opts).toStrictEqual({ medRunTime: false }) + } + expect( + pool.workerChoiceStrategyContext.getRequiredStatistics() + ).toStrictEqual({ + runTime: true, + avgRunTime: true, + medRunTime: false, + waitTime: false, + avgWaitTime: false, + medWaitTime: false + }) + await pool.destroy() + }) + + it('Verify that tasks queue can be enabled/disabled', async () => { + const pool = new FixedThreadPool( + numberOfWorkers, + './tests/worker-files/thread/testWorker.js' + ) + expect(pool.opts.enableTasksQueue).toBe(false) + expect(pool.opts.tasksQueueOptions).toBeUndefined() + pool.enableTasksQueue(true) + expect(pool.opts.enableTasksQueue).toBe(true) + expect(pool.opts.tasksQueueOptions).toStrictEqual({ concurrency: 1 }) + pool.enableTasksQueue(true, { concurrency: 2 }) + expect(pool.opts.enableTasksQueue).toBe(true) + expect(pool.opts.tasksQueueOptions).toStrictEqual({ concurrency: 2 }) + pool.enableTasksQueue(false) + expect(pool.opts.enableTasksQueue).toBe(false) + expect(pool.opts.tasksQueueOptions).toBeUndefined() + await pool.destroy() + }) + + it('Verify that tasks queue options can be set', async () => { + const pool = new FixedThreadPool( + numberOfWorkers, + './tests/worker-files/thread/testWorker.js', + { enableTasksQueue: true } + ) + expect(pool.opts.tasksQueueOptions).toStrictEqual({ concurrency: 1 }) + pool.setTasksQueueOptions({ concurrency: 2 }) + expect(pool.opts.tasksQueueOptions).toStrictEqual({ concurrency: 2 }) + expect(() => pool.setTasksQueueOptions({ concurrency: 0 })).toThrowError( + "Invalid worker tasks concurrency '0'" + ) + await pool.destroy() + }) + + it('Simulate worker not found', async () => { const pool = new StubPoolWithRemoveAllWorker( numberOfWorkers, './tests/worker-files/cluster/testWorker.js', @@ -138,11 +279,10 @@ describe('Abstract pool test suite', () => { errorHandler: e => console.error(e) } ) + expect(pool.workerNodes.length).toBe(numberOfWorkers) // Simulate worker not found. pool.removeAllWorker() - expect(() => pool.getWorkerTasksUsage()).toThrowError( - workerNotFoundInPoolError - ) + expect(pool.workerNodes.length).toBe(0) await pool.destroy() }) @@ -152,15 +292,19 @@ describe('Abstract pool test suite', () => { './tests/worker-files/cluster/testWorker.js' ) for (const workerNode of pool.workerNodes) { - expect(workerNode.tasksUsage).toBeDefined() - expect(workerNode.tasksUsage.run).toBe(0) - expect(workerNode.tasksUsage.running).toBe(0) - expect(workerNode.tasksUsage.runTime).toBe(0) - expect(workerNode.tasksUsage.runTimeHistory).toBeInstanceOf(CircularArray) - expect(workerNode.tasksUsage.runTimeHistory.length).toBe(0) - expect(workerNode.tasksUsage.avgRunTime).toBe(0) - expect(workerNode.tasksUsage.medRunTime).toBe(0) - expect(workerNode.tasksUsage.error).toBe(0) + expect(workerNode.tasksUsage).toStrictEqual({ + run: 0, + running: 0, + runTime: 0, + runTimeHistory: expect.any(CircularArray), + avgRunTime: 0, + medRunTime: 0, + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) } await pool.destroy() }) @@ -172,8 +316,8 @@ describe('Abstract pool test suite', () => { ) for (const workerNode of pool.workerNodes) { expect(workerNode.tasksQueue).toBeDefined() - expect(workerNode.tasksQueue).toBeInstanceOf(Array) - expect(workerNode.tasksQueue.length).toBe(0) + expect(workerNode.tasksQueue).toBeInstanceOf(Queue) + expect(workerNode.tasksQueue.size).toBe(0) } await pool.destroy() }) @@ -188,27 +332,35 @@ describe('Abstract pool test suite', () => { promises.push(pool.execute()) } for (const workerNode of pool.workerNodes) { - expect(workerNode.tasksUsage).toBeDefined() - expect(workerNode.tasksUsage.run).toBe(0) - expect(workerNode.tasksUsage.running).toBe(numberOfWorkers * 2) - expect(workerNode.tasksUsage.runTime).toBe(0) - expect(workerNode.tasksUsage.runTimeHistory).toBeInstanceOf(CircularArray) - expect(workerNode.tasksUsage.runTimeHistory.length).toBe(0) - expect(workerNode.tasksUsage.avgRunTime).toBe(0) - expect(workerNode.tasksUsage.medRunTime).toBe(0) - expect(workerNode.tasksUsage.error).toBe(0) + expect(workerNode.tasksUsage).toStrictEqual({ + run: 0, + running: numberOfWorkers * 2, + runTime: 0, + runTimeHistory: expect.any(CircularArray), + avgRunTime: 0, + medRunTime: 0, + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) } await Promise.all(promises) for (const workerNode of pool.workerNodes) { - expect(workerNode.tasksUsage).toBeDefined() - expect(workerNode.tasksUsage.run).toBe(numberOfWorkers * 2) - expect(workerNode.tasksUsage.running).toBe(0) - expect(workerNode.tasksUsage.runTime).toBeGreaterThanOrEqual(0) - expect(workerNode.tasksUsage.runTimeHistory).toBeInstanceOf(CircularArray) - expect(workerNode.tasksUsage.runTimeHistory.length).toBe(0) - expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0) - expect(workerNode.tasksUsage.medRunTime).toBe(0) - expect(workerNode.tasksUsage.error).toBe(0) + expect(workerNode.tasksUsage).toStrictEqual({ + run: numberOfWorkers * 2, + running: 0, + runTime: 0, + runTimeHistory: expect.any(CircularArray), + avgRunTime: 0, + medRunTime: 0, + waitTime: 0, + waitTimeHistory: expect.any(CircularArray), + avgWaitTime: 0, + medWaitTime: 0, + error: 0 + }) } await pool.destroy() }) @@ -220,32 +372,43 @@ describe('Abstract pool test suite', () => { './tests/worker-files/thread/testWorker.js' ) const promises = [] - for (let i = 0; i < numberOfWorkers * 2; i++) { + const maxMultiplier = 2 + for (let i = 0; i < numberOfWorkers * maxMultiplier; i++) { promises.push(pool.execute()) } await Promise.all(promises) for (const workerNode of pool.workerNodes) { - expect(workerNode.tasksUsage).toBeDefined() - expect(workerNode.tasksUsage.run).toBe(numberOfWorkers * 2) - expect(workerNode.tasksUsage.running).toBe(0) - expect(workerNode.tasksUsage.runTime).toBeGreaterThanOrEqual(0) - expect(workerNode.tasksUsage.runTimeHistory).toBeInstanceOf(CircularArray) - expect(workerNode.tasksUsage.runTimeHistory.length).toBe(0) - expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0) - expect(workerNode.tasksUsage.medRunTime).toBe(0) - expect(workerNode.tasksUsage.error).toBe(0) + expect(workerNode.tasksUsage).toStrictEqual({ + run: numberOfWorkers * 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 + }) } pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.FAIR_SHARE) for (const workerNode of pool.workerNodes) { - expect(workerNode.tasksUsage).toBeDefined() - expect(workerNode.tasksUsage.run).toBe(0) - expect(workerNode.tasksUsage.running).toBe(0) - expect(workerNode.tasksUsage.runTime).toBe(0) - expect(workerNode.tasksUsage.runTimeHistory).toBeInstanceOf(CircularArray) + expect(workerNode.tasksUsage).toStrictEqual({ + run: 0, + 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.tasksUsage.runTimeHistory.length).toBe(0) - expect(workerNode.tasksUsage.avgRunTime).toBe(0) - expect(workerNode.tasksUsage.medRunTime).toBe(0) - expect(workerNode.tasksUsage.error).toBe(0) + expect(workerNode.tasksUsage.waitTimeHistory.length).toBe(0) } await pool.destroy() }) @@ -286,4 +449,21 @@ describe('Abstract pool test suite', () => { expect(poolBusy).toBe(numberOfWorkers + 1) await pool.destroy() }) + + it('Verify that multiple tasks worker is working', async () => { + const pool = new DynamicClusterPool( + numberOfWorkers, + numberOfWorkers * 2, + './tests/worker-files/cluster/testMultiTasksWorker.js' + ) + const data = { n: 10 } + const result0 = await pool.execute(data) + expect(result0).toBe(false) + const result1 = await pool.execute(data, 'jsonIntegerSerialization') + expect(result1).toBe(false) + const result2 = await pool.execute(data, 'factorial') + expect(result2).toBe(3628800) + const result3 = await pool.execute(data, 'fibonacci') + expect(result3).toBe(89) + }) })