@talabes/throng
Advanced tools
Comparing version 1.0.0 to 2.0.0
@@ -1,37 +0,40 @@ | ||
const cluster = require('cluster') | ||
const os = require('os') | ||
const defaultsDeep = require('lodash').defaultsDeep | ||
const cluster = require("cluster"); | ||
const os = require("os"); | ||
const nCPU = os.cpus().length | ||
const defaults = { | ||
const nCPU = os.cpus().length; | ||
const defaultOpts = { | ||
master: () => {}, | ||
count: nCPU, | ||
delay: 0, | ||
lifetime: Infinity, | ||
grace: 5000, | ||
signals: ['SIGTERM', 'SIGINT'], | ||
} | ||
signals: ["SIGTERM", "SIGINT"], | ||
}; | ||
module.exports = async function throng(options, legacy) { | ||
const config = defaultsDeep({}, parseOptions(options, legacy), defaults) | ||
const worker = config.worker | ||
const master = config.master | ||
module.exports = async function throng(options) { | ||
const config = Object.assign(defaultOpts, parseOptions(options)); | ||
const worker = config.worker; | ||
if (typeof worker !== 'function') { | ||
throw new Error('Start function required'); | ||
if (typeof worker !== "function") { | ||
throw new Error(`Start function required, config: ${options}`); | ||
} | ||
if (cluster.isWorker) { | ||
return await worker(cluster.worker.id, disconnect) | ||
await worker(cluster.worker.id, disconnect); | ||
} else { | ||
startMaster(config) | ||
} | ||
} | ||
const reviveUntil = Date.now() + config.lifetime | ||
let running = true | ||
listen() | ||
await master() | ||
fork(config.count) | ||
async function startMaster (config) { | ||
const reviveUntil = Date.now() + config.lifetime; | ||
let running = true; | ||
listen(); | ||
await config.master(); | ||
fork(config.count, config.delay); | ||
function listen() { | ||
cluster.on('disconnect', revive) | ||
config.signals.forEach(signal => process.on(signal, shutdown(signal))) | ||
cluster.on("exit", revive); | ||
config.signals.forEach((signal) => process.on(signal, shutdown(signal))); | ||
} | ||
@@ -41,26 +44,33 @@ | ||
return () => { | ||
running = false | ||
setTimeout(() => forceKill(signal), config.grace).unref() | ||
Object.values(cluster.workers).forEach(w => { | ||
w.process.kill(signal) | ||
}) | ||
} | ||
running = false; | ||
setTimeout(() => forceKill(signal), config.grace).unref(); | ||
Object.values(cluster.workers).forEach((w) => { | ||
w.process.kill(signal); | ||
}); | ||
}; | ||
} | ||
function revive() { | ||
if (!running) return | ||
if (Date.now() >= reviveUntil) return | ||
cluster.fork() | ||
if (!running) return; | ||
if (Date.now() >= reviveUntil) return; | ||
cluster.fork(); | ||
} | ||
function forceKill(signal) { | ||
Object.values(cluster.workers).forEach(w => w.kill(signal)) | ||
process.exit() | ||
Object.values(cluster.workers).forEach((w) => w.kill(signal)); | ||
process.exit(); | ||
} | ||
} | ||
function fork(n) { | ||
for (var i = 0; i < n; i++) { | ||
cluster.fork() | ||
function fork(numForks, delayMs) { | ||
for (let i = 0; i < numForks; i++) { | ||
if (delayMs > 0) { | ||
const delay = i * delayMs; | ||
setTimeout(() => { | ||
cluster.fork(); | ||
}, delay); | ||
} else { | ||
cluster.fork(); | ||
} | ||
} | ||
@@ -77,25 +87,10 @@ } | ||
function disconnect() { | ||
setTimeout(() => cluster.worker.disconnect(), 50) | ||
setTimeout(() => cluster.worker.disconnect(), 50); | ||
} | ||
// Once upon a time, | ||
// options could be startFn, options object, or worker count | ||
// and startFunction could be startFn or options object. | ||
// (whew - what a bad idea!) | ||
function parseOptions(options = {}, startFunction) { | ||
if (typeof options === 'function') { | ||
return { worker: options } | ||
function parseOptions(options = {}) { | ||
if (typeof options === "function") { | ||
return { worker: options }; | ||
} | ||
if (typeof options === 'number') { | ||
return { count: options, worker: startFunction } | ||
} | ||
return { | ||
master: options.master, | ||
worker: options.worker || options.start, | ||
count: options.count !== undefined ? options.count : options.workers, | ||
lifetime: options.lifetime, | ||
grace: options.grace, | ||
signals: options.signals, | ||
} | ||
} | ||
return options; | ||
} |
{ | ||
"name": "@talabes/throng", | ||
"version": "1.0.0", | ||
"version": "2.0.0", | ||
"description": "A simple worker-manager for clustered apps", | ||
@@ -29,6 +29,3 @@ "keywords": [ | ||
"mocha": "^8.1.3" | ||
}, | ||
"dependencies": { | ||
"lodash": "^4.17.20" | ||
} | ||
} |
# Throng | ||
Open source fork of <https://github.com/hunterloftis/throng> | ||
Fork of <https://github.com/hunterloftis/throng> | ||
* with typescript definition | ||
* using `exit` for reviving forks (recommended by [node docs](https://nodejs.org/api/cluster.html#cluster_event_exit_1)) | ||
* `delay` option if you want more progressive forking | ||
* check releases! | ||
Dead-simple one-liner for clustered Node.js apps. | ||
@@ -81,3 +86,4 @@ | ||
grace: 5000, // Grace period between signal and hard shutdown (ms) | ||
signals: ['SIGTERM', 'SIGINT'] // Signals that trigger a shutdown (proxied to workers) | ||
signals: ['SIGTERM', 'SIGINT'], // Signals that trigger a shutdown (proxied to workers) | ||
delay: 0 // Delay between each fork is created (milliseconds) | ||
}) | ||
@@ -144,9 +150,1 @@ ``` | ||
``` | ||
## Test | ||
```shell | ||
$ docker-compose run --rm dev | ||
node@docker:/home/app$ npm test | ||
``` |
0
7672
80
149
- Removedlodash@^4.17.20
- Removedlodash@4.17.21(transitive)