Skip to content

Commit 0ee4df9

Browse files
sam-githubrvagg
authored andcommitted
test: make listen-fd-cluster/server more robust
- eliminate unnecessary intermediate process ("parent") - children exit if runner dies unexpectedly (killed on a test timeout, for example) - use explicit messaging from children to parents to indicate when worker is ready to accept http requests, rather than racing to see whether runner will make request before worker is listening PR-URL: #1944 Reviewed-By: Johan Bergstrom <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
1 parent 1738c96 commit 0ee4df9

File tree

2 files changed

+88
-79
lines changed

2 files changed

+88
-79
lines changed

test/parallel/test-listen-fd-cluster.js

+50-35
Original file line numberDiff line numberDiff line change
@@ -7,63 +7,62 @@ var PORT = common.PORT;
77
var spawn = require('child_process').spawn;
88
var cluster = require('cluster');
99

10-
console.error('Cluster listen fd test', process.argv.slice(2));
10+
console.error('Cluster listen fd test', process.argv[2] || 'runner');
1111

1212
if (common.isWindows) {
1313
console.log('1..0 # Skipped: This test is disabled on windows.');
1414
return;
1515
}
1616

17+
// Process relationship is:
18+
//
19+
// parent: the test main script
20+
// -> master: the cluster master
21+
// -> worker: the cluster worker
1722
switch (process.argv[2]) {
1823
case 'master': return master();
1924
case 'worker': return worker();
2025
case 'parent': return parent();
21-
default: return test();
2226
}
2327

28+
var ok;
29+
30+
process.on('exit', function() {
31+
assert.ok(ok);
32+
});
33+
2434
// spawn the parent, and listen for it to tell us the pid of the cluster.
2535
// WARNING: This is an example of listening on some arbitrary FD number
2636
// that has already been bound elsewhere in advance. However, binding
2737
// server handles to stdio fd's is NOT a good or reliable way to do
2838
// concurrency in HTTP servers! Use the cluster module, or if you want
2939
// a more low-level approach, use child process IPC manually.
30-
function test() {
31-
var parent = spawn(process.execPath, [__filename, 'parent'], {
32-
stdio: [ 0, 'pipe', 2 ]
33-
});
34-
var json = '';
35-
parent.stdout.on('data', function(c) {
36-
json += c.toString();
37-
if (json.indexOf('\n') !== -1) next();
38-
});
39-
function next() {
40-
console.error('output from parent = %s', json);
41-
var cluster = JSON.parse(json);
42-
// now make sure that we can request to the worker, then kill it.
43-
http.get({
44-
server: 'localhost',
45-
port: PORT,
46-
path: '/',
47-
}).on('response', function(res) {
48-
var s = '';
49-
res.on('data', function(c) {
50-
s += c.toString();
51-
});
52-
res.on('end', function() {
53-
// kill the worker before we start doing asserts.
54-
// it's really annoying when tests leave orphans!
55-
parent.kill();
56-
process.kill(cluster.master, 'SIGKILL');
57-
40+
test(function(parent) {
41+
// now make sure that we can request to the worker, then kill it.
42+
http.get({
43+
server: 'localhost',
44+
port: PORT,
45+
path: '/',
46+
}).on('response', function(res) {
47+
var s = '';
48+
res.on('data', function(c) {
49+
s += c.toString();
50+
});
51+
res.on('end', function() {
52+
// kill the worker before we start doing asserts.
53+
// it's really annoying when tests leave orphans!
54+
parent.kill();
55+
parent.on('exit', function() {
5856
assert.equal(s, 'hello from worker\n');
5957
assert.equal(res.statusCode, 200);
6058
console.log('ok');
59+
ok = true;
6160
});
6261
});
63-
}
64-
}
62+
});
63+
});
6564

66-
function parent() {
65+
function test(cb) {
6766
console.error('about to listen in parent');
6867
var server = net.createServer(function(conn) {
6968
console.error('connection on parent');
@@ -73,7 +72,7 @@ function parent() {
7372

7473
var spawn = require('child_process').spawn;
7574
var master = spawn(process.execPath, [__filename, 'master'], {
76-
stdio: [ 0, 1, 2, server._handle ],
75+
stdio: [ 0, 'pipe', 2, server._handle, 'ipc' ],
7776
detached: true
7877
});
7978

@@ -90,6 +89,11 @@ function parent() {
9089
console.error('master closed');
9190
});
9291
console.error('master spawned');
92+
master.on('message', function(msg) {
93+
if (msg === 'started worker') {
94+
cb(master);
95+
}
96+
});
9397
});
9498
}
9599

@@ -99,7 +103,17 @@ function master() {
99103
args: [ 'worker' ]
100104
});
101105
var worker = cluster.fork();
102-
console.log('%j\n', { master: process.pid, worker: worker.pid });
106+
worker.on('message', function(msg) {
107+
if (msg === 'worker ready') {
108+
process.send('started worker');
109+
}
110+
});
111+
// Prevent outliving our parent process in case it is abnormally killed -
112+
// under normal conditions our parent kills this process before exiting.
113+
process.on('disconnect', function() {
114+
console.error('master exit on disconnect');
115+
process.exit(0);
116+
});
103117
}
104118

105119

@@ -112,5 +126,6 @@ function worker() {
112126
res.end('hello from worker\n');
113127
}).listen({ fd: 3 }, function() {
114128
console.error('worker listening on fd=3');
129+
process.send('worker ready');
115130
});
116131
}

test/parallel/test-listen-fd-server.js

+38-44
Original file line numberDiff line numberDiff line change
@@ -13,65 +13,62 @@ if (common.isWindows) {
1313

1414
switch (process.argv[2]) {
1515
case 'child': return child();
16-
case 'parent': return parent();
17-
default: return test();
1816
}
1917

20-
// spawn the parent, and listen for it to tell us the pid of the child.
18+
var ok;
19+
20+
process.on('exit', function() {
21+
assert.ok(ok);
22+
});
23+
2124
// WARNING: This is an example of listening on some arbitrary FD number
2225
// that has already been bound elsewhere in advance. However, binding
2326
// server handles to stdio fd's is NOT a good or reliable way to do
2427
// concurrency in HTTP servers! Use the cluster module, or if you want
2528
// a more low-level approach, use child process IPC manually.
26-
function test() {
27-
var parent = spawn(process.execPath, [__filename, 'parent'], {
28-
stdio: [ 0, 'pipe', 2 ]
29-
});
30-
var json = '';
31-
parent.stdout.on('data', function(c) {
32-
json += c.toString();
33-
if (json.indexOf('\n') !== -1) next();
34-
});
35-
function next() {
36-
console.error('output from parent = %s', json);
37-
var child = JSON.parse(json);
38-
// now make sure that we can request to the child, then kill it.
39-
http.get({
40-
server: 'localhost',
41-
port: PORT,
42-
path: '/',
43-
}).on('response', function(res) {
44-
var s = '';
45-
res.on('data', function(c) {
46-
s += c.toString();
47-
});
48-
res.on('end', function() {
49-
// kill the child before we start doing asserts.
50-
// it's really annoying when tests leave orphans!
51-
process.kill(child.pid, 'SIGKILL');
52-
try {
53-
parent.kill();
54-
} catch (e) {}
55-
29+
test(function(child) {
30+
// now make sure that we can request to the child, then kill it.
31+
http.get({
32+
server: 'localhost',
33+
port: PORT,
34+
path: '/',
35+
}).on('response', function(res) {
36+
var s = '';
37+
res.on('data', function(c) {
38+
s += c.toString();
39+
});
40+
res.on('end', function() {
41+
child.kill();
42+
child.on('exit', function() {
5643
assert.equal(s, 'hello from child\n');
5744
assert.equal(res.statusCode, 200);
45+
console.log('ok');
46+
ok = true;
5847
});
5948
});
60-
}
61-
}
49+
});
50+
});
6251

6352
function child() {
53+
// Prevent outliving the parent process in case it is terminated before
54+
// killing this child process.
55+
process.on('disconnect', function() {
56+
console.error('exit on disconnect');
57+
process.exit(0);
58+
});
59+
6460
// start a server on fd=3
6561
http.createServer(function(req, res) {
6662
console.error('request on child');
6763
console.error('%s %s', req.method, req.url, req.headers);
6864
res.end('hello from child\n');
6965
}).listen({ fd: 3 }, function() {
7066
console.error('child listening on fd=3');
67+
process.send('listening');
7168
});
7269
}
7370

74-
function parent() {
71+
function test(cb) {
7572
var server = net.createServer(function(conn) {
7673
console.error('connection on parent');
7774
conn.end('hello from parent\n');
@@ -80,7 +77,7 @@ function parent() {
8077

8178
var spawn = require('child_process').spawn;
8279
var child = spawn(process.execPath, [__filename, 'child'], {
83-
stdio: [ 0, 1, 2, server._handle ]
80+
stdio: [ 0, 1, 2, server._handle, 'ipc' ]
8481
});
8582

8683
console.log('%j\n', { pid: child.pid });
@@ -90,13 +87,10 @@ function parent() {
9087
// be accepted, because the child has the fd open.
9188
server.close();
9289

93-
child.on('exit', function(code) {
94-
console.error('child exited', code);
95-
});
96-
97-
child.on('close', function() {
98-
console.error('child closed');
90+
child.on('message', function(msg) {
91+
if (msg === 'listening') {
92+
cb(child);
93+
}
9994
});
100-
console.error('child spawned');
10195
});
10296
}

0 commit comments

Comments
 (0)