From 60dc0a9cc569efeb0566180dd63e6eea8ac2bf3b Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Mon, 1 Apr 2024 13:16:22 +0200 Subject: [PATCH] fix: guarantee the minimun number of workers on started pool MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- CHANGELOG.md | 4 +++ .../express-cluster/pnpm-lock.yaml | 15 ++++++++-- .../express-hybrid/pnpm-lock.yaml | 15 ++++++++-- .../express-worker_threads/pnpm-lock.yaml | 15 ++++++++-- src/pools/abstract-pool.ts | 28 +++++++++++++------ tests/pools/cluster/fixed.test.mjs | 3 +- tests/pools/thread/fixed.test.mjs | 3 +- 7 files changed, 63 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d88f0ba..3e0ca378 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Ensure the minimum number of workers on a started pool is guaranteed. + ## [3.1.27] - 2024-03-27 ### Fixed diff --git a/examples/typescript/http-server-pool/express-cluster/pnpm-lock.yaml b/examples/typescript/http-server-pool/express-cluster/pnpm-lock.yaml index 49bb317d..e91a6000 100644 --- a/examples/typescript/http-server-pool/express-cluster/pnpm-lock.yaml +++ b/examples/typescript/http-server-pool/express-cluster/pnpm-lock.yaml @@ -277,8 +277,11 @@ packages: resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} dev: true - /@types/mime@3.0.4: - resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} + /@types/mime@4.0.0: + resolution: {integrity: sha512-5eEkJZ/BLvTE3vXGKkWlyTSUVZuzj23Wj8PoyOq2lt5I3CYbiLBOPb3XmCW6QcuOibIUE6emHXHt9E/F/rCa6w==} + deprecated: This is a stub types definition. mime provides its own type definitions, so you do not need this installed. + dependencies: + mime: 4.0.1 dev: true /@types/minimatch@5.1.2: @@ -310,7 +313,7 @@ packages: resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} dependencies: '@types/http-errors': 2.0.4 - '@types/mime': 3.0.4 + '@types/mime': 4.0.0 '@types/node': 20.12.2 dev: true @@ -993,6 +996,12 @@ packages: hasBin: true dev: false + /mime@4.0.1: + resolution: {integrity: sha512-5lZ5tyrIfliMXzFtkYyekWbtRXObT9OWa8IwQ5uxTBDHucNNwniRqo0yInflj+iYi5CBa6qxadGzGarDfuEOxA==} + engines: {node: '>=16'} + hasBin: true + dev: true + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: diff --git a/examples/typescript/http-server-pool/express-hybrid/pnpm-lock.yaml b/examples/typescript/http-server-pool/express-hybrid/pnpm-lock.yaml index 49bb317d..e91a6000 100644 --- a/examples/typescript/http-server-pool/express-hybrid/pnpm-lock.yaml +++ b/examples/typescript/http-server-pool/express-hybrid/pnpm-lock.yaml @@ -277,8 +277,11 @@ packages: resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} dev: true - /@types/mime@3.0.4: - resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} + /@types/mime@4.0.0: + resolution: {integrity: sha512-5eEkJZ/BLvTE3vXGKkWlyTSUVZuzj23Wj8PoyOq2lt5I3CYbiLBOPb3XmCW6QcuOibIUE6emHXHt9E/F/rCa6w==} + deprecated: This is a stub types definition. mime provides its own type definitions, so you do not need this installed. + dependencies: + mime: 4.0.1 dev: true /@types/minimatch@5.1.2: @@ -310,7 +313,7 @@ packages: resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} dependencies: '@types/http-errors': 2.0.4 - '@types/mime': 3.0.4 + '@types/mime': 4.0.0 '@types/node': 20.12.2 dev: true @@ -993,6 +996,12 @@ packages: hasBin: true dev: false + /mime@4.0.1: + resolution: {integrity: sha512-5lZ5tyrIfliMXzFtkYyekWbtRXObT9OWa8IwQ5uxTBDHucNNwniRqo0yInflj+iYi5CBa6qxadGzGarDfuEOxA==} + engines: {node: '>=16'} + hasBin: true + dev: true + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: diff --git a/examples/typescript/http-server-pool/express-worker_threads/pnpm-lock.yaml b/examples/typescript/http-server-pool/express-worker_threads/pnpm-lock.yaml index f778f367..c9fa1b6a 100644 --- a/examples/typescript/http-server-pool/express-worker_threads/pnpm-lock.yaml +++ b/examples/typescript/http-server-pool/express-worker_threads/pnpm-lock.yaml @@ -78,8 +78,11 @@ packages: resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} dev: true - /@types/mime@3.0.4: - resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} + /@types/mime@4.0.0: + resolution: {integrity: sha512-5eEkJZ/BLvTE3vXGKkWlyTSUVZuzj23Wj8PoyOq2lt5I3CYbiLBOPb3XmCW6QcuOibIUE6emHXHt9E/F/rCa6w==} + deprecated: This is a stub types definition. mime provides its own type definitions, so you do not need this installed. + dependencies: + mime: 4.0.1 dev: true /@types/node@20.12.2: @@ -107,7 +110,7 @@ packages: resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} dependencies: '@types/http-errors': 2.0.4 - '@types/mime': 3.0.4 + '@types/mime': 4.0.0 '@types/node': 20.12.2 dev: true @@ -593,6 +596,12 @@ packages: hasBin: true dev: false + /mime@4.0.1: + resolution: {integrity: sha512-5lZ5tyrIfliMXzFtkYyekWbtRXObT9OWa8IwQ5uxTBDHucNNwniRqo0yInflj+iYi5CBa6qxadGzGarDfuEOxA==} + engines: {node: '>=16'} + hasBin: true + dev: true + /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} dev: true diff --git a/src/pools/abstract-pool.ts b/src/pools/abstract-pool.ts index b7b30161..9610f341 100644 --- a/src/pools/abstract-pool.ts +++ b/src/pools/abstract-pool.ts @@ -949,6 +949,21 @@ export abstract class AbstractPool< }) } + /** + * Starts the minimum number of workers. + */ + private startMinimumNumberOfWorkers (): void { + while ( + this.workerNodes.reduce( + (accumulator, workerNode) => + !workerNode.info.dynamic ? accumulator + 1 : accumulator, + 0 + ) < this.minimumNumberOfWorkers + ) { + this.createAndSetupWorkerNode() + } + } + /** @inheritdoc */ public start (): void { if (this.started) { @@ -961,15 +976,7 @@ export abstract class AbstractPool< throw new Error('Cannot start a destroying pool') } this.starting = true - while ( - this.workerNodes.reduce( - (accumulator, workerNode) => - !workerNode.info.dynamic ? accumulator + 1 : accumulator, - 0 - ) < this.minimumNumberOfWorkers - ) { - this.createAndSetupWorkerNode() - } + this.startMinimumNumberOfWorkers() this.starting = false this.started = true } @@ -1266,6 +1273,9 @@ export abstract class AbstractPool< ) workerNode.registerOnceWorkerEventHandler('exit', () => { this.removeWorkerNode(workerNode) + if (this.started && !this.destroying) { + this.startMinimumNumberOfWorkers() + } }) const workerNodeKey = this.addWorkerNode(workerNode) this.afterWorkerNodeSetup(workerNodeKey) diff --git a/tests/pools/cluster/fixed.test.mjs b/tests/pools/cluster/fixed.test.mjs index 794da1c0..ad7c003a 100644 --- a/tests/pools/cluster/fixed.test.mjs +++ b/tests/pools/cluster/fixed.test.mjs @@ -327,7 +327,8 @@ describe('Fixed cluster pool test suite', () => { await expect(pool.destroyWorkerNode(workerNodeKey)).resolves.toBeUndefined() expect(disconnectEvent).toBe(1) expect(exitEvent).toBe(1) - expect(pool.workerNodes.length).toBe(numberOfWorkers - 1) + // Simulates an illegitimate worker node destroy and the minimum number of worker nodes is guaranteed + expect(pool.workerNodes.length).toBe(numberOfWorkers) await pool.destroy() }) diff --git a/tests/pools/thread/fixed.test.mjs b/tests/pools/thread/fixed.test.mjs index 48f19e4d..3da05735 100644 --- a/tests/pools/thread/fixed.test.mjs +++ b/tests/pools/thread/fixed.test.mjs @@ -348,7 +348,8 @@ describe('Fixed thread pool test suite', () => { }) await expect(pool.destroyWorkerNode(workerNodeKey)).resolves.toBeUndefined() expect(exitEvent).toBe(1) - expect(pool.workerNodes.length).toBe(numberOfThreads - 1) + // Simulates an illegitimate worker node destroy and the minimum number of worker nodes is guaranteed + expect(pool.workerNodes.length).toBe(numberOfThreads) await pool.destroy() }) -- 2.34.1