From bce38ac4e0acf0b2339dae03510d8641a00a8193 Mon Sep 17 00:00:00 2001 From: Jeff Barczewski Date: Tue, 15 Jan 2013 13:36:20 -0600 Subject: [PATCH 1/4] emit events on resize, restart, quit, quitHard Return emitter from clusterMaster constructor. Also provide getter clusterMaster.emitter() which returns the same emitter. Emit events when clusterMaster methods (resize, restart, quit, or quitHard) are invoked. The event is emitted to allow additional cleanup in your code on the tick prior to the method being invoked. ```javascript // listen to events to additional cleanup or shutdown clusterMaster.emitter() .on('resize', function (clusterSize) { }) .on('restart', function () { }) .on('quit', function () { }) .on('quitHard', function () { }); ``` --- README.md | 29 ++++++++++++++++++++++++++- cluster-master.js | 50 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 68 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 5a3fb6a..a19d7d7 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,13 @@ clusterMaster.quit() // not so graceful shutdown clusterMaster.quitHard() + +// listen to events to additional cleanup or shutdown +clusterMaster.emitter() + .on('resize', function (clusterSize) { }) + .on('restart', function () { }) + .on('quit', function () { }) + .on('quitHard', function () { }); ``` ## Methods @@ -57,19 +64,29 @@ clusterMaster.quitHard() Set the cluster size to `n`. This will disconnect extra nodes and/or spin up new nodes, as needed. Done by default on restarts. +Fires `resize` event with new clusterSize just before performing the +resize. ### clusterMaster.restart(cb) One by one, shut down nodes and spin up new ones. Callback is called -when finished. +when finished. Fires `restart` event just before performing restart. ### clusterMaster.quit() Gracefully shut down the worker nodes and then process.exit(0). +Fires `quit` event just before performing the shutdown. ### clusterMaster.quitHard() Forcibly shut down the worker nodes and then process.exit(1). +Fires `quitHard` event just before performing hard shut down. + +### clusterMaster.emitter() + +Retrieve the clusterMaster EventEmitter to be able to listen +to clusterMaster events. This emitter is also returned from +the original clusterMaster() constructor. ## Configs @@ -89,3 +106,13 @@ The `exec`, `env`, `argv`, and `silent` configs are passed to the the parent. Called in the context of the worker, so you can reply by looking at `this`. +## Events + +clusterMaster emits events on clusterMaster.emitter() when its methods +are called which allows you to respond and do additional cleanup right +before the action is carried out. + +* `resize` - fired on clusterMaster.resize(n), listener ex: fn(clusterSize) +* `restart` - fired on clusterMaster.restart() +* `quit` - fired on clusterMaster.quit() +* `quitHard` - fired on clusterMaster.quitHard() diff --git a/cluster-master.js b/cluster-master.js index 555d18c..1bde9d2 100644 --- a/cluster-master.js +++ b/cluster-master.js @@ -10,14 +10,17 @@ var cluster = require("cluster") , onmessage , repl = require('repl') , net = require('net') +, EventEmitter = require('events').EventEmitter +, masterEmitter = new EventEmitter() , fs = require('fs') , util = require('util') exports = module.exports = clusterMaster -exports.restart = restart -exports.resize = resize -exports.quitHard = quitHard -exports.quit = quit +exports.emitter = emitter +exports.restart = emitAndRestart +exports.resize = emitAndResize +exports.quitHard = emitAndQuitHard +exports.quit = emitAndQuit var debugStreams = {} function debug () { @@ -77,6 +80,8 @@ function clusterMaster (config) { // now make it the right size debug('resize and then setup repl') resize(setupRepl) + + return masterEmitter } function select (field) { @@ -126,9 +131,10 @@ function setupRepl () { }) var context = { repl: r, - resize: resize, - restart: restart, - quit: quit, + resize: emitAndResize, + restart: emitAndRestart, + quit: emitAndQuit, + quitHard: emitAndQuitHard, cluster: cluster, get size () { return clusterSize @@ -412,9 +418,9 @@ function quit () { function setupSignals () { try { - process.on("SIGHUP", restart) - process.on("SIGINT", quit) - process.on("SIGKILL", quitHard) + process.on("SIGHUP", emitAndRestart) + process.on("SIGINT", emitAndQuit) + process.on("SIGKILL", emitAndQuitHard) } catch (e) { // Must be on Windows, waaa-waaah. } @@ -423,3 +429,27 @@ function setupSignals () { if (!quitting) quitHard() }) } + +function emitter() { + return masterEmitter +} + +function emitAndResize(n) { + masterEmitter.emit('resize', n) + process.nextTick(function () { resize(n) }) +} + +function emitAndRestart(cb) { + masterEmitter.emit('restart') + process.nextTick(function () { restart(cb) }); +} + +function emitAndQuit() { + masterEmitter.emit('quit') + process.nextTick(function () { quit() }) +} + +function emitAndQuitHard() { + masterEmitter.emit('quitHard') + process.nextTick(function () { quitHard() }) +} \ No newline at end of file From e4d00630513854112e9e1cd20c4f9e08a43dd197 Mon Sep 17 00:00:00 2001 From: Jeff Barczewski Date: Fri, 18 Jan 2013 15:08:38 -0600 Subject: [PATCH 2/4] restart event have workers param and add restartComplete --- README.md | 3 ++- cluster-master.js | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a19d7d7..e6d3992 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,7 @@ are called which allows you to respond and do additional cleanup right before the action is carried out. * `resize` - fired on clusterMaster.resize(n), listener ex: fn(clusterSize) -* `restart` - fired on clusterMaster.restart() +* `restart` - fired on clusterMaster.restart(), listener ex: fn(oldWorkers) + `restartComplete` - fired when restart is completed * `quit` - fired on clusterMaster.quit() * `quitHard` - fired on clusterMaster.quitHard() diff --git a/cluster-master.js b/cluster-master.js index 1bde9d2..15bceb4 100644 --- a/cluster-master.js +++ b/cluster-master.js @@ -440,8 +440,21 @@ function emitAndResize(n) { } function emitAndRestart(cb) { - masterEmitter.emit('restart') - process.nextTick(function () { restart(cb) }); + if (restarting) { + debug("Already restarting. Cannot restart yet.") + return + } + var currentWorkers = Object.keys(cluster.workers).reduce(function (accum, k) { + accum[k] = { pid: cluster.workers[k].pid }; + return accum; + }, {}); + masterEmitter.emit('restart', currentWorkers); + process.nextTick(function () { + restart(function () { + masterEmitter.emit('restartComplete'); + if (cb) cb(); + }); + }); } function emitAndQuit() { From f3ce212dae8a181f2065b4153ee30e5e9f49df74 Mon Sep 17 00:00:00 2001 From: Jeff Barczewski Date: Fri, 18 Jan 2013 16:42:33 -0600 Subject: [PATCH 3/4] if already restarting still call cb --- cluster-master.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cluster-master.js b/cluster-master.js index 15bceb4..648f848 100644 --- a/cluster-master.js +++ b/cluster-master.js @@ -260,6 +260,7 @@ function forkListener () { function restart (cb) { if (restarting) { debug("Already restarting. Cannot restart yet.") + if (cb) cb(new Error('already restarting, cannot restart yet')) return } @@ -442,6 +443,7 @@ function emitAndResize(n) { function emitAndRestart(cb) { if (restarting) { debug("Already restarting. Cannot restart yet.") + if (cb) cb(new Error('already restarting, cannot restart yet')) return } var currentWorkers = Object.keys(cluster.workers).reduce(function (accum, k) { From 9f67b3feceef792072690a39213baa146c36041b Mon Sep 17 00:00:00 2001 From: Jeff Barczewski Date: Fri, 18 Jan 2013 16:46:19 -0600 Subject: [PATCH 4/4] revert previous commit should not call cb in case of error the restart cb is only called on success --- cluster-master.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/cluster-master.js b/cluster-master.js index 648f848..15bceb4 100644 --- a/cluster-master.js +++ b/cluster-master.js @@ -260,7 +260,6 @@ function forkListener () { function restart (cb) { if (restarting) { debug("Already restarting. Cannot restart yet.") - if (cb) cb(new Error('already restarting, cannot restart yet')) return } @@ -443,7 +442,6 @@ function emitAndResize(n) { function emitAndRestart(cb) { if (restarting) { debug("Already restarting. Cannot restart yet.") - if (cb) cb(new Error('already restarting, cannot restart yet')) return } var currentWorkers = Object.keys(cluster.workers).reduce(function (accum, k) {