/* eslint global-require:0 */ 'use strict'; process.env.UV_THREADPOOL_SIZE = 16; const v8 = require('node:v8'); const Path = require('path'); const os = require('os'); const config = require('wild-config'); if (process.env.NODE_CONFIG_ONLY === 'true') { console.log(require('util').inspect(config, false, 22)); // eslint-disable-line return process.exit(); } const errors = require('./lib/errors'); const fs = require('fs'); const log = require('npmlog'); const packageData = require('./package.json'); const { init: initElasticSearch } = require('./lib/elasticsearch'); log.level = config.log.level; const printLogo = () => { let logo = fs .readFileSync(__dirname + '/logo.txt', 'utf-8') .replace(/^\n+|\n+$/g, '') .split('\n'); let columnLength = logo.map(l => l.length).reduce((max, val) => (val > max ? val : max), 0); let versionString = ' ' + packageData.name + '@' + packageData.version + ' '; let versionPrefix = '-'.repeat(Math.round(columnLength / 2 - versionString.length / 2)); let versionSuffix = '-'.repeat(columnLength - versionPrefix.length - versionString.length); log.info('App', ' ' + '-'.repeat(columnLength)); log.info('App', ''); logo.forEach(line => { log.info('App', ' ' + line); }); log.info('App', ''); log.info('App', ' ' + versionPrefix + versionString + versionSuffix); log.info('App', ''); }; let processCount = config.processes; if (processCount) { if (/^\s*cpus\s*$/i.test(processCount)) { processCount = os.cpus().length; } if (typeof processCount !== 'number' && !isNaN(processCount)) { processCount = Number(processCount); } if (isNaN(processCount)) { processCount = 1; } } if (!processCount || processCount <= 1) { printLogo(); if (config.ident) { process.title = config.ident; } // single process mode, do not fork anything initElasticSearch() .then(started => { if (started) { log.verbose('App', `ElasticSearch setup checked`); } }) .catch(err => { log.error('App', `ElasticSearch setup failed: ${err.message}${err.meta?.statusCode ? ` (${err.meta?.statusCode})` : ''}`); }) .finally(() => { require('./worker.js'); }); } else { let cluster = require('cluster'); if (cluster.isMaster) { printLogo(); if (config.ident) { process.title = config.ident + ' master'; } log.info('App', `Master [${process.pid}] is running`); let workers = new Set(); let forkWorker = () => { let worker = cluster.fork(); workers.add(worker); log.info('App', `Forked worker ${worker.process.pid}`); }; // Fork workers. initElasticSearch() .then(started => { if (started) { log.verbose('App', `ElasticSearch setup checked`); } }) .catch(err => { log.error('App', `ElasticSearch setup failed: ${err.message}${err.meta?.statusCode ? ` (${err.meta?.statusCode})` : ''}`); }) .finally(() => { for (let i = 0; i < processCount; i++) { forkWorker(); } }); cluster.on('exit', worker => { log.info('App', `Worker ${worker.process.pid} died`); workers.delete(worker); setTimeout(forkWorker, 1000); }); config.on('reload', () => { workers.forEach(child => { try { child.kill('SIGHUP'); } catch (E) { //ignore } }); }); } else { if (config.ident) { process.title = config.ident + ' worker'; } require('./worker.js'); } } process.on('unhandledRejection', err => { log.error('App', 'Unhandled rejection: %s', (err && err.stack) || err); errors.notify(err); }); process.on('SIGHUP', () => { // generate memory dump log.info('Process', 'PID=%s Generating heap snapshot...', process.pid); let stream; try { stream = v8.getHeapSnapshot(); } catch (err) { log.error('Process', 'PID=%s Failed to generate heap snapshot: %s', process.pid, err.stack || err); return; } if (stream) { const path = Path.join( os.tmpdir(), `Heap-${process.pid}-${new Date() .toISOString() .substring(0, 19) .replace(/[^0-9T]+/g, '')}.heapsnapshot` ); let f; try { f = fs.createWriteStream(path); } catch (err) { log.error('Process', 'PID=%s Failed to generate heap snapshot: %s', process.pid, err.stack || err); return; } f.once('error', err => { log.error('Process', 'PID=%s Failed to generate heap snapshot: %s', process.pid, err.stack || err); }); stream.once('error', err => { log.error('Process', 'PID=%s Failed to generate heap snapshot: %s', process.pid, err.stack || err); }); stream.pipe(f); f.once('finish', () => { log.info('Process', 'PID=%s Generated heap snapshot: %s', process.pid, path); }); } });