Skip to content

Commit 0f0069c

Browse files
authored
feat: add beforeExit options (#4)
1 parent c49d382 commit 0f0069c

7 files changed

Lines changed: 193 additions & 11 deletions

File tree

exit.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict';
2+
3+
const assert = require('assert');
4+
const is = require('is-type-of');
5+
const once = require('once');
6+
7+
module.exports = getExitFunction;
8+
9+
function getExitFunction(beforeExit, logger, label) {
10+
if (beforeExit) assert(is.function(beforeExit), 'beforeExit only support function');
11+
12+
return once(code => {
13+
if (!beforeExit) process.exit(code);
14+
Promise.resolve()
15+
.then(() => {
16+
return beforeExit();
17+
})
18+
.then(() => {
19+
logger.info('[%s] beforeExit success', label);
20+
process.exit(code);
21+
})
22+
.catch(err => {
23+
logger.error('[%s] beforeExit fail, error: %s', label, err.message);
24+
process.exit(code);
25+
});
26+
});
27+
28+
}

index.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
'use strict';
22

33
const cluster = require('cluster');
4+
const getExitFunction = require('./exit');
5+
46
const init = Symbol('graceful-process-init');
57

68
module.exports = (options = {}) => {
@@ -29,11 +31,13 @@ module.exports = (options = {}) => {
2931
}
3032
process[init] = true;
3133

34+
const exit = getExitFunction(options.beforeExit, logger, label);
35+
3236
// https://github.com/eggjs/egg-cluster/blob/master/lib/agent_worker.js#L35
3337
// exit gracefully
3438
process.once('SIGTERM', () => {
3539
printLogLevels.info && logger.info('[%s] receive signal SIGTERM, exiting with code:0', label);
36-
process.exit(0);
40+
exit(0);
3741
});
3842

3943
process.once('exit', code => {
@@ -56,7 +60,7 @@ module.exports = (options = {}) => {
5660
setImmediate(() => {
5761
// if disconnect event emit, maybe master exit in accident
5862
logger.error('[%s] receive disconnect event on child_process fork mode, exiting with code:110', label);
59-
process.exit(110);
63+
exit(110);
6064
});
6165
});
6266
}

package.json

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33
"version": "1.1.0",
44
"description": "graceful exit process even parent exit on SIGKILL.",
55
"files": [
6-
"index.js"
6+
"index.js",
7+
"exit.js"
78
],
89
"scripts": {
910
"lint": "eslint *.js test",
1011
"test": "npm run lint && npm run test-local",
1112
"test-local": "egg-bin test",
1213
"cov": "cross-env COV=true egg-bin cov",
1314
"autod": "autod",
15+
"pkgfiles": "egg-bin pkgfiles",
1416
"ci": "npm run lint && npm run cov"
1517
},
1618
"repository": {
@@ -22,17 +24,21 @@
2224
},
2325
"author": "fengmk2",
2426
"license": "MIT",
25-
"dependencies": {},
27+
"dependencies": {
28+
"is-type-of": "^1.2.0",
29+
"once": "^1.4.0"
30+
},
2631
"devDependencies": {
27-
"autod": "^2.8.0",
28-
"coffee": "^4.0.1",
32+
"autod": "^3.0.1",
33+
"coffee": "^4.1.0",
2934
"cross-env": "^5.0.1",
30-
"egg-bin": "^4.0.4",
35+
"egg-bin": "^4.3.7",
3136
"egg-ci": "^1.8.0",
32-
"eslint": "^4.0.0",
33-
"eslint-config-egg": "^4.2.1",
34-
"mz-modules": "^1.0.0",
35-
"urllib": "^2.22.0"
37+
"eslint": "^4.18.1",
38+
"eslint-config-egg": "^7.0.0",
39+
"mm": "^2.2.0",
40+
"mz-modules": "^2.1.0",
41+
"urllib": "^2.26.0"
3642
},
3743
"ci": {
3844
"version": "6, 8, 9"

test/fixtures/_async.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
'use strict';
2+
3+
const sleep = require('mz-modules/sleep');
4+
5+
module.exports = async () => {
6+
console.log('process exiting');
7+
await sleep(1000);
8+
console.log('process exited');
9+
};

test/fixtures/_async_error.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
'use strict';
2+
3+
const sleep = require('mz-modules/sleep');
4+
5+
module.exports = async () => {
6+
await sleep(1000);
7+
throw new Error('reject');
8+
};

test/fixtures/before-exit.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
3+
const http = require('http');
4+
const sleep = require('mz-modules/sleep');
5+
6+
http.createServer((req, res) => {
7+
res.writeHead(200);
8+
res.end('hello world\n');
9+
}).listen(8000);
10+
11+
let beforeExit;
12+
switch (process.env.MODE) {
13+
case 'async':
14+
beforeExit = require('./_async');
15+
break;
16+
case 'async-error':
17+
beforeExit = require('./_async_error');
18+
break;
19+
case 'promise':
20+
beforeExit = () => {
21+
console.log('process exiting');
22+
return sleep(1000).then(() => console.log('process exited'));
23+
};
24+
break;
25+
case 'function-error':
26+
beforeExit = () => {
27+
throw new Error('process exit');
28+
};
29+
break;
30+
default:
31+
beforeExit = () => {
32+
console.log('process exit');
33+
};
34+
}
35+
36+
console.log(`Worker ${process.pid} started`);
37+
require('../..')({
38+
logger: console,
39+
beforeExit,
40+
});
41+
42+
// run again should work
43+
require('../..')();

test/index.test.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const path = require('path');
55
const coffee = require('coffee');
66
const sleep = require('mz-modules/sleep');
77
const urllib = require('urllib');
8+
const mm = require('mm');
89

910
const fixtures = path.join(__dirname, 'fixtures');
1011
const waitStart = process.env.COV ? 5000 : 2000;
@@ -108,4 +109,87 @@ describe('test/index.test.js', () => {
108109
child.expect('stderr', /222exit with code:110/);
109110
});
110111
});
112+
113+
describe('beforeExit', () => {
114+
afterEach(mm.restore);
115+
116+
it('should support normal function', function* () {
117+
mm(process.env, 'MODE', '');
118+
const startFile = path.join(fixtures, 'before-exit.js');
119+
const child = coffee.fork(startFile)
120+
.debug();
121+
yield sleep(waitStart);
122+
const result = yield urllib.request('http://127.0.0.1:8000/');
123+
assert(result.status === 200);
124+
125+
child.proc.kill();
126+
yield sleep(5000);
127+
child.expect('stdout', /process exit/);
128+
child.expect('stdout', /exit with code:0/);
129+
});
130+
131+
it('should support function return promise', function* () {
132+
mm(process.env, 'MODE', 'promise');
133+
const startFile = path.join(fixtures, 'before-exit.js');
134+
const child = coffee.fork(startFile)
135+
.debug();
136+
yield sleep(waitStart);
137+
const result = yield urllib.request('http://127.0.0.1:8000/');
138+
assert(result.status === 200);
139+
140+
child.proc.kill();
141+
yield sleep(5000);
142+
child.expect('stdout', /beforeExit success/);
143+
child.expect('stdout', /process exiting\nprocess exited/);
144+
child.expect('stdout', /exit with code:0/);
145+
});
146+
147+
if (parseInt(process.versions.node) < 8) return;
148+
149+
it('should support async function', function* () {
150+
mm(process.env, 'MODE', 'async');
151+
const startFile = path.join(fixtures, 'before-exit.js');
152+
const child = coffee.fork(startFile)
153+
.debug();
154+
yield sleep(waitStart);
155+
const result = yield urllib.request('http://127.0.0.1:8000/');
156+
assert(result.status === 200);
157+
158+
child.proc.kill();
159+
yield sleep(5000);
160+
child.expect('stdout', /beforeExit success/);
161+
child.expect('stdout', /process exiting\nprocess exited/);
162+
child.expect('stdout', /exit with code:0/);
163+
});
164+
165+
it('should exit when async error', function* () {
166+
mm(process.env, 'MODE', 'async-error');
167+
const startFile = path.join(fixtures, 'before-exit.js');
168+
const child = coffee.fork(startFile)
169+
.debug();
170+
yield sleep(waitStart);
171+
const result = yield urllib.request('http://127.0.0.1:8000/');
172+
assert(result.status === 200);
173+
174+
child.proc.kill();
175+
yield sleep(5000);
176+
child.expect('stderr', /beforeExit fail, error: reject/);
177+
child.expect('stdout', /exit with code:0/);
178+
});
179+
180+
it('should exit when function error', function* () {
181+
mm(process.env, 'MODE', 'function-error');
182+
const startFile = path.join(fixtures, 'before-exit.js');
183+
const child = coffee.fork(startFile)
184+
.debug();
185+
yield sleep(waitStart);
186+
const result = yield urllib.request('http://127.0.0.1:8000/');
187+
assert(result.status === 200);
188+
189+
child.proc.kill();
190+
yield sleep(5000);
191+
child.expect('stderr', /beforeExit fail, error: process exit/);
192+
child.expect('stdout', /exit with code:0/);
193+
});
194+
});
111195
});

0 commit comments

Comments
 (0)