Merge branch 'master' into add-worker-test
authorShinigami92 <chrissi92@hotmail.de>
Tue, 16 Feb 2021 15:08:46 +0000 (16:08 +0100)
committerShinigami92 <chrissi92@hotmail.de>
Tue, 16 Feb 2021 15:08:46 +0000 (16:08 +0100)
42 files changed:
.eslintignore
.github/workflows/ci.yml
.github/workflows/npmpublish.yml
.gitignore
.prettierignore
.vscode/launch.json
.vscode/tasks.json
CHANGELOG.md
README.md
benchmarks/bench.js
docs/logo.png [new file with mode: 0644]
package-lock.json
package.json
rollup.config.mjs
sonar-project.properties
src/index.ts
src/pools/abstract-pool.ts
src/pools/cluster/dynamic.ts
src/pools/thread/dynamic.ts
src/utility-types.ts
src/worker/abstract-worker.ts
src/worker/worker-options.ts
tests/pools/abstract/abstract-pool.test.js [new file with mode: 0644]
tests/pools/cluster/dynamic.test.js
tests/pools/cluster/fixed.test.js
tests/pools/thread/dynamic.test.js
tests/pools/thread/fixed.test.js
tests/worker-files/cluster/asyncErrorWorker.js
tests/worker-files/cluster/asyncWorker.js
tests/worker-files/cluster/echoWorker.js
tests/worker-files/cluster/emptyWorker.js
tests/worker-files/cluster/errorWorker.js
tests/worker-files/cluster/testWorker.js
tests/worker-files/thread/asyncWorker.js
tests/worker-files/thread/echoWorker.js
tests/worker-files/thread/emptyWorker.js
tests/worker-files/thread/errorWorker.js
tests/worker-files/thread/testWorker.js
tests/worker/cluster/longRunningWorkerHardBehavior.js [new file with mode: 0644]
tests/worker/cluster/longRunningWorkerSoftBehavior.js [new file with mode: 0644]
tests/worker/thread/longRunningWorkerHardBehavior.js [new file with mode: 0644]
tests/worker/thread/longRunningWorkerSoftBehavior.js [new file with mode: 0644]

index b7d3d591f81c519fd8a47af9167de549f5e5d3a8..c3af857904ebfd987602da36b219a186a84e4e7a 100644 (file)
@@ -1,2 +1 @@
 lib/
-lib.min/
index d7ab024d2e637450244501dd431988a4097e9cce..d10434394915dccf4b1e7ea65e7e5cb2271c734c 100644 (file)
@@ -26,7 +26,7 @@ jobs:
           fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis. This is needed for better sonar
 
       - name: Set node version to ${{ matrix.node-version }}
-        uses: actions/setup-node@v1
+        uses: actions/setup-node@v2
         with:
           node-version: ${{ matrix.node-version }}
 
index 31b02ef4c6e2016e8e88b6daaf4d3e2292b84a42..90ba5eb7b355c5f0ecc4d1c883c58f0d60f92c60 100644 (file)
@@ -10,15 +10,15 @@ jobs:
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v1
+        uses: actions/checkout@v2
 
       - name: Setup Node
-        uses: actions/setup-node@v1
+        uses: actions/setup-node@v2
         with:
           node-version: 12
       - run: npm ci
       - run: npm run lint
-      - run: npm run test
+      - run: npm run test:prod
       - run: npm run coverage
 
   publish-npm:
@@ -27,10 +27,10 @@ jobs:
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v1
+        uses: actions/checkout@v2
 
       - name: Setup Node
-        uses: actions/setup-node@v1
+        uses: actions/setup-node@v2
         with:
           node-version: 12
           registry-url: https://registry.npmjs.org/
index c918d5dd19c968d7e82cf7dd08f92bc8de7e127e..2e2848ad7a6cf6c1378b3b30b6923f93185a563b 100644 (file)
@@ -50,4 +50,3 @@ jspm_packages
 .history/
 .scannerwork
 lib
-lib.min
index f5df346941e775c00c7b275067df72c9506dd7c8..e40d57b1e25c381fbadae36741e29467be34e9a1 100644 (file)
@@ -1,4 +1,3 @@
 .nyc_output/
 coverage/
 lib/
-lib.min/
index c2c4c33f19bc39cdc7a5dc966f14967c8e0dea0a..f62ecf671b713103fd2c20fe565b3487f2a1a804 100644 (file)
@@ -9,9 +9,9 @@
       "request": "launch",
       "name": "Launch Test Debug",
       "cwd": "${workspaceFolder}",
-      "preLaunchTask": "Development build",
+      // "preLaunchTask": "Development build",
       "runtimeExecutable": "npm",
-      "runtimeArgs": ["run-script", "test:debug:vscode"],
+      "runtimeArgs": ["run-script", "test:debug"],
       "skipFiles": ["<node_internals>/**"],
       "stopOnEntry": true
     },
@@ -20,9 +20,9 @@
       "request": "launch",
       "name": "Launch Benchmark Debug",
       "cwd": "${workspaceFolder}",
-      "preLaunchTask": "Development build",
+      // "preLaunchTask": "Development build",
       "runtimeExecutable": "npm",
-      "runtimeArgs": ["run-script", "benchmark:debug:vscode"],
+      "runtimeArgs": ["run-script", "benchmark:debug"],
       "skipFiles": ["<node_internals>/**"],
       "stopOnEntry": true
     }
index ed1577700c35c42b9a2a0f9e26e262a7150c4b14..a6d60cd7e5c139f87ade991ffc21fd12663cf98d 100644 (file)
@@ -1,10 +1,15 @@
 {
   "version": "2.0.0",
   "tasks": [
+    {
+      "label": "Build",
+      "type": "npm",
+      "script": "build:prod"
+    },
     {
       "label": "Development build",
       "type": "npm",
-      "script": "build:dev"
+      "script": "build"
     }
   ]
 }
index 6e8e90a03a527a3192c64c8eb8ad6114009b5285..d23919efeb0da43d7e260a0564390347f62c898d 100644 (file)
@@ -7,9 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [2.0.0] - not released yet
 
+### Bug fixes
+
+- Now a thread/process by default is not deleted when the task submitted take more time than maxInactiveTime configured (issue #70).
+
 ### Breaking Changes
 
-We changed some internal structures, but you shouldn't be too affected by them as these are internal changes.
+- `maxInactiveTime` default behavior is now changed, if you want to keep the old behavior set `killBehavior` to `KillBehaviors.HARD`.
+  _Find more details on our JSDoc._
+
+- We changed some internal structures, but you shouldn't be too affected by them as these are internal changes.
 
 #### New `export` strategy
 
index 17c46101e753e251b51257bc230b0ef73841bf49..5f1e892fb9c72cd1b1baa154a3057609ae84d373 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,13 +1,29 @@
-# Node Thread Pool :arrow_double_up: :on:
-
-[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
-[![Dependabot](https://badgen.net/dependabot/dependabot/dependabot-core/?icon=dependabot)](https://badgen.net/dependabot/dependabot/dependabot-core/?icon=dependabot)
-[![npm w](https://img.shields.io/npm/dw/poolifier)](https://www.npmjs.com/package/poolifier)
-[![Actions Status](https://github.com/pioardi/node-pool/workflows/NodeCI/badge.svg)](https://github.com/pioardi/node-pool/actions)
-[![Coverage Status](https://coveralls.io/repos/github/pioardi/poolifier/badge.svg?branch=master)](https://coveralls.io/github/pioardi/poolifier?branch=master)
-[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
-[![NODEP](https://img.shields.io/static/v1?label=dependencies&message=no%20dependencies&color=brightgreen)](https://img.shields.io/static/v1?label=dependencies&message=no%20dependencies&color=brightgreen)
-[![Gitter](https://badges.gitter.im/poolifier/community.svg)](https://gitter.im/poolifier/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
+<div align="center">
+<img src="./docs/logo.png" width="475" height="400"/>
+</div>
+
+<h2 align="center">Node Thread Pool :arrow_double_up: :on:</h2>
+
+<p align="center">
+  <a href="https://www.npmjs.com/package/poolifier">
+    <img alt="Weekly Downloads" src="https://img.shields.io/npm/dw/poolifier"></a>
+  <a href="https://github.com/pioardi/node-pool/actions">
+    <img alt="Actions Status" src="https://github.com/pioardi/node-pool/workflows/NodeCI/badge.svg"></a>
+  <a href="https://sonarcloud.io/dashboard?id=pioardi_poolifier">
+    <img alt="Quality Gate Status" src="https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=alert_status"></a>
+  <a href="https://sonarcloud.io/component_measures/metric/coverage/list?id=pioardi_poolifier">
+    <img alt="Code coverage" src="https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=coverage"></a>
+  <a href="https://standardjs.com">
+    <img alt="Javascript Standard Style Guide" src="https://img.shields.io/badge/code_style-standard-brightgreen.svg"></a>
+  <a href="https://gitter.im/poolifier/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge">
+    <img alt="Gitter chat" src="https://badges.gitter.im/poolifier/community.svg"></a>
+  <a href="https://badgen.net/dependabot/dependabot/dependabot-core/?icon=dependabot">
+    <img alt="Dependabot" src="https://badgen.net/dependabot/dependabot/dependabot-core/?icon=dependabot"></a>
+  <a href="http://makeapullrequest.com">
+    <img alt="PR Welcome" src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square"></a>
+  <a href="https://img.shields.io/static/v1?label=dependencies&message=no%20dependencies&color=brightgreen">
+    <img alt="No dependencies" src="https://img.shields.io/static/v1?label=dependencies&message=no%20dependencies&color=brightgreen"></a>
+</p>
 
 ## Why Poolifier?
 
@@ -15,6 +31,26 @@ Poolifier is used to perform heavy CPU bound tasks on nodejs servers, it impleme
 With poolifier you can improve your **performance** and resolve problems related to the event loop.  
 Moreover you can execute your CPU tasks using an API designed to improve the **developer experience**.
 
+- Performance :racehorse:
+- Security :bank: :cop: [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=security_rating)](https://sonarcloud.io/dashboard?id=pioardi_poolifier) [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
+- Easy to use :couple:
+- Easy switch from a pool to another, easy to tune :heavy_check_mark:
+- Dynamic pool size :heavy_check_mark:
+- No runtime dependencies :heavy_check_mark:
+- Proper async integration with node async hooks :heavy_check_mark:
+- Support for worker threads and cluster node modules :heavy_check_mark:
+- Support sync and async tasks :heavy_check_mark:
+- General guidance on pools to use :heavy_check_mark:
+- Widely tested :heavy_check_mark:
+- Error handling out of the box :heavy_check_mark:
+- Active community :heavy_check_mark:
+- Code quality :octocat: [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=bugs)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
+  [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=code_smells)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
+  [![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=duplicated_lines_density)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
+  [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
+  [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
+  [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=sqale_index)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
+
 ## Contents
 
 <h3 align="center">
index b2091613e7909d4f920043032413014923e3e740..f8d933813a8b393692a124bbece9243aec244681 100644 (file)
@@ -43,6 +43,5 @@ async function test () {
       // eslint-disable-next-line no-process-exit
       process.exit()
     })
-    // run async
-    .run({ async: true })
+    .run()
 }
diff --git a/docs/logo.png b/docs/logo.png
new file mode 100644 (file)
index 0000000..bc92da3
Binary files /dev/null and b/docs/logo.png differ
index fe7d419b434cc8a7e0d2c7c6361ae3bb4fc4469f..cab635a492b0c8c48e7792b9c97133458c248629 100644 (file)
       "dev": true
     },
     "@types/node": {
-      "version": "14.14.27",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.27.tgz",
-      "integrity": "sha512-Ecfmo4YDQPwuqTCl1yBxLV5ihKfRlkBmzUEDcfIRvDxOTGQEeikr317Ln7Gcv0tjA8dVgKI3rniqW2G1OyKDng==",
+      "version": "14.14.28",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.28.tgz",
+      "integrity": "sha512-lg55ArB+ZiHHbBBttLpzD07akz0QPrZgUODNakeC09i62dnrywr9mFErHuaPlB6I7z+sEbK+IYmplahvplCj2g==",
       "dev": true
     },
     "@types/parse-json": {
       "dev": true
     },
     "@typescript-eslint/eslint-plugin": {
-      "version": "4.15.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.15.0.tgz",
-      "integrity": "sha512-DJgdGZW+8CFUTz5C/dnn4ONcUm2h2T0itWD85Ob5/V27Ndie8hUoX5HKyGssvR8sUMkAIlUc/AMK67Lqa3kBIQ==",
+      "version": "4.15.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.15.1.tgz",
+      "integrity": "sha512-yW2epMYZSpNJXZy22Biu+fLdTG8Mn6b22kR3TqblVk50HGNV8Zya15WAXuQCr8tKw4Qf1BL4QtI6kv6PCkLoJw==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/experimental-utils": "4.15.0",
-        "@typescript-eslint/scope-manager": "4.15.0",
+        "@typescript-eslint/experimental-utils": "4.15.1",
+        "@typescript-eslint/scope-manager": "4.15.1",
         "debug": "^4.1.1",
         "functional-red-black-tree": "^1.0.1",
         "lodash": "^4.17.15",
       }
     },
     "@typescript-eslint/experimental-utils": {
-      "version": "4.15.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.0.tgz",
-      "integrity": "sha512-V4vaDWvxA2zgesg4KPgEGiomWEBpJXvY4ZX34Y3qxK8LUm5I87L+qGIOTd9tHZOARXNRt9pLbblSKiYBlGMawg==",
+      "version": "4.15.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.1.tgz",
+      "integrity": "sha512-9LQRmOzBRI1iOdJorr4jEnQhadxK4c9R2aEAsm7WE/7dq8wkKD1suaV0S/JucTL8QlYUPU1y2yjqg+aGC0IQBQ==",
       "dev": true,
       "requires": {
         "@types/json-schema": "^7.0.3",
-        "@typescript-eslint/scope-manager": "4.15.0",
-        "@typescript-eslint/types": "4.15.0",
-        "@typescript-eslint/typescript-estree": "4.15.0",
+        "@typescript-eslint/scope-manager": "4.15.1",
+        "@typescript-eslint/types": "4.15.1",
+        "@typescript-eslint/typescript-estree": "4.15.1",
         "eslint-scope": "^5.0.0",
         "eslint-utils": "^2.0.0"
       }
     },
     "@typescript-eslint/parser": {
-      "version": "4.15.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.15.0.tgz",
-      "integrity": "sha512-L6Dtbq8Bc7g2aZwnIBETpmUa9XDKCMzKVwAArnGp5Mn7PRNFjf3mUzq8UeBjL3K8t311hvevnyqXAMSmxO8Gpg==",
+      "version": "4.15.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.15.1.tgz",
+      "integrity": "sha512-V8eXYxNJ9QmXi5ETDguB7O9diAXlIyS+e3xzLoP/oVE4WCAjssxLIa0mqCLsCGXulYJUfT+GV70Jv1vHsdKwtA==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/scope-manager": "4.15.0",
-        "@typescript-eslint/types": "4.15.0",
-        "@typescript-eslint/typescript-estree": "4.15.0",
+        "@typescript-eslint/scope-manager": "4.15.1",
+        "@typescript-eslint/types": "4.15.1",
+        "@typescript-eslint/typescript-estree": "4.15.1",
         "debug": "^4.1.1"
       }
     },
     "@typescript-eslint/scope-manager": {
-      "version": "4.15.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz",
-      "integrity": "sha512-CSNBZnCC2jEA/a+pR9Ljh8Y+5TY5qgbPz7ICEk9WCpSEgT6Pi7H2RIjxfrrbUXvotd6ta+i27sssKEH8Azm75g==",
+      "version": "4.15.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.1.tgz",
+      "integrity": "sha512-ibQrTFcAm7yG4C1iwpIYK7vDnFg+fKaZVfvyOm3sNsGAerKfwPVFtYft5EbjzByDJ4dj1WD8/34REJfw/9wdVA==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "4.15.0",
-        "@typescript-eslint/visitor-keys": "4.15.0"
+        "@typescript-eslint/types": "4.15.1",
+        "@typescript-eslint/visitor-keys": "4.15.1"
       }
     },
     "@typescript-eslint/types": {
-      "version": "4.15.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.0.tgz",
-      "integrity": "sha512-su4RHkJhS+iFwyqyXHcS8EGPlUVoC+XREfy5daivjLur9JP8GhvTmDipuRpcujtGC4M+GYhUOJCPDE3rC5NJrg==",
+      "version": "4.15.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.1.tgz",
+      "integrity": "sha512-iGsaUyWFyLz0mHfXhX4zO6P7O3sExQpBJ2dgXB0G5g/8PRVfBBsmQIc3r83ranEQTALLR3Vko/fnCIVqmH+mPw==",
       "dev": true
     },
     "@typescript-eslint/typescript-estree": {
-      "version": "4.15.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.0.tgz",
-      "integrity": "sha512-jG6xTmcNbi6xzZq0SdWh7wQ9cMb2pqXaUp6bUZOMsIlu5aOlxGxgE/t6L/gPybybQGvdguajXGkZKSndZJpksA==",
+      "version": "4.15.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.1.tgz",
+      "integrity": "sha512-z8MN3CicTEumrWAEB2e2CcoZa3KP9+SMYLIA2aM49XW3cWIaiVSOAGq30ffR5XHxRirqE90fgLw3e6WmNx5uNw==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "4.15.0",
-        "@typescript-eslint/visitor-keys": "4.15.0",
+        "@typescript-eslint/types": "4.15.1",
+        "@typescript-eslint/visitor-keys": "4.15.1",
         "debug": "^4.1.1",
         "globby": "^11.0.1",
         "is-glob": "^4.0.1",
       }
     },
     "@typescript-eslint/visitor-keys": {
-      "version": "4.15.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz",
-      "integrity": "sha512-RnDtJwOwFucWFAMjG3ghCG/ikImFJFEg20DI7mn4pHEx3vC48lIAoyjhffvfHmErRDboUPC7p9Z2il4CLb7qxA==",
+      "version": "4.15.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.1.tgz",
+      "integrity": "sha512-tYzaTP9plooRJY8eNlpAewTOqtWW/4ff/5wBjNVaJ0S0wC4Gpq/zDVRTJa5bq2v1pCNQ08xxMCndcvR+h7lMww==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "4.15.0",
+        "@typescript-eslint/types": "4.15.1",
         "eslint-visitor-keys": "^2.0.0"
       }
     },
       }
     },
     "eslint-plugin-jsdoc": {
-      "version": "31.6.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-31.6.1.tgz",
-      "integrity": "sha512-5hCV3u+1VSEUMyfdTl+dpWsioD7tqQr2ILQw+KbXrF42AVxCLO8gnNLR6zDCDjqGGpt79V1sgY0RRchCWuCigg==",
+      "version": "32.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-32.0.1.tgz",
+      "integrity": "sha512-7T6cKNGJsJ1SxhG4vbEBi2fRmUL3DHzRNsiQZri4vkgIQjCoBb40ZNxSNwBqiVz16C7tW3qLncPoNXsiIFdzcg==",
       "dev": true,
       "requires": {
         "comment-parser": "1.1.2",
       },
       "dependencies": {
         "globals": {
-          "version": "13.5.0",
-          "resolved": "https://registry.npmjs.org/globals/-/globals-13.5.0.tgz",
-          "integrity": "sha512-TMJe2Iu/qCIEFnG7IQ62C9N/iKdgX5wSvmGOVuk75+UAGDW+Yv/hH5+Ky6d/8UMqo4WCzhFCy+pHsvv09zhBoQ==",
+          "version": "13.6.0",
+          "resolved": "https://registry.npmjs.org/globals/-/globals-13.6.0.tgz",
+          "integrity": "sha512-YFKCX0SiPg7l5oKYCJ2zZGxcXprVXHcSnVuvzrT3oSENQonVLqM5pf9fN5dLGZGyCjhw8TN8Btwe/jKnZ0pjvQ==",
           "dev": true,
           "requires": {
             "type-fest": "^0.20.2"
       "dev": true
     },
     "handlebars": {
-      "version": "4.7.6",
-      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
-      "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
+      "version": "4.7.7",
+      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
+      "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
       "dev": true,
       "requires": {
         "minimist": "^1.2.5",
index fdba3a8b9ab5df262d49dd37d4b90e480423d72b..daf3df0e441da1499e58b8ff9fa1c7dcc0191965 100644 (file)
@@ -4,14 +4,14 @@
   "description": "Library on top of node js worker threads that implement various worker pools type",
   "main": "lib/index.js",
   "scripts": {
-    "build": "rollup --config",
-    "build:dev": "rollup --config --environment BUILD:development",
-    "benchmark": "npm run build && node benchmarks/bench.js",
-    "benchmark:debug": "npm run build:dev && node -r source-map-support/register --inspect-brk benchmarks/bench.js",
-    "benchmark:debug:vscode": "node -r source-map-support/register benchmarks/bench.js",
-    "test": "npm run build && nyc mocha --exit --timeout 20000 'tests/**/*.test.js'",
-    "test:debug": "npm run build:dev && mocha -r source-map-support/register --inspect-brk --exit 'tests/**/*.test.js'",
-    "test:debug:vscode": "mocha -r source-map-support/register --exit 'tests/**/*.test.js'",
+    "build": "rollup --config --environment BUILD:development",
+    "build:prod": "rollup --config",
+    "benchmark": "npm run build && node -r source-map-support/register benchmarks/bench.js",
+    "benchmark:debug": "npm run build && node -r source-map-support/register --inspect benchmarks/bench.js",
+    "benchmark:prod": "npm run build:prod && node -r source-map-support/register benchmarks/bench.js",
+    "test": "npm run build && nyc mocha -r source-map-support/register --exit --timeout 20000 'tests/**/*.test.js'",
+    "test:debug": "npm run build && mocha -r source-map-support/register --inspect --exit --timeout 20000 'tests/**/*.test.js'",
+    "test:prod": "npm run build:prod && nyc mocha -r source-map-support/register --exit --timeout 20000 'tests/**/*.test.js'",
     "sonar": "sonar-scanner",
     "coverage": "nyc report --reporter=lcov --check-coverage --lines 80",
     "coverage:html": "nyc report --reporter=html --check-coverage --lines 80",
     "lib"
   ],
   "devDependencies": {
-    "@types/node": "^14.14.27",
-    "@typescript-eslint/eslint-plugin": "^4.15.0",
-    "@typescript-eslint/parser": "^4.15.0",
+    "@types/node": "^14.14.28",
+    "@typescript-eslint/eslint-plugin": "^4.15.1",
+    "@typescript-eslint/parser": "^4.15.1",
     "benchmark": "^2.1.4",
     "eslint": "^7.20.0",
     "eslint-config-standard": "^16.0.2",
     "eslint-plugin-import": "^2.22.1",
-    "eslint-plugin-jsdoc": "^31.6.1",
+    "eslint-plugin-jsdoc": "^32.0.1",
     "eslint-plugin-node": "^11.1.0",
     "eslint-plugin-prettierx": "^0.17.1",
     "eslint-plugin-promise": "^4.3.1",
index 8afbc006de8827bccd162a14d2d3a1d4e33de695..bac8f614a5524bd3983969c29ef1d4b36f6fb202 100644 (file)
@@ -4,24 +4,18 @@ import { terser } from 'rollup-plugin-terser'
 import del from 'rollup-plugin-delete'
 
 const isDevelopmentBuild = process.env.BUILD === 'development'
+const isAnalyze = process.env.ANALYZE
 
 export default {
   input: 'src/index.ts',
-  output: [
-    {
-      dir: 'lib',
-      format: 'cjs',
-      sourcemap: !!isDevelopmentBuild,
-      preserveModules: true,
-      preserveModulesRoot: 'src'
-    },
-    isDevelopmentBuild && {
-      file: 'lib.min/index.js',
-      format: 'cjs',
-      sourcemap: !!isDevelopmentBuild,
-      plugins: [terser({ numWorkers: 2 })]
-    }
-  ],
+  output: {
+    ...(isDevelopmentBuild ? { dir: 'lib' } : { file: 'lib/index.js' }),
+    format: 'cjs',
+    sourcemap: !!isDevelopmentBuild,
+    ...(isDevelopmentBuild && { preserveModules: true }),
+    ...(isDevelopmentBuild && { preserveModulesRoot: 'src' }),
+    ...(!isDevelopmentBuild && { plugins: [terser({ numWorkers: 2 })] })
+  },
   external: ['async_hooks', 'cluster', 'events', 'worker_threads'],
   plugins: [
     typescript({
@@ -30,8 +24,8 @@ export default {
         : 'tsconfig.json'
     }),
     del({
-      targets: ['lib/*', 'lib.min/*']
+      targets: ['lib/*']
     }),
-    isDevelopmentBuild && analyze()
+    isAnalyze && analyze()
   ]
 }
index 846b8a5ac99f32bcdb7eda8d8cf9679824e54a08..4bd411a059030ac90e5dba334db47b4451ff5509 100644 (file)
@@ -4,7 +4,7 @@ sonar.javascript.lcov.reportPaths=coverage/lcov.info
 sonar.projectName=poolifier
 sonar.projectVersion=2.0.0
 sonar.host.url=https://sonarcloud.io
-sonar.sources=lib
+sonar.sources=src
 sonar.tests=tests
 # sonar.login=TOKEN #To use for local troubleshooting
 sonar.verbose=false
index 7242dd5e1fbe4c36bbbbd00f17bc3af7cc2c756d..6cc415000d1b27c19483417ad3ce9013b2b5aa59 100644 (file)
@@ -15,4 +15,5 @@ export type { ThreadWorkerWithMessageChannel } from './pools/thread/fixed'
 export { AbstractWorker } from './worker/abstract-worker'
 export { ClusterWorker } from './worker/cluster-worker'
 export { ThreadWorker } from './worker/thread-worker'
-export type { WorkerOptions } from './worker/worker-options'
+export { KillBehaviors } from './worker/worker-options'
+export type { KillBehavior, WorkerOptions } from './worker/worker-options'
index 2ffd292afab9a0b540826820935b4a0febd34057..0685f998d4aaf11d0cb93d86fcdb24b0e3476fbc 100644 (file)
@@ -83,7 +83,7 @@ export abstract class AbstractPool<
 
   /**
    * - `key`: The `Worker`
-   * - `value`: Number of tasks that has been assigned to that worker since it started
+   * - `value`: Number of tasks currently in progress on the worker.
    */
   public readonly tasks: Map<Worker, number> = new Map<Worker, number>()
 
@@ -120,7 +120,6 @@ export abstract class AbstractPool<
     if (!this.filePath) {
       throw new Error('Please specify a file with a worker implementation')
     }
-
     this.setupHook()
 
     for (let i = 1; i <= this.numberOfWorkers; i++) {
@@ -191,9 +190,23 @@ export abstract class AbstractPool<
    * @param worker Workers whose tasks are increased.
    */
   protected increaseWorkersTask (worker: Worker): void {
-    const numberOfTasksTheWorkerHas = this.tasks.get(worker)
-    if (numberOfTasksTheWorkerHas !== undefined) {
-      this.tasks.set(worker, numberOfTasksTheWorkerHas + 1)
+    const numberOfTasksInProgress = this.tasks.get(worker)
+    if (numberOfTasksInProgress !== undefined) {
+      this.tasks.set(worker, numberOfTasksInProgress + 1)
+    } else {
+      throw Error('Worker could not be found in tasks map')
+    }
+  }
+
+  /**
+   * Decrease the number of tasks that the given workers has done.
+   *
+   * @param worker Workers whose tasks are decreased.
+   */
+  protected decreaseWorkersTasks (worker: Worker): void {
+    const numberOfTasksInProgress = this.tasks.get(worker)
+    if (numberOfTasksInProgress !== undefined) {
+      this.tasks.set(worker, numberOfTasksInProgress - 1)
     } else {
       throw Error('Worker could not be found in tasks map')
     }
@@ -254,7 +267,7 @@ export abstract class AbstractPool<
       const listener: (message: MessageValue<Response>) => void = message => {
         if (message.id === messageId) {
           this.unregisterWorkerMessageListener(worker, listener)
-          this.increaseWorkersTask(worker)
+          this.decreaseWorkersTasks(worker)
           if (message.error) reject(message.error)
           else resolve(message.data as Response)
         }
index b0bb697372cdc3312d4cf78200ecc7ceca214cac..e97ab4a9be2fdbe536fd8ac5e6fa8ae65ea1b41d 100644 (file)
@@ -1,5 +1,6 @@
 import type { Worker } from 'cluster'
 import type { JSONValue } from '../../utility-types'
+import { isKillBehavior, KillBehaviors } from '../../worker/worker-options'
 import type { ClusterPoolOptions } from './fixed'
 import { FixedClusterPool } from './fixed'
 
@@ -59,13 +60,18 @@ export class DynamicClusterPool<
     }
 
     // All workers are busy, create a new worker
-    const worker = this.createAndSetupWorker()
-    this.registerWorkerMessageListener<Data>(worker, message => {
-      if (message.kill) {
-        this.sendToWorker(worker, { kill: 1 })
-        void this.destroyWorker(worker)
+    const workerCreated = this.createAndSetupWorker()
+    this.registerWorkerMessageListener<Data>(workerCreated, message => {
+      const tasksInProgress = this.tasks.get(workerCreated)
+      if (
+        isKillBehavior(KillBehaviors.HARD, message.kill) ||
+        tasksInProgress === 0
+      ) {
+        // Kill received from the worker, means that no new tasks are submitted to that worker for a while ( > maxInactiveTime)
+        this.sendToWorker(workerCreated, { kill: 1 })
+        void this.destroyWorker(workerCreated)
       }
     })
-    return worker
+    return workerCreated
   }
 }
index fddae4662e8b4a4a2b68baf043015fa498d78b4f..86950b43a6de1d79431a587b0d42f78e91bcb99a 100644 (file)
@@ -1,4 +1,5 @@
 import type { JSONValue } from '../../utility-types'
+import { isKillBehavior, KillBehaviors } from '../../worker/worker-options'
 import type { PoolOptions } from '../abstract-pool'
 import type { ThreadWorkerWithMessageChannel } from './fixed'
 import { FixedThreadPool } from './fixed'
@@ -59,13 +60,18 @@ export class DynamicThreadPool<
     }
 
     // All workers are busy, create a new worker
-    const worker = this.createAndSetupWorker()
-    this.registerWorkerMessageListener<Data>(worker, message => {
-      if (message.kill) {
-        this.sendToWorker(worker, { kill: 1 })
-        void this.destroyWorker(worker)
+    const workerCreated = this.createAndSetupWorker()
+    this.registerWorkerMessageListener<Data>(workerCreated, message => {
+      const tasksInProgress = this.tasks.get(workerCreated)
+      if (
+        isKillBehavior(KillBehaviors.HARD, message.kill) ||
+        tasksInProgress === 0
+      ) {
+        // Kill received from the worker, means that no new tasks are submitted to that worker for a while ( > maxInactiveTime)
+        this.sendToWorker(workerCreated, { kill: 1 })
+        void this.destroyWorker(workerCreated)
       }
     })
-    return worker
+    return workerCreated
   }
 }
index eb3f9727369f837d9cd36d5bd6b7dd69c9d5f0c3..d680fa16ddc7e4748a41f2e878def0384eb2e305 100644 (file)
@@ -1,5 +1,6 @@
 import type { Worker } from 'cluster'
 import type { MessagePort } from 'worker_threads'
+import type { KillBehavior } from './worker/worker-options'
 
 /**
  * Make all properties in T non-readonly
@@ -42,7 +43,7 @@ export interface MessageValue<
   /**
    * Kill code.
    */
-  readonly kill?: number
+  readonly kill?: KillBehavior | 1
   /**
    * Error.
    */
index 34ab964c68d17c873b13149d2b228f570a9a6595..6aa34e8e899fda868bf0c1935b656a21d8b67629 100644 (file)
@@ -2,7 +2,11 @@ import { AsyncResource } from 'async_hooks'
 import type { Worker } from 'cluster'
 import type { MessagePort } from 'worker_threads'
 import type { MessageValue } from '../utility-types'
-import type { WorkerOptions } from './worker-options'
+import type { KillBehavior, WorkerOptions } from './worker-options'
+import { KillBehaviors } from './worker-options'
+
+const DEFAULT_MAX_INACTIVE_TIME = 1000 * 60
+const DEFAULT_KILL_BEHAVIOR: KillBehavior = KillBehaviors.SOFT
 
 /**
  * Base class containing some shared logic for all poolifier workers.
@@ -20,6 +24,10 @@ export abstract class AbstractWorker<
    * The maximum time to keep this worker alive while idle. The pool automatically checks and terminates this worker when the time expires.
    */
   protected readonly maxInactiveTime: number
+  /**
+   * The kill behavior set as option on the Worker constructor or a default value.
+   */
+  protected readonly killBehavior: KillBehavior
   /**
    * Whether the worker is working asynchronously or not.
    */
@@ -47,11 +55,15 @@ export abstract class AbstractWorker<
     isMain: boolean,
     fn: (data: Data) => Response,
     protected mainWorker?: MainWorker | null,
-    public readonly opts: WorkerOptions = {}
+    public readonly opts: WorkerOptions = {
+      killBehavior: DEFAULT_KILL_BEHAVIOR,
+      maxInactiveTime: DEFAULT_MAX_INACTIVE_TIME
+    }
   ) {
     super(type)
-
-    this.maxInactiveTime = this.opts.maxInactiveTime ?? 1000 * 60
+    this.killBehavior = this.opts.killBehavior ?? DEFAULT_KILL_BEHAVIOR
+    this.maxInactiveTime =
+      this.opts.maxInactiveTime ?? DEFAULT_MAX_INACTIVE_TIME
     this.async = !!this.opts.async
     this.lastTask = Date.now()
     if (!fn) throw new Error('fn parameter is mandatory')
@@ -108,7 +120,7 @@ export abstract class AbstractWorker<
    */
   protected checkAlive (): void {
     if (Date.now() - this.lastTask > this.maxInactiveTime) {
-      this.sendToMainWorker({ kill: 1 })
+      this.sendToMainWorker({ kill: this.killBehavior })
     }
   }
 
index 0236164b382ed66972789c8ce3209e73e6b28564..4473a97533b5c07db172d1c756ae0bce47d1d03a 100644 (file)
@@ -1,3 +1,37 @@
+/**
+ * Enumeration of kill behaviors.
+ */
+export const KillBehaviors = Object.freeze({
+  /**
+   * If `currentTime - lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker **wont** be deleted.
+   */
+  SOFT: 'SOFT',
+  /**
+   * If `lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker will be deleted.
+   */
+  HARD: 'HARD'
+} as const)
+
+/**
+ * Kill behavior.
+ */
+export type KillBehavior = keyof typeof KillBehaviors
+
+/**
+ * Detects whether the given value is a kill behavior or not.
+ *
+ * @template KB Which specific KillBehavior to test against.
+ * @param killBehavior Which kind of kill behavior to detect. Default: `KillBehaviors.HARD`.
+ * @param value Any value.
+ * @returns `true` if `value` was strictly equals to `killBehavior`, otherwise `false`.
+ */
+export function isKillBehavior<KB extends KillBehavior> (
+  killBehavior: KB,
+  value: unknown
+): value is KB {
+  return value === killBehavior
+}
+
 /**
  * Options for workers.
  */
@@ -6,6 +40,11 @@ export interface WorkerOptions {
    * Maximum waiting time in milliseconds for tasks.
    *
    * After this time, newly created workers will be terminated.
+   * The last active time of your worker unit will be updated when a task is submitted to a worker or when a worker terminate a task.
+   *
+   * - If `killBehavior` is set to `KillBehaviors.HARD` this value represents also the timeout for the tasks that you submit to the pool,
+   *   when this timeout expires your tasks is interrupted and the worker is killed if is not part of the minimum size of the pool.
+   * - If `killBehavior` is set to `KillBehaviors.SOFT` your tasks have no timeout and your workers will not be terminated until your task is.
    *
    * @default 60.000 ms
    */
@@ -16,4 +55,15 @@ export interface WorkerOptions {
    * @default false
    */
   async?: boolean
+  /**
+   * `killBehavior` dictates if your async unit (worker/process) will be deleted in case that a task is active on it.
+   *
+   * - SOFT: If `currentTime - lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker **wont** be deleted.
+   * - HARD: If `lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker will be deleted.
+   *
+   * This option only apply to the newly created workers.
+   *
+   * @default KillBehaviors.SOFT
+   */
+  killBehavior?: KillBehavior
 }
diff --git a/tests/pools/abstract/abstract-pool.test.js b/tests/pools/abstract/abstract-pool.test.js
new file mode 100644 (file)
index 0000000..65d667f
--- /dev/null
@@ -0,0 +1,55 @@
+const expect = require('expect')
+const { FixedThreadPool } = require('../../../lib/index')
+const expectedError = new Error('Worker could not be found in tasks map')
+
+class StubPoolWithTasksMapClear extends FixedThreadPool {
+  removeAllWorker () {
+    this.tasks.clear()
+  }
+}
+
+class StubPoolWithIsMainMethod extends FixedThreadPool {
+  isMain () {
+    return false
+  }
+}
+
+describe('Abstract pool test suite ', () => {
+  it('Simulate worker not found during increaseWorkersTask', () => {
+    const pool = new StubPoolWithTasksMapClear(
+      1,
+      './tests/worker-files/cluster/testWorker.js',
+      {
+        errorHandler: e => console.error(e)
+      }
+    )
+    // simulate worker not found.
+    pool.removeAllWorker()
+    expect(() => pool.increaseWorkersTask()).toThrowError(expectedError)
+  })
+
+  it('Simulate worker not found during decreaseWorkersTasks', () => {
+    const pool = new StubPoolWithTasksMapClear(
+      1,
+      './tests/worker-files/cluster/testWorker.js',
+      {
+        errorHandler: e => console.error(e)
+      }
+    )
+    // simulate worker not found.
+    pool.removeAllWorker()
+    expect(() => pool.decreaseWorkersTasks()).toThrowError(expectedError)
+  })
+
+  it('Simulate pool creation from a non main thread/process', () => {
+    expect(() => {
+      const pool = new StubPoolWithIsMainMethod(
+        1,
+        './tests/worker-files/cluster/testWorker.js',
+        {
+          errorHandler: e => console.error(e)
+        }
+      )
+    }).toThrowError()
+  })
+})
index 36d17d8695f37c4698a294b7e66b2f28a4beb4a3..01b720720ac4761a768645b329d3cdb00a93283e 100644 (file)
@@ -7,8 +7,7 @@ const pool = new DynamicClusterPool(
   max,
   './tests/worker-files/cluster/testWorker.js',
   {
-    errorHandler: e => console.error(e),
-    onlineHandler: () => console.log('worker is online')
+    errorHandler: e => console.error(e)
   }
 )
 
@@ -51,7 +50,7 @@ describe('Dynamic cluster pool test suite ', () => {
       pool.execute({ test: 'test' })
     }
     expect(pool.workers.length).toBeGreaterThan(min)
-    await new Promise(resolve => setTimeout(resolve, 2000))
+    await new Promise(resolve => setTimeout(resolve, 3000))
     expect(pool.workers.length).toBe(min)
   })
   it('Shutdown test', async () => {
@@ -62,7 +61,7 @@ describe('Dynamic cluster pool test suite ', () => {
       })
     })
     pool.destroy()
-    await new Promise(resolve => setTimeout(resolve, 1000))
+    await new Promise(resolve => setTimeout(resolve, 2000))
     expect(closedWorkers).toBe(min)
   })
 
@@ -87,4 +86,36 @@ describe('Dynamic cluster pool test suite ', () => {
     const res = await pool1.execute({ test: 'test' })
     expect(res).toBeFalsy()
   })
+
+  it('Verify scale processes up and down is working when long running task is used:hard', async () => {
+    const longRunningPool = new DynamicClusterPool(
+      min,
+      max,
+      './tests/worker/cluster/longRunningWorkerHardBehavior.js'
+    )
+    expect(longRunningPool.workers.length).toBe(min)
+    for (let i = 0; i < max * 10; i++) {
+      longRunningPool.execute({ test: 'test' })
+    }
+    expect(longRunningPool.workers.length).toBe(max)
+    await new Promise(resolve => setTimeout(resolve, 3000))
+    // Here we expect the workers to be at the max size since that the task is still running
+    expect(longRunningPool.workers.length).toBe(min)
+  })
+
+  it('Verify scale processes up and down is working when long running task is used:soft', async () => {
+    const longRunningPool = new DynamicClusterPool(
+      min,
+      max,
+      './tests/worker/cluster/longRunningWorkerSoftBehavior.js'
+    )
+    expect(longRunningPool.workers.length).toBe(min)
+    for (let i = 0; i < max * 10; i++) {
+      longRunningPool.execute({ test: 'test' })
+    }
+    expect(longRunningPool.workers.length).toBe(max)
+    await new Promise(resolve => setTimeout(resolve, 3000))
+    // Here we expect the workers to be at the max size since that the task is still running
+    expect(longRunningPool.workers.length).toBe(max)
+  })
 })
index 1cbfadf5b63ad3e029b61d47e24f6d6d9a445467..f5300eccf424c39028e340296b18d1d0492f45f4 100644 (file)
@@ -6,8 +6,7 @@ const pool = new FixedClusterPool(
   numberOfWorkers,
   './tests/worker-files/cluster/testWorker.js',
   {
-    errorHandler: e => console.error(e),
-    onlineHandler: () => console.log('worker is online')
+    errorHandler: e => console.error(e)
   }
 )
 const emptyPool = new FixedClusterPool(
@@ -22,8 +21,7 @@ const errorPool = new FixedClusterPool(
   1,
   './tests/worker-files/cluster/errorWorker.js',
   {
-    errorHandler: e => console.error(e),
-    onlineHandler: () => console.log('worker is online')
+    errorHandler: e => console.error(e)
   }
 )
 
@@ -124,7 +122,7 @@ describe('Fixed cluster pool test suite ', () => {
       })
     })
     await pool.destroy()
-    await new Promise(resolve => setTimeout(resolve, 200))
+    await new Promise(resolve => setTimeout(resolve, 500))
     expect(closedWorkers).toBe(numberOfWorkers)
   })
 
index 5f6dc8ba47cb92fe26aefc82a767dd2acaa2ec9b..4efb0f4b83bdad12b0d96e83d6276ab773198eda 100644 (file)
@@ -7,8 +7,7 @@ const pool = new DynamicThreadPool(
   max,
   './tests/worker-files/thread/testWorker.js',
   {
-    errorHandler: e => console.error(e),
-    onlineHandler: () => console.log('worker is online')
+    errorHandler: e => console.error(e)
   }
 )
 
@@ -50,9 +49,10 @@ describe('Dynamic thread pool test suite ', () => {
       pool.execute({ test: 'test' })
     }
     expect(pool.workers.length).toBe(max)
-    await new Promise(resolve => setTimeout(resolve, 1000))
+    await new Promise(resolve => setTimeout(resolve, 1500))
     expect(pool.workers.length).toBe(min)
   })
+
   it('Shutdown test', async () => {
     let closedThreads = 0
     pool.workers.forEach(w => {
@@ -85,4 +85,43 @@ describe('Dynamic thread pool test suite ', () => {
     const res = await pool1.execute({ test: 'test' })
     expect(res).toBeFalsy()
   })
+
+  it('Verify scale thread up and down is working when long running task is used:hard', async () => {
+    const longRunningPool = new DynamicThreadPool(
+      min,
+      max,
+      './tests/worker/thread/longRunningWorkerHardBehavior.js',
+      {
+        errorHandler: e => console.error(e),
+        onlineHandler: () => console.log('worker is online')
+      }
+    )
+    expect(longRunningPool.workers.length).toBe(min)
+    for (let i = 0; i < max * 10; i++) {
+      longRunningPool.execute({ test: 'test' })
+    }
+    expect(longRunningPool.workers.length).toBe(max)
+    await new Promise(resolve => setTimeout(resolve, 1500))
+    expect(longRunningPool.workers.length).toBe(min)
+  })
+
+  it('Verify scale thread up and down is working when long running task is used:soft', async () => {
+    const longRunningPool = new DynamicThreadPool(
+      min,
+      max,
+      './tests/worker/thread/longRunningWorkerSoftBehavior.js',
+      {
+        errorHandler: e => console.error(e),
+        onlineHandler: () => console.log('worker is online')
+      }
+    )
+    expect(longRunningPool.workers.length).toBe(min)
+    for (let i = 0; i < max * 10; i++) {
+      longRunningPool.execute({ test: 'test' })
+    }
+    expect(longRunningPool.workers.length).toBe(max)
+    await new Promise(resolve => setTimeout(resolve, 1500))
+    // Here we expect the workers to be at the max size since that the task is still running
+    expect(longRunningPool.workers.length).toBe(max)
+  })
 })
index ff945a700f6bf6efe85359b260499597c3497a1c..755ccf4d4fa2aa12b0621344d1010af377431248 100644 (file)
@@ -6,8 +6,7 @@ const pool = new FixedThreadPool(
   numberOfThreads,
   './tests/worker-files/thread/testWorker.js',
   {
-    errorHandler: e => console.error(e),
-    onlineHandler: () => console.log('worker is online')
+    errorHandler: e => console.error(e)
   }
 )
 const emptyPool = new FixedThreadPool(
index 2476853b55520ab5fea4c917fb70d0f05c66dfa0..b9fd9016ab91801122e4cb6394b5e590377b4f3f 100644 (file)
@@ -1,5 +1,5 @@
 'use strict'
-const { ClusterWorker } = require('../../../lib/index')
+const { ClusterWorker, KillBehaviors } = require('../../../lib/index')
 
 async function error (data) {
   return new Promise((resolve, reject) => {
@@ -12,5 +12,6 @@ async function error (data) {
 
 module.exports = new ClusterWorker(error, {
   maxInactiveTime: 500,
-  async: true
+  async: true,
+  killBehavior: KillBehaviors.HARD
 })
index 975b0b365efb92dad4d24f44a20434f1267a4cd4..b5c784dfaca6b7491b25914ceefa81a94042c44f 100644 (file)
@@ -1,5 +1,5 @@
 'use strict'
-const { ClusterWorker } = require('../../../lib/index')
+const { ClusterWorker, KillBehaviors } = require('../../../lib/index')
 
 async function sleep (data) {
   return new Promise((resolve, reject) => {
@@ -7,4 +7,8 @@ async function sleep (data) {
   })
 }
 
-module.exports = new ClusterWorker(sleep, { maxInactiveTime: 500, async: true })
+module.exports = new ClusterWorker(sleep, {
+  maxInactiveTime: 500,
+  async: true,
+  killBehavior: KillBehaviors.HARD
+})
index 6c77bcce888b77b20f4f6045d4bc3f4fe00905a3..054c4bb30c2d8ef2ad2ed9dd52bc5c6f9af793e2 100644 (file)
@@ -1,8 +1,11 @@
 'use strict'
-const { ClusterWorker } = require('../../../lib/index')
+const { ClusterWorker, KillBehaviors } = require('../../../lib/index')
 
 function echo (data) {
   return data
 }
 
-module.exports = new ClusterWorker(echo, { maxInactiveTime: 500 })
+module.exports = new ClusterWorker(echo, {
+  maxInactiveTime: 500,
+  killBehavior: KillBehaviors.HARD
+})
index 62c8e2bb0438e426dd3838101002b53bba20b692..58c55af12b8c171a12c8b430a3b32b3e2d7c0dc5 100644 (file)
@@ -1,6 +1,9 @@
 'use strict'
-const { ClusterWorker } = require('../../../lib/index')
+const { ClusterWorker, KillBehaviors } = require('../../../lib/index')
 
 function test (data) {}
 
-module.exports = new ClusterWorker(test, { maxInactiveTime: 500 })
+module.exports = new ClusterWorker(test, {
+  maxInactiveTime: 500,
+  killBehavior: KillBehaviors.HARD
+})
index 87df92543664b6e3c5395a52e8c822d65ea0c8b6..d6d9297ac8e5f671c3249622139c3ba0c8b8d8b6 100644 (file)
@@ -1,5 +1,5 @@
 'use strict'
-const { ClusterWorker } = require('../../../lib/index')
+const { ClusterWorker, KillBehaviors } = require('../../../lib/index')
 
 function error (data) {
   throw new Error('Error Message from ClusterWorker')
@@ -7,5 +7,6 @@ function error (data) {
 
 module.exports = new ClusterWorker(error, {
   maxInactiveTime: 500,
-  async: false
+  async: false,
+  killBehavior: KillBehaviors.HARD
 })
index a8a6bb9ee14a0e1e670bb12348dfc47a1590b1b3..7caad9476da291f19319b981c67e8115ac0e32a7 100644 (file)
@@ -1,5 +1,5 @@
 'use strict'
-const { ClusterWorker } = require('../../../lib/index')
+const { ClusterWorker, KillBehaviors } = require('../../../lib/index')
 const { isMaster } = require('cluster')
 
 function test (data) {
@@ -12,4 +12,7 @@ function test (data) {
   return isMaster
 }
 
-module.exports = new ClusterWorker(test, { maxInactiveTime: 500 })
+module.exports = new ClusterWorker(test, {
+  maxInactiveTime: 500,
+  killBehavior: KillBehaviors.HARD
+})
index 25401cfb14dea2ff9fed837f138f349218ff7b34..0bf5d244758ce100683ea9c23faf114bd03428c8 100644 (file)
@@ -1,5 +1,5 @@
 'use strict'
-const { ThreadWorker } = require('../../../lib/index')
+const { ThreadWorker, KillBehaviors } = require('../../../lib/index')
 
 async function sleep (data) {
   return new Promise((resolve, reject) => {
@@ -7,4 +7,8 @@ async function sleep (data) {
   })
 }
 
-module.exports = new ThreadWorker(sleep, { maxInactiveTime: 500, async: true })
+module.exports = new ThreadWorker(sleep, {
+  maxInactiveTime: 500,
+  async: true,
+  killBehavior: KillBehaviors.HARD
+})
index 006bf97cd390f59f2c5bd37c28a2bfbb307a889b..071428c5bc5f1414b5e61718ccf605826480bf45 100644 (file)
@@ -1,8 +1,11 @@
 'use strict'
-const { ThreadWorker } = require('../../../lib/index')
+const { ThreadWorker, KillBehaviors } = require('../../../lib/index')
 
 function echo (data) {
   return data
 }
 
-module.exports = new ThreadWorker(echo, { maxInactiveTime: 500 })
+module.exports = new ThreadWorker(echo, {
+  maxInactiveTime: 500,
+  killBehavior: KillBehaviors.HARD
+})
index 69a83a7710fed25eeae541c9f1b83b737d135664..6a146c2653496f99238945c6648aa1dbe51ab4e2 100644 (file)
@@ -1,6 +1,9 @@
 'use strict'
-const { ThreadWorker } = require('../../../lib/index')
+const { ThreadWorker, KillBehaviors } = require('../../../lib/index')
 
 function test (data) {}
 
-module.exports = new ThreadWorker(test, { maxInactiveTime: 500 })
+module.exports = new ThreadWorker(test, {
+  maxInactiveTime: 500,
+  killBehavior: KillBehaviors.HARD
+})
index 63a27513a74638aafbfabc08f04c6dec01f46ea9..e9f20ab8e5101120d7f51459bf338676208f404f 100644 (file)
@@ -1,8 +1,11 @@
 'use strict'
-const { ThreadWorker } = require('../../../lib/index')
+const { ThreadWorker, KillBehaviors } = require('../../../lib/index')
 
 function error (data) {
   throw new Error(data)
 }
 
-module.exports = new ThreadWorker(error, { maxInactiveTime: 500 })
+module.exports = new ThreadWorker(error, {
+  maxInactiveTime: 500,
+  killBehavior: KillBehaviors.HARD
+})
index 3556da01f8051960125d833ec4b38e7196f6ad94..c70069c7269f17d2f83069554d0e3fba5d2a15e6 100644 (file)
@@ -1,5 +1,5 @@
 'use strict'
-const { ThreadWorker } = require('../../../lib/index')
+const { ThreadWorker, KillBehaviors } = require('../../../lib/index')
 const { isMainThread } = require('worker_threads')
 
 function test (data) {
@@ -12,4 +12,7 @@ function test (data) {
   return isMainThread
 }
 
-module.exports = new ThreadWorker(test, { maxInactiveTime: 500 })
+module.exports = new ThreadWorker(test, {
+  maxInactiveTime: 500,
+  killBehavior: KillBehaviors.HARD
+})
diff --git a/tests/worker/cluster/longRunningWorkerHardBehavior.js b/tests/worker/cluster/longRunningWorkerHardBehavior.js
new file mode 100644 (file)
index 0000000..04c78f4
--- /dev/null
@@ -0,0 +1,14 @@
+'use strict'
+const { ClusterWorker, KillBehaviors } = require('../../../lib/index')
+
+async function sleep (data) {
+  return new Promise((resolve, reject) => {
+    setTimeout(() => resolve(data), 50000)
+  })
+}
+
+module.exports = new ClusterWorker(sleep, {
+  maxInactiveTime: 500,
+  async: true,
+  killBehavior: KillBehaviors.HARD
+})
diff --git a/tests/worker/cluster/longRunningWorkerSoftBehavior.js b/tests/worker/cluster/longRunningWorkerSoftBehavior.js
new file mode 100644 (file)
index 0000000..c4c00f6
--- /dev/null
@@ -0,0 +1,13 @@
+'use strict'
+const { ClusterWorker } = require('../../../lib/index')
+
+async function sleep (data) {
+  return new Promise((resolve, reject) => {
+    setTimeout(() => resolve(data), 50000)
+  })
+}
+
+module.exports = new ClusterWorker(sleep, {
+  maxInactiveTime: 500,
+  async: true
+})
diff --git a/tests/worker/thread/longRunningWorkerHardBehavior.js b/tests/worker/thread/longRunningWorkerHardBehavior.js
new file mode 100644 (file)
index 0000000..7d9714a
--- /dev/null
@@ -0,0 +1,14 @@
+'use strict'
+const { ThreadWorker, KillBehaviors } = require('../../../lib/index')
+
+async function sleep (data) {
+  return new Promise((resolve, reject) => {
+    setTimeout(() => resolve(data), 50000)
+  })
+}
+
+module.exports = new ThreadWorker(sleep, {
+  maxInactiveTime: 500,
+  async: true,
+  killBehavior: KillBehaviors.HARD
+})
diff --git a/tests/worker/thread/longRunningWorkerSoftBehavior.js b/tests/worker/thread/longRunningWorkerSoftBehavior.js
new file mode 100644 (file)
index 0000000..eed0586
--- /dev/null
@@ -0,0 +1,13 @@
+'use strict'
+const { ThreadWorker } = require('../../../lib/index')
+
+async function sleep (data) {
+  return new Promise((resolve, reject) => {
+    setTimeout(() => resolve(data), 50000)
+  })
+}
+
+module.exports = new ThreadWorker(sleep, {
+  maxInactiveTime: 500,
+  async: true
+})