cluster: ignore queryServer msgs on disconnection
authorSantiago Gimeno <santiago.gimeno@gmail.com>
Tue, 29 Dec 2015 09:21:55 +0000 (10:21 +0100)
committerMyles Borins <mborins@us.ibm.com>
Mon, 15 Feb 2016 19:30:23 +0000 (11:30 -0800)
It avoids the creation of unnecessary handles. This issue is causing
intermitent failures in `test-cluster-disconnect-race` on `FreeBSD`
and `OS X`.

The problem is that the `worker2.disconnect` is being called on the
master before the `queryServer` is handled, causing the worker to
be deleted, then the Server handle is created afterwards. Later on,
when `removeWorker` is called from the `exit` handler, there are no
workers left, but one handle, thus the `AssertionError`.

Add a new `test/sequential/test-cluster-disconnect-leak` based on
`test-cluster-disconnect-race` that creates lots of workers and fails
consistently without this patch.

PR-URL: https://github.com/nodejs/node/pull/4465
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Johan Bergström <bugs@bergstroem.nu>
Reviewed-By: Rich Trott <rtrott@gmail.com>
lib/cluster.js
test/sequential/test-cluster-disconnect-leak.js [new file with mode: 0644]

index 0900b22..82e0e4c 100644 (file)
@@ -445,6 +445,9 @@ function masterInit() {
   }
 
   function queryServer(worker, message) {
+    // Stop processing if worker already disconnecting
+    if (worker.suicide)
+      return;
     var args = [message.address,
                 message.port,
                 message.addressType,
diff --git a/test/sequential/test-cluster-disconnect-leak.js b/test/sequential/test-cluster-disconnect-leak.js
new file mode 100644 (file)
index 0000000..33476dd
--- /dev/null
@@ -0,0 +1,47 @@
+'use strict';
+// Flags: --expose-internals
+
+const common = require('../common');
+const assert = require('assert');
+const net = require('net');
+const cluster = require('cluster');
+const handles = require('internal/cluster').handles;
+const os = require('os');
+
+if (common.isWindows) {
+  console.log('1..0 # Skipped: This test does not apply to Windows.');
+  return;
+}
+
+cluster.schedulingPolicy = cluster.SCHED_NONE;
+
+if (cluster.isMaster) {
+  const cpus = os.cpus().length;
+  const tries = cpus > 8 ? 128 : cpus * 16;
+
+  const worker1 = cluster.fork();
+  worker1.on('message', common.mustCall(() => {
+    worker1.disconnect();
+    for (let i = 0; i < tries; ++ i) {
+      const w = cluster.fork();
+      w.on('online', common.mustCall(w.disconnect));
+    }
+  }));
+
+  cluster.on('exit', common.mustCall((worker, code) => {
+    assert.strictEqual(code, 0, 'worker exited with error');
+  }, tries + 1));
+
+  process.on('exit', () => {
+    assert.deepEqual(Object.keys(cluster.workers), []);
+    assert.strictEqual(Object.keys(handles).length, 0);
+  });
+
+  return;
+}
+
+var server = net.createServer();
+
+server.listen(common.PORT, function() {
+  process.send('listening');
+});