From 5b96a740fa6854b4925942417da3b35912f0e3ad Mon Sep 17 00:00:00 2001 From: Alexis Tyler Date: Fri, 11 Sep 2020 18:13:06 +0930 Subject: [PATCH] fix: make clustering optional --- .gitignore | 3 + app/graphql/index.ts | 4 +- app/run.ts | 2 +- cluster.js | 128 ++++++++++++++++++++++++++++++++++++++ index.js | 143 ++++++------------------------------------- package-lock.json | 82 +++++++++++++++---------- 6 files changed, 204 insertions(+), 158 deletions(-) create mode 100644 cluster.js diff --git a/.gitignore b/.gitignore index 4bad43f82..1b48a1ca9 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,6 @@ test/__temp__/* # Built files dist + +# Typescript +typescript \ No newline at end of file diff --git a/app/graphql/index.ts b/app/graphql/index.ts index cda02c42d..ca5542c07 100644 --- a/app/graphql/index.ts +++ b/app/graphql/index.ts @@ -263,11 +263,11 @@ class FuncDirective extends SchemaDirectiveVisitor { // Allow fields to be extracted if (directiveArgs.extractFromResponse) { const extractedField = get(result, directiveArgs.extractFromResponse); - log.debug(pluginOrModule, pluginOrModuleName, 'Result:', extractedField); + log.debug(pluginOrModule, pluginOrModuleName, 'Result:', JSON.stringify(extractedField)); return extractedField; } - log.debug(pluginOrModule, pluginOrModuleName, 'Result:', result); + log.debug(pluginOrModule, pluginOrModuleName, 'Result:', JSON.stringify(result)); return result; }; } diff --git a/app/run.ts b/app/run.ts index d901f7c71..50b2ac231 100644 --- a/app/run.ts +++ b/app/run.ts @@ -58,7 +58,7 @@ export const run = async (channel: string, mutation: string, options: RunOptions return resolve(moduleToRun(context)); }); - log.debug('Module:', moduleToRun.name, 'Result:', result.json); + // log.debug('Module:', moduleToRun.name, 'Result:', result.json); // Save result publish(channel, mutation, result.json); diff --git a/cluster.js b/cluster.js new file mode 100644 index 000000000..9acaa1ebf --- /dev/null +++ b/cluster.js @@ -0,0 +1,128 @@ +// @ts-check +const path = require('path'); +const cluster = require('cluster'); + +const log = process.env.NODE_ENV === 'production' ? { + info() { }, + log() { }, + debug() { }, + error() { } +} : console; + +// Set current working directory +process.chdir(__dirname); + +const RESTART_ATTEMPTS = 10; +let currentRestartAttempt = 0; +let currentWorker; +let restart = true; + +// @see https://github.com/nodejs/node-v0.x-archive/blob/master/doc/api/process.markdown#exit-codes +const onWorkerExit = (_, code) => { + if (!restart) { + process.exit(0); + } + + // Worker killed itself and doesn't want to be restarted + if (code === 130) { + log.debug(`Worker killed itself intentially. Exiting.`); + process.exit(130); + } + + // Reload worker + if (code === null || code === 0) { + const newWorker = cluster.fork(); + newWorker.once('online', () => { + currentRestartAttempt = 0; + currentWorker = newWorker; + }); + return; + } + + // Too many restarts, kill process + if (currentRestartAttempt >= RESTART_ATTEMPTS) { + log.debug(`No restart attempts left. Exiting.`); + process.exit(1); + } + + // Known error restart worker + currentRestartAttempt++; + currentWorker = cluster.fork(); +}; + +// Master +if (cluster.isMaster) { + // Set process name + process.title = 'gql-supervisor'; + + log.info(` pid = ${process.pid}`); + currentWorker = cluster.fork(); + + cluster.on('exit', onWorkerExit); + + // Allow reload on SIGHUP + process.on('SIGHUP', () => { + log.debug(' Reloading worker'); + currentWorker.send('shutdown'); + }); + + // Toggle debug logs + process.on('SIGUSR2', () => { + log.debug(' Updating log level.'); + currentWorker.send('SIGUSR2'); + }); + + // Kill all workers then exit gracefully + process.on('SIGTERM', () => { + log.info(`Killing worker`); + restart = false; + currentWorker.send('shutdown'); + }); +} + +// Worker +if (cluster.isWorker) { + log.info(` pid = ${process.pid}`); + + const isMissingMainFile = (error) => { + // Other error + if (error.code !== 'MODULE_NOT_FOUND') { + return false; + } + + // Missing file but it's multiple levels deep + // This likely isn't main + if (error.requireStack.length >= 2) { + return false; + } + + // It's a single require but for another file + if (error.requireStack[0] !== __filename) { + return false; + } + + return true; + }; + + // @ts-ignore + const package = require('./package.json'); + const { main } = package; + + if (!main) { + log.error(' Missing main field in package.json'); + process.exit(1); + } + + const mainPath = path.resolve(__dirname, main); + + try { + log.info(' loaded'); + require(mainPath); + } catch (error) { + if (isMissingMainFile(error)) { + log.error(` Missing main file "${mainPath}".`); + process.exit(130); + } + log.error(error); + } +} diff --git a/index.js b/index.js index 520239272..ee0f7c9b4 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,7 @@ +// @ts-check const path = require('path'); -const cluster = require('cluster'); +const package = require('./package.json'); +const { main } = package; const log = process.env.NODE_ENV === 'production' ? { info() { }, @@ -8,131 +10,26 @@ const log = process.env.NODE_ENV === 'production' ? { error() { } } : console; -// Set current working directory -process.chdir(__dirname); - -const RESTART_ATTEMPTS = 10; -let currentRestartAttempt = 0; -let currentWorker; -let restart = true; - -// @see https://github.com/nodejs/node-v0.x-archive/blob/master/doc/api/process.markdown#exit-codes -const onWorkerExit = (_, code) => { - if (!restart) { - process.exit(0); - } - - // Worker killed itself and doesn't want to be restarted - if (code === 130) { - log.debug(`Worker killed itself intentially. Exiting.`); - process.exit(130); - } - - // Reload worker - if (code === null || code === 0) { - const newWorker = cluster.fork(); - newWorker.once('online', () => { - currentRestartAttempt = 0; - currentWorker = newWorker; - }); - return; - } - - // Too many restarts, kill process - if (currentRestartAttempt >= RESTART_ATTEMPTS) { - log.debug(`No restart attempts left. Exiting.`); - process.exit(1); - } - - // Known error restart worker - currentRestartAttempt++; - currentWorker = cluster.fork(); -}; - -// Master -if (cluster.isMaster) { - // Set process name - process.title = 'gql-supervisor'; - - // Show real stack trace in development - if (process.env.NODE_ENV === 'development') { - try { - require('source-map-support').install({ - handleUncaughtExceptions: false - }); - } catch { - log.error(`Could not load "source-map-support", do you have it installed?`); - } - } - - log.info(` pid = ${process.pid}`); - currentWorker = cluster.fork(); - - cluster.on('exit', onWorkerExit); - - // Allow reload on SIGHUP - process.on('SIGHUP', () => { - log.debug(' Reloading worker'); - currentWorker.send('shutdown'); - }); - - // Toggle debug logs - process.on('SIGUSR2', () => { - log.debug(' Updating log level.'); - currentWorker.send('SIGUSR2'); - }); - - // Kill all workers then exit gracefully - process.on('SIGTERM', () => { - log.info(`Killing worker`); - restart = false; - currentWorker.send('shutdown'); - }); +if (!main) { + log.error(' Missing main field in package.json'); + process.exit(1); } -// Worker -if (cluster.isWorker) { - log.info(` pid = ${process.pid}`); - - const isMissingMainFile = (error) => { - // Other error - if (error.code !== 'MODULE_NOT_FOUND') { - return false; - } - - // Missing file but it's multiple levels deep - // This likely isn't main - if (error.requireStack.length >= 2) { - return false; - } - - // It's a single require but for another file - if (error.requireStack[0] !== __filename) { - return false; - } - - return true; - } - - // @ts-ignore - const package = require('./package.json'); - const { main } = package; - - if (!main) { - log.error(' Missing main field in package.json'); - process.exit(1); - } - - const mainPath = path.resolve(__dirname, main); +const mainPath = path.resolve(__dirname, main); +// Show real stack trace in development +if (process.env.NODE_ENV === 'development') { try { - log.info(' loaded'); - require(mainPath); - } catch (error) { - if (isMissingMainFile(error)) { - log.error(` Missing main file "${mainPath}".`); - process.exit(130); - } - log.error(error); + require('source-map-support').install({ + handleUncaughtExceptions: false + }); + } catch { + log.error(`Could not load "source-map-support", do you have it installed?`); } } + +if (!process.env.CLUSTER) { + require(mainPath); +} else { + require('./cluster'); +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 84d35f41b..bab095db4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1223,7 +1223,7 @@ } }, "@unraid/core": { - "version": "github:unraid/core#8c8d0f18ddc7dfd39716f0fc8e54620c7cca6dd9", + "version": "github:unraid/core#7241bb2f92301613f6de94591ef91ded54428467", "from": "github:unraid/core", "requires": { "accesscontrol": "^2.2.1", @@ -2248,9 +2248,9 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", - "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", + "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==" }, "babel-eslint": { "version": "10.1.0", @@ -2402,9 +2402,9 @@ } }, "bl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -6418,9 +6418,9 @@ }, "dependencies": { "bl": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz", - "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", + "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", "requires": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -7979,9 +7979,9 @@ }, "dependencies": { "type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", - "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", + "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" } } }, @@ -8925,9 +8925,9 @@ }, "dependencies": { "@types/node": { - "version": "9.6.57", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.57.tgz", - "integrity": "sha512-588MBlPWKeJFshLmnYbqMEaM3NaJFCVZFgpQ5rQxKCVXMNw2Gs7sTORvCDlaPBP6AabiIvmd22eT9fcIvTeZUw==" + "version": "9.6.58", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.58.tgz", + "integrity": "sha512-I5B2ZIvr5G5qW6VUZgMk284p/41/U5x3Lqyz3Hz9va3bmxgV7p5CBnDPGv/lGFgGDP4415pqZXLsa4cKA3BHAg==" } } }, @@ -11871,17 +11871,25 @@ } }, "mqtt-packet": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.3.2.tgz", - "integrity": "sha512-i56+2kN6F57KInGtjjfUXSl4xG8u/zOvfaXFLKFAbBXzWkXOmwcmjaSCBPayf2IQCkQU0+h+S2DizCo3CF6gQA==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.6.0.tgz", + "integrity": "sha512-LvghnKMFC70hKWMVykmhJarlO5e7lT3t9s9A2qPCUx+lazL3Mq55U+eCV0eLi7/nRRQYvEUWo/2tTo89EjnCJQ==", "requires": { - "bl": "^1.2.2", + "bl": "^4.0.2", "debug": "^4.1.1", - "inherits": "^2.0.3", - "process-nextick-args": "^2.0.0", - "safe-buffer": "^5.1.2" + "process-nextick-args": "^2.0.1" }, "dependencies": { + "bl": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", + "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -11889,6 +11897,16 @@ "requires": { "ms": "^2.1.1" } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } } } }, @@ -15772,9 +15790,9 @@ } }, "split2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.1.1.tgz", - "integrity": "sha512-emNzr1s7ruq4N+1993yht631/JH+jaj0NYBosuKmLcq+JkGQ9MmTw1RB1fGaTCzUuseRIClrlSLHRNYGwWQ58Q==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", "requires": { "readable-stream": "^3.0.0" }, @@ -15804,9 +15822,9 @@ }, "dependencies": { "@types/node": { - "version": "9.6.57", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.57.tgz", - "integrity": "sha512-588MBlPWKeJFshLmnYbqMEaM3NaJFCVZFgpQ5rQxKCVXMNw2Gs7sTORvCDlaPBP6AabiIvmd22eT9fcIvTeZUw==" + "version": "9.6.58", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.58.tgz", + "integrity": "sha512-I5B2ZIvr5G5qW6VUZgMk284p/41/U5x3Lqyz3Hz9va3bmxgV7p5CBnDPGv/lGFgGDP4415pqZXLsa4cKA3BHAg==" } } }, @@ -16370,9 +16388,9 @@ "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" }, "systeminformation": { - "version": "4.26.10", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-4.26.10.tgz", - "integrity": "sha512-bO4FIzrjESAfh4KHwkUJym3jvKtJ4oJ2PG0BBQGBmKa0pF2oanpkB7CF4ZsSX7vfp3+GKaLzioVwpV/3Tyk+lQ==" + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-4.27.3.tgz", + "integrity": "sha512-0Nc8AYEK818h7FI+bbe/kj7xXsMD5zOHvO9alUqQH/G4MHXu5tHQfWqC/bzWOk4JtoQPhnyLgxMYncDA2eeSBw==" }, "table": { "version": "5.4.6",