diff --git a/cluster.js b/cluster.js new file mode 100644 index 0000000..ece25d9 --- /dev/null +++ b/cluster.js @@ -0,0 +1,44 @@ +const cluster = require('cluster'); + +if (cluster.isPrimary) { + const requested = parseInt(process.env.WEB_CONCURRENCY, 10); + const workers = Number.isInteger(requested) && requested > 0 ? requested : 4; + console.log(`Primary ${process.pid} starting ${workers} workers`); + for (let i = 0; i < workers; i++) fork(); + + // resurrect a worker if it dies, unless we're shutting down, also track fast crashes and exit if too many happen in a row + let shuttingDown = false; + let fastCrashes = 0; + cluster.on('exit', (worker, code, signal) => { + if (shuttingDown) return; + const ranForMs = Date.now() - worker.startedAt; + if (ranForMs < 5000) { + fastCrashes++; + console.error(`Worker ${worker.process.pid} died after ${ranForMs}ms (${signal || code}) [fast-crash ${fastCrashes}/${workers}]`); + if (fastCrashes >= workers) { + console.error('Too many fast worker crashes; exiting for the service manager to restart.'); + process.exit(1); + } + } else { + fastCrashes = 0; // a healthy run clears the streak + console.log(`Worker ${worker.process.pid} died after ${Math.round(ranForMs / 1000)}s (${signal || code}); respawning`); + } + fork(); + }); + + for (const sig of ['SIGTERM', 'SIGINT']) { + process.on(sig, () => { + shuttingDown = true; + for (const w of Object.values(cluster.workers)) w.kill(sig); + }); + } +} else { + // equivalent to `probot run ./index.js` (app path passed as a positional arg) + require('probot').run([process.argv[0], process.argv[1], './index.js']); +} + +function fork() { + const worker = cluster.fork(); + worker.startedAt = Date.now(); + return worker; +} diff --git a/package.json b/package.json index 575dcd9..ae9cf67 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,10 @@ "license": "ISC", "repository": "https://github.com/stilliard/github-task-list-completed.git", "scripts": { - "dev-debug": "nodemon --exec \"LOG_LEVEL=debug npm start\"", - "dev": "nodemon --exec \"npm start\"", - "start": "probot run ./index.js", + "dev-debug": "nodemon --exec \"LOG_LEVEL=debug npm run start:single\"", + "dev": "nodemon --exec \"npm run start:single\"", + "start": "node cluster.js", + "start:single": "probot run ./index.js", "test": "jest" }, "dependencies": {