Source

index.js

'use strict'
// @ts-check

const _ = require('lodash')
const process = require('process')
const cluster = require('cluster')
const Logger = require('@fizz.js/node-logger')
const Common = require('@fizz.js/node-common')
const ErrorReporter = require('@fizz.js/node-error-reporter')
const { ServerError } = require('@fizz.js/node-errors')

/**
 *
 *
 * @class ClusterHandler
 */
class ClusterHandler {
  /**
   *
   * @returns {cluster}
   * @memberof ClusterHandler
   */
  registerHandler() {
    return cluster
      .on('message', this.onMessage.bind(this))
      .on('online', this.onOnline.bind(this))
      .on('disconnect', this.onDisconnect.bind(this))
      .on('exit', this.onExit.bind(this))
      .on('listening', this.onListening.bind(this))
      .on('setup', this.onSetup.bind(this))
      .on('death', this.onDeath.bind(this))
  }

  /**
   *
   * @param {cluster.Worker} worker
   * @param {any} message
   * @param {net.Socket|net.Server} [handle=undefined]
   * @returns {void}
   * @memberof ClusterHandler
   */
  onMessage(worker, message, handle = undefined) {
    try {
      if (!_.has(process.env, 'NODE_APP_INSTANCE')) {
        return cluster.isMaster
          ? this.onMasterMessage(worker, message, handle)
          : this.onWorkerMessage(worker, message, handle)
      }
    } catch (error) {
      Logger.error(error)
      throw error
    }
  }

  /**
   *
   *
   * @param {cluster.Worker} worker
   * @param {any} message
   * @param {net.Socket|net.Server} [handle=undefined]
   * @returns {void}
   * @memberof ClusterHandler
   */
  onWorkerMessage(worker, message, handle = undefined) {
    try {
      Logger.info(`WORKER ON MESSAGE: ${Common.toJSON(message)} PID: ${_.get(worker, 'id')}`)
    } catch (error) {
      Logger.error(error)
      throw error
    }
  }

  /**
   *
   *
   * @param {cluster.Worker} worker
   * @param {any} message
   * @param {net.Socket|net.Server} [handle=undefined]
   * @returns {void}
   * @memberof ClusterHandler
   */
  onMasterMessage(worker, message, handle = undefined) {
    try {
      Logger.info(`MASTER ON MESSAGE: ${Common.toJSON(message)} PID: ${_.get(worker, 'id')}`)
    } catch (error) {
      Logger.error(error)
      throw error
    }
  }

  /**
   *
   *
   * @param {cluster.Worker} worker
   * @returns {void}
   * @memberof ClusterHandler
   */
  onOnline(worker) {
    try {
      Logger.info(`WORKER IS NOW ONLINE ${_.get(worker, 'id')} ${_.get(worker, 'process.pid')}`)
    } catch (error) {
      Logger.error(error)
      throw error
    }
  }

  /**
   *
   * @param {cluster.Worker} worker
   * @param {cluster.Address} address
   * @returns {void}
   * @memberof ClusterHandler
   */
  async onListening(worker, address) {
    try {
      Logger.debug(
        `WORKER ON LISTENING [ID: ${_.get(worker, 'id')}] [ADDRESS: ${_.get(address, 'address')}] [PORT: ${_.get(
          address,
          'port'
        )}] [TYPE: ${_.get(address, 'addressType')}]`
      )
    } catch (error) {
      Logger.error(error)
    }
  }

  /**
   *
   *
   * @param {cluster.ClusterSettings} settings
   * @returns {void}
   * @memberof ClusterHandler
   */
  async onSetup(settings) {
    try {
      Logger.debug(
        `WORKER ON SETTING UP ARGS: [${_.get(settings, 'args')}] [EXEC: ${_.get(settings, 'exec')}] [ARGV: ${_.get(
          settings,
          'execArgv'
        )}] [SILENT: ${_.get(settings, 'silent')}]`
      )
    } catch (error) {
      Logger.error(error)
    }
  }

  /**
   *
   *
   * @param {cluster.Worker} worker
   * @param {number} code
   * @param {string} sig
   * @returns {void}
   * @memberof ClusterHandler
   */
  async onExit(worker, code, sig) {
    try {
      await this.onError(`WORKER IS EXITED pid: ${_.get(worker, 'id')} code: ${code} sig: ${sig}`)
    } catch (error) {
      Logger.error(error)
    }
  }

  /**
   *
   *
   * @param {cluster.Worker} worker
   * @returns {void}
   * @memberof ClusterHandler
   */
  async onDisconnect(worker) {
    try {
      await this.onError(`WORKER IS DISCONNECTED PID: ${_.get(worker, 'id')}`)
    } catch (error) {
      Logger.error(error)
    }
  }

  /**
   *
   * @deprecated
   * @param {cluster.Worker} worker
   * @returns {void}
   * @memberof ClusterHandler
   */
  async onDeath(worker) {
    try {
      await this.onError(`WORKER ON DEATH PID: ${_.get(worker, 'id')}`, _.get(process, 'env.NODE_ENV') === 'production')
    } catch (error) {
      Logger.error(error)
    }
  }

  /**
   *
   *
   * @param {cluster.Worker} worker
   * @param {boolean} [recovery=false]
   * @returns {void}
   * @memberof ClusterHandler
   */
  async onError(message, recovery = false) {
    try {
      const error = new ServerError(message)
      await ErrorReporter.handleError(error, 'warn', false).catch(e => Logger.noop())
    } catch (error) {
      Logger.error(error)
    } finally {
      if (recovery) {
        this.recover()
      }
    }
  }

  /**
   *
   *
   * @returns {cluster.Worker}
   * @memberof ClusterHandler
   */
  recover() {
    return cluster.fork()
  }
}

module.exports = new ClusterHandler()