cluster: migrate from worker.suicide

Replace it with worker.exitedAfterDisconnect. Print deprecation
message when getting or setting until it is removed.

PR-URL: https://github.com/nodejs/node/pull/3743
Fixes: https://github.com/nodejs/node/issues/3721
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Myles Borins <myles.borins@gmail.com>
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
This commit is contained in:
Evan Lucas 2015-11-10 13:14:34 -06:00
parent 9bb5a5e2a1
commit 4f619bde4c
6 changed files with 84 additions and 34 deletions

View File

@ -239,7 +239,7 @@ those servers, and then disconnect the IPC channel.
In the master, an internal message is sent to the worker causing it to call In the master, an internal message is sent to the worker causing it to call
`.disconnect()` on itself. `.disconnect()` on itself.
Causes `.suicide` to be set. Causes `.exitedAfterDisconnect` to be set.
Note that after a server is closed, it will no longer accept new connections, Note that after a server is closed, it will no longer accept new connections,
but connections may be accepted by any other listening worker. Existing but connections may be accepted by any other listening worker. Existing
@ -292,6 +292,27 @@ if (cluster.isMaster) {
} }
``` ```
### worker.exitedAfterDisconnect
* {Boolean}
Set by calling `.kill()` or `.disconnect()`. Until then, it is `undefined`.
The boolean `worker.exitedAfterDisconnect` lets you distinguish between voluntary
and accidental exit, the master may choose not to respawn a worker based on
this value.
```js
cluster.on('exit', (worker, code, signal) => {
if (worker.exitedAfterDisconnect === true) {
console.log('Oh, it was just voluntary\' no need to worry').
}
});
// kill worker
worker.kill();
```
### worker.id ### worker.id
* {Number} * {Number}
@ -322,7 +343,7 @@ This function will kill the worker. In the master, it does this by disconnecting
the `worker.process`, and once disconnected, killing with `signal`. In the the `worker.process`, and once disconnected, killing with `signal`. In the
worker, it does it by disconnecting the channel, and then exiting with code `0`. worker, it does it by disconnecting the channel, and then exiting with code `0`.
Causes `.suicide` to be set. Causes `.exitedAfterDisconnect` to be set.
This method is aliased as `worker.destroy()` for backwards compatibility. This method is aliased as `worker.destroy()` for backwards compatibility.
@ -340,8 +361,8 @@ is stored.
See: [Child Process module][] See: [Child Process module][]
Note that workers will call `process.exit(0)` if the `'disconnect'` event occurs Note that workers will call `process.exit(0)` if the `'disconnect'` event occurs
on `process` and `.suicide` is not `true`. This protects against accidental on `process` and `.exitedAfterDisconnect` is not `true`. This protects against
disconnection. accidental disconnection.
### worker.send(message[, sendHandle][, callback]) ### worker.send(message[, sendHandle][, callback])
@ -374,17 +395,20 @@ if (cluster.isMaster) {
### worker.suicide ### worker.suicide
* {Boolean} Stability: 0 - Deprecated: Use [`worker.exitedAfterDisconnect`][] instead.
Set by calling `.kill()` or `.disconnect()`, until then it is `undefined`. An alias to [`worker.exitedAfterDisconnect`][].
The boolean `worker.suicide` lets you distinguish between voluntary and accidental Set by calling `.kill()` or `.disconnect()`. Until then, it is `undefined`.
exit, the master may choose not to respawn a worker based on this value.
The boolean `worker.suicide` lets you distinguish between voluntary
and accidental exit, the master may choose not to respawn a worker based on
this value.
```js ```js
cluster.on('exit', (worker, code, signal) => { cluster.on('exit', (worker, code, signal) => {
if (worker.suicide === true) { if (worker.suicide === true) {
console.log('Oh, it was just suicide\' no need to worry'). console.log('Oh, it was just voluntary\' no need to worry').
} }
}); });
@ -392,6 +416,9 @@ cluster.on('exit', (worker, code, signal) => {
worker.kill(); worker.kill();
``` ```
This API only exists for backwards compatibility and will be removed in the
future.
## Event: 'disconnect' ## Event: 'disconnect'
* `worker` {cluster.Worker} * `worker` {cluster.Worker}
@ -707,6 +734,7 @@ socket.on('data', (id) => {
[`disconnect`]: child_process.html#child_process_child_disconnect [`disconnect`]: child_process.html#child_process_child_disconnect
[`kill`]: process.html#process_process_kill_pid_signal [`kill`]: process.html#process_process_kill_pid_signal
[`server.close()`]: net.html#net_event_close [`server.close()`]: net.html#net_event_close
[`worker.exitedAfterDisconnect`]: #cluster_worker_exitedafterdisconnect
[Child Process module]: child_process.html#child_process_child_process_fork_modulepath_args_options [Child Process module]: child_process.html#child_process_child_process_fork_modulepath_args_options
[child_process event: 'exit']: child_process.html#child_process_event_exit [child_process event: 'exit']: child_process.html#child_process_event_exit
[child_process event: 'message']: child_process.html#child_process_event_message [child_process event: 'message']: child_process.html#child_process_event_message

View File

@ -27,7 +27,20 @@ function Worker(options) {
if (options === null || typeof options !== 'object') if (options === null || typeof options !== 'object')
options = {}; options = {};
this.suicide = undefined; this.exitedAfterDisconnect = undefined;
Object.defineProperty(this, 'suicide', {
get: function() {
// TODO: Print deprecation message.
return this.exitedAfterDisconnect;
},
set: function(val) {
// TODO: Print deprecation message.
this.exitedAfterDisconnect = val;
},
enumerable: true
});
this.state = options.state || 'none'; this.state = options.state || 'none';
this.id = options.id | 0; this.id = options.id | 0;
@ -355,7 +368,7 @@ function masterInit() {
removeWorker(worker); removeWorker(worker);
} }
worker.suicide = !!worker.suicide; worker.exitedAfterDisconnect = !!worker.exitedAfterDisconnect;
worker.state = 'dead'; worker.state = 'dead';
worker.emit('exit', exitCode, signalCode); worker.emit('exit', exitCode, signalCode);
cluster.emit('exit', worker, exitCode, signalCode); cluster.emit('exit', worker, exitCode, signalCode);
@ -376,7 +389,7 @@ function masterInit() {
*/ */
if (worker.isDead()) removeWorker(worker); if (worker.isDead()) removeWorker(worker);
worker.suicide = !!worker.suicide; worker.exitedAfterDisconnect = !!worker.exitedAfterDisconnect;
worker.state = 'disconnected'; worker.state = 'disconnected';
worker.emit('disconnect'); worker.emit('disconnect');
cluster.emit('disconnect', worker); cluster.emit('disconnect', worker);
@ -407,7 +420,7 @@ function masterInit() {
}; };
Worker.prototype.disconnect = function() { Worker.prototype.disconnect = function() {
this.suicide = true; this.exitedAfterDisconnect = true;
send(this, { act: 'disconnect' }); send(this, { act: 'disconnect' });
removeHandlesForWorker(this); removeHandlesForWorker(this);
removeWorker(this); removeWorker(this);
@ -432,8 +445,8 @@ function masterInit() {
queryServer(worker, message); queryServer(worker, message);
else if (message.act === 'listening') else if (message.act === 'listening')
listening(worker, message); listening(worker, message);
else if (message.act === 'suicide') else if (message.act === 'exitedAfterDisconnect')
suicide(worker, message); exitedAfterDisconnect(worker, message);
else if (message.act === 'close') else if (message.act === 'close')
close(worker, message); close(worker, message);
} }
@ -444,14 +457,14 @@ function masterInit() {
cluster.emit('online', worker); cluster.emit('online', worker);
} }
function suicide(worker, message) { function exitedAfterDisconnect(worker, message) {
worker.suicide = true; worker.exitedAfterDisconnect = true;
send(worker, { ack: message.seq }); send(worker, { ack: message.seq });
} }
function queryServer(worker, message) { function queryServer(worker, message) {
// Stop processing if worker already disconnecting // Stop processing if worker already disconnecting
if (worker.suicide) if (worker.exitedAfterDisconnect)
return; return;
var args = [message.address, var args = [message.address,
message.port, message.port,
@ -533,7 +546,7 @@ function workerInit() {
cluster.worker = worker; cluster.worker = worker;
process.once('disconnect', function() { process.once('disconnect', function() {
worker.emit('disconnect'); worker.emit('disconnect');
if (!worker.suicide) { if (!worker.exitedAfterDisconnect) {
// Unexpected disconnect, master exited, or some such nastiness, so // Unexpected disconnect, master exited, or some such nastiness, so
// worker exits immediately. // worker exits immediately.
process.exit(0); process.exit(0);
@ -670,10 +683,10 @@ function workerInit() {
}; };
Worker.prototype.destroy = function() { Worker.prototype.destroy = function() {
this.suicide = true; this.exitedAfterDisconnect = true;
if (!this.isConnected()) process.exit(0); if (!this.isConnected()) process.exit(0);
var exit = process.exit.bind(null, 0); var exit = process.exit.bind(null, 0);
send({ act: 'suicide' }, () => process.disconnect()); send({ act: 'exitedAfterDisconnect' }, () => process.disconnect());
process.once('disconnect', exit); process.once('disconnect', exit);
}; };
@ -682,19 +695,20 @@ function workerInit() {
} }
function _disconnect(masterInitiated) { function _disconnect(masterInitiated) {
this.suicide = true; this.exitedAfterDisconnect = true;
let waitingCount = 1; let waitingCount = 1;
function checkWaitingCount() { function checkWaitingCount() {
waitingCount--; waitingCount--;
if (waitingCount === 0) { if (waitingCount === 0) {
// If disconnect is worker initiated, wait for ack to be sure suicide // If disconnect is worker initiated, wait for ack to be sure
// is properly set in the master, otherwise, if it's master initiated // exitedAfterDisconnect is properly set in the master, otherwise, if
// there's no need to send the suicide message // it's master initiated there's no need to send the
// exitedAfterDisconnect message
if (masterInitiated) { if (masterInitiated) {
process.disconnect(); process.disconnect();
} else { } else {
send({ act: 'suicide' }, () => process.disconnect()); send({ act: 'exitedAfterDisconnect' }, () => process.disconnect());
} }
} }
} }

View File

@ -8,8 +8,12 @@ if (cluster.isWorker) {
http.Server(function() { http.Server(function() {
}).listen(common.PORT, '127.0.0.1'); }).listen(common.PORT, '127.0.0.1');
const worker = cluster.worker;
assert.strictEqual(worker.exitedAfterDisconnect, worker.suicide);
cluster.worker.on('disconnect', function() { cluster.worker.on('disconnect', function() {
assert.strictEqual(cluster.worker.exitedAfterDisconnect,
cluster.worker.suicide);
process.exit(42); process.exit(42);
}); });
@ -26,7 +30,7 @@ if (cluster.isWorker) {
emitDisconnectInsideWorker: false, emitDisconnectInsideWorker: false,
emitExit: false, emitExit: false,
state: false, state: false,
suicideMode: false, voluntaryMode: false,
died: false died: false
} }
}; };
@ -60,7 +64,7 @@ if (cluster.isWorker) {
// Check worker events and properties // Check worker events and properties
worker.once('disconnect', function() { worker.once('disconnect', function() {
checks.worker.emitDisconnect = true; checks.worker.emitDisconnect = true;
checks.worker.suicideMode = worker.suicide; checks.worker.voluntaryMode = worker.exitedAfterDisconnect;
checks.worker.state = worker.state; checks.worker.state = worker.state;
}); });
@ -86,7 +90,7 @@ if (cluster.isWorker) {
// flags // flags
assert.equal(w.state, 'disconnected', 'The state property was not set'); assert.equal(w.state, 'disconnected', 'The state property was not set');
assert.equal(w.suicideMode, true, 'Suicide mode was not set'); assert.equal(w.voluntaryMode, true, 'Voluntary exit mode was not set');
// is process alive // is process alive
assert.ok(w.died, 'The worker did not die'); assert.ok(w.died, 'The worker did not die');

View File

@ -3,7 +3,7 @@
// verifies that, when a child process exits (by calling `process.exit(code)`) // verifies that, when a child process exits (by calling `process.exit(code)`)
// - the parent receives the proper events in the proper order, no duplicates // - the parent receives the proper events in the proper order, no duplicates
// - the exitCode and signalCode are correct in the 'exit' event // - the exitCode and signalCode are correct in the 'exit' event
// - the worker.suicide flag, and worker.state are correct // - the worker.exitedAfterDisconnect flag, and worker.state are correct
// - the worker process actually goes away // - the worker process actually goes away
var common = require('../common'); var common = require('../common');
@ -32,6 +32,8 @@ if (cluster.isWorker) {
worker_emitExit: [1, "the worker did not emit 'exit'"], worker_emitExit: [1, "the worker did not emit 'exit'"],
worker_state: ['disconnected', 'the worker state is incorrect'], worker_state: ['disconnected', 'the worker state is incorrect'],
worker_suicideMode: [false, 'the worker.suicide flag is incorrect'], worker_suicideMode: [false, 'the worker.suicide flag is incorrect'],
worker_exitedAfterDisconnect: [false,
'the .exitedAfterDisconnect flag is incorrect'],
worker_died: [true, 'the worker is still running'], worker_died: [true, 'the worker is still running'],
worker_exitCode: [EXIT_CODE, 'the worker exited w/ incorrect exitCode'], worker_exitCode: [EXIT_CODE, 'the worker exited w/ incorrect exitCode'],
worker_signalCode: [null, 'the worker exited w/ incorrect signalCode'] worker_signalCode: [null, 'the worker exited w/ incorrect signalCode']
@ -66,6 +68,7 @@ if (cluster.isWorker) {
worker.on('disconnect', function() { worker.on('disconnect', function() {
results.worker_emitDisconnect += 1; results.worker_emitDisconnect += 1;
results.worker_suicideMode = worker.suicide; results.worker_suicideMode = worker.suicide;
results.worker_exitedAfterDisconnect = worker.exitedAfterDisconnect;
results.worker_state = worker.state; results.worker_state = worker.state;
if (results.worker_emitExit > 0) { if (results.worker_emitExit > 0) {
process.nextTick(function() { finish_test(); }); process.nextTick(function() { finish_test(); });

View File

@ -6,7 +6,7 @@ var cluster = require('cluster');
var SENTINEL = 42; var SENTINEL = 42;
// workers forcibly exit when control channel is disconnected, if // workers forcibly exit when control channel is disconnected, if
// their .suicide flag isn't set // their .exitedAfterDisconnect flag isn't set
// //
// test this by: // test this by:
// //

View File

@ -3,7 +3,7 @@
// verifies that, when a child process is killed (we use SIGKILL) // verifies that, when a child process is killed (we use SIGKILL)
// - the parent receives the proper events in the proper order, no duplicates // - the parent receives the proper events in the proper order, no duplicates
// - the exitCode and signalCode are correct in the 'exit' event // - the exitCode and signalCode are correct in the 'exit' event
// - the worker.suicide flag, and worker.state are correct // - the worker.exitedAfterDisconnect flag, and worker.state are correct
// - the worker process actually goes away // - the worker process actually goes away
var common = require('../common'); var common = require('../common');
@ -29,7 +29,8 @@ if (cluster.isWorker) {
worker_emitDisconnect: [1, "the worker did not emit 'disconnect'"], worker_emitDisconnect: [1, "the worker did not emit 'disconnect'"],
worker_emitExit: [1, "the worker did not emit 'exit'"], worker_emitExit: [1, "the worker did not emit 'exit'"],
worker_state: ['disconnected', 'the worker state is incorrect'], worker_state: ['disconnected', 'the worker state is incorrect'],
worker_suicideMode: [false, 'the worker.suicide flag is incorrect'], worker_exitedAfter: [false,
'the .exitedAfterDisconnect flag is incorrect'],
worker_died: [true, 'the worker is still running'], worker_died: [true, 'the worker is still running'],
worker_exitCode: [null, 'the worker exited w/ incorrect exitCode'], worker_exitCode: [null, 'the worker exited w/ incorrect exitCode'],
worker_signalCode: [KILL_SIGNAL, worker_signalCode: [KILL_SIGNAL,
@ -65,7 +66,7 @@ if (cluster.isWorker) {
// Check worker events and properties // Check worker events and properties
worker.on('disconnect', function() { worker.on('disconnect', function() {
results.worker_emitDisconnect += 1; results.worker_emitDisconnect += 1;
results.worker_suicideMode = worker.suicide; results.worker_exitedAfter = worker.exitedAfterDisconnect;
results.worker_state = worker.state; results.worker_state = worker.state;
}); });