Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
cluster: reset handle index on close
It allows reopening a server after it has been closed.

Fixes: #6693
PR-URL: #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>
  • Loading branch information
santigimeno committed May 27, 2016
commit 0c294362502684b9273e7e7c7039ec7028471014
26 changes: 14 additions & 12 deletions lib/cluster.js
Original file line number Diff line number Diff line change
Expand Up @@ -564,18 +564,18 @@ function workerInit() {

// obj is a net#Server or a dgram#Socket object.
cluster._getServer = function(obj, options, cb) {
const key = [ options.address,
options.port,
options.addressType,
options.fd ].join(':');
if (indexes[key] === undefined)
indexes[key] = 0;
const indexesKey = [ options.address,
options.port,
options.addressType,
options.fd ].join(':');
if (indexes[indexesKey] === undefined)
indexes[indexesKey] = 0;
else
indexes[key]++;
indexes[indexesKey]++;

const message = util._extend({
act: 'queryServer',
index: indexes[key],
index: indexes[indexesKey],
data: null
}, options);

Expand All @@ -585,9 +585,9 @@ function workerInit() {
if (obj._setServerData) obj._setServerData(reply.data);

if (handle)
shared(reply, handle, cb); // Shared listen socket.
shared(reply, handle, indexesKey, cb); // Shared listen socket.
else
rr(reply, cb); // Round-robin.
rr(reply, indexesKey, cb); // Round-robin.
});
obj.once('listening', function() {
cluster.worker.state = 'listening';
Expand All @@ -599,14 +599,15 @@ function workerInit() {
};

// Shared listen socket.
function shared(message, handle, cb) {
function shared(message, handle, indexesKey, cb) {
var key = message.key;
// Monkey-patch the close() method so we can keep track of when it's
// closed. Avoids resource leaks when the handle is short-lived.
var close = handle.close;
handle.close = function() {
send({ act: 'close', key: key });
delete handles[key];
delete indexes[indexesKey];
return close.apply(this, arguments);
};
assert(handles[key] === undefined);
Expand All @@ -615,7 +616,7 @@ function workerInit() {
}

// Round-robin. Master distributes handles across workers.
function rr(message, cb) {
function rr(message, indexesKey, cb) {
if (message.errno)
return cb(message.errno, null);

Expand All @@ -636,6 +637,7 @@ function workerInit() {
if (key === undefined) return;
send({ act: 'close', key: key });
delete handles[key];
delete indexes[indexesKey];
key = undefined;
}

Expand Down
37 changes: 37 additions & 0 deletions test/parallel/test-cluster-server-restart-none.js
Original file line number Diff line number Diff line change
@@ -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 changes: 37 additions & 0 deletions test/parallel/test-cluster-server-restart-rr.js
Original file line number Diff line number Diff line change
@@ -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();
});
}));
});
}
}));
}