Browse Source

cluster: reset handle index on close

It allows reopening a server after it has been closed.

Fixes: https://github.com/nodejs/node/issues/6693
PR-URL: https://github.com/nodejs/node/pull/6981
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Ron Korving <ron@ronkorving.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
v6.x
Santiago Gimeno 9 years ago
committed by Rod Vagg
parent
commit
0bd8f4c4d8
  1. 26
      lib/cluster.js
  2. 37
      test/parallel/test-cluster-server-restart-none.js
  3. 37
      test/parallel/test-cluster-server-restart-rr.js

26
lib/cluster.js

@ -564,18 +564,18 @@ function workerInit() {
// obj is a net#Server or a dgram#Socket object. // obj is a net#Server or a dgram#Socket object.
cluster._getServer = function(obj, options, cb) { cluster._getServer = function(obj, options, cb) {
const key = [ options.address, const indexesKey = [ options.address,
options.port, options.port,
options.addressType, options.addressType,
options.fd ].join(':'); options.fd ].join(':');
if (indexes[key] === undefined) if (indexes[indexesKey] === undefined)
indexes[key] = 0; indexes[indexesKey] = 0;
else else
indexes[key]++; indexes[indexesKey]++;
const message = util._extend({ const message = util._extend({
act: 'queryServer', act: 'queryServer',
index: indexes[key], index: indexes[indexesKey],
data: null data: null
}, options); }, options);
@ -585,9 +585,9 @@ function workerInit() {
if (obj._setServerData) obj._setServerData(reply.data); if (obj._setServerData) obj._setServerData(reply.data);
if (handle) if (handle)
shared(reply, handle, cb); // Shared listen socket. shared(reply, handle, indexesKey, cb); // Shared listen socket.
else else
rr(reply, cb); // Round-robin. rr(reply, indexesKey, cb); // Round-robin.
}); });
obj.once('listening', function() { obj.once('listening', function() {
cluster.worker.state = 'listening'; cluster.worker.state = 'listening';
@ -599,7 +599,7 @@ function workerInit() {
}; };
// Shared listen socket. // Shared listen socket.
function shared(message, handle, cb) { function shared(message, handle, indexesKey, cb) {
var key = message.key; var key = message.key;
// Monkey-patch the close() method so we can keep track of when it's // Monkey-patch the close() method so we can keep track of when it's
// closed. Avoids resource leaks when the handle is short-lived. // closed. Avoids resource leaks when the handle is short-lived.
@ -607,6 +607,7 @@ function workerInit() {
handle.close = function() { handle.close = function() {
send({ act: 'close', key: key }); send({ act: 'close', key: key });
delete handles[key]; delete handles[key];
delete indexes[indexesKey];
return close.apply(this, arguments); return close.apply(this, arguments);
}; };
assert(handles[key] === undefined); assert(handles[key] === undefined);
@ -615,7 +616,7 @@ function workerInit() {
} }
// Round-robin. Master distributes handles across workers. // Round-robin. Master distributes handles across workers.
function rr(message, cb) { function rr(message, indexesKey, cb) {
if (message.errno) if (message.errno)
return cb(message.errno, null); return cb(message.errno, null);
@ -636,6 +637,7 @@ function workerInit() {
if (key === undefined) return; if (key === undefined) return;
send({ act: 'close', key: key }); send({ act: 'close', key: key });
delete handles[key]; delete handles[key];
delete indexes[indexesKey];
key = undefined; key = undefined;
} }

37
test/parallel/test-cluster-server-restart-none.js

@ -0,0 +1,37 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const cluster = require('cluster');
cluster.schedulingPolicy = cluster.SCHED_NONE;
if (cluster.isMaster) {
const worker1 = cluster.fork();
worker1.on('listening', common.mustCall(() => {
const worker2 = cluster.fork();
worker2.on('exit', (code, signal) => {
assert.strictEqual(code, 0, 'worker2 did not exit normally');
assert.strictEqual(signal, null, 'worker2 did not exit normally');
worker1.disconnect();
});
}));
worker1.on('exit', common.mustCall((code, signal) => {
assert.strictEqual(code, 0, 'worker1 did not exit normally');
assert.strictEqual(signal, null, 'worker1 did not exit normally');
}));
} else {
const net = require('net');
const server = net.createServer();
server.listen(common.PORT, common.mustCall(() => {
if (cluster.worker.id === 2) {
server.close(() => {
server.listen(common.PORT, common.mustCall(() => {
server.close(() => {
process.disconnect();
});
}));
});
}
}));
}

37
test/parallel/test-cluster-server-restart-rr.js

@ -0,0 +1,37 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const cluster = require('cluster');
cluster.schedulingPolicy = cluster.SCHED_RR;
if (cluster.isMaster) {
const worker1 = cluster.fork();
worker1.on('listening', common.mustCall(() => {
const worker2 = cluster.fork();
worker2.on('exit', (code, signal) => {
assert.strictEqual(code, 0, 'worker2 did not exit normally');
assert.strictEqual(signal, null, 'worker2 did not exit normally');
worker1.disconnect();
});
}));
worker1.on('exit', common.mustCall((code, signal) => {
assert.strictEqual(code, 0, 'worker1 did not exit normally');
assert.strictEqual(signal, null, 'worker1 did not exit normally');
}));
} else {
const net = require('net');
const server = net.createServer();
server.listen(common.PORT, common.mustCall(() => {
if (cluster.worker.id === 2) {
server.close(() => {
server.listen(common.PORT, common.mustCall(() => {
server.close(() => {
process.disconnect();
});
}));
});
}
}));
}
Loading…
Cancel
Save