-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathindex.js
More file actions
100 lines (88 loc) · 3.62 KB
/
Copy pathindex.js
File metadata and controls
100 lines (88 loc) · 3.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
'use strict';
let _ = require('lodash'),
httpShutdown = require('http-shutdown').extend();
let shuttingDown, server, events, logger, newConnectionsTimeout, shutDownTimeout, closeAllConnectionsTimeoutId, callback;
const REQUIRED_LOGGER_IMPLEMENTATIONS = ['trace', 'info', 'error'];
function registerShutdownEvent(options) {
validateOptions(options);
try {
server = options.server.withShutdown();
} catch (error) {
throw new Error('server must be an express server instance');
}
shuttingDown = false;
logger = options.logger;
shutDownTimeout = Number(options.shutdownTimeout);
newConnectionsTimeout = Number(options.newConnectionsTimeout);
events = options.events || ['SIGINT', 'SIGTERM'];
callback = options.callback;
logger.trace('Registering shutdown events', events);
[].concat(events).map((event) => { process.on(event, shutdown) });
}
function validateOptions(options) {
if (!_.isArray(options.events) && typeof options.events !== 'string') {
throw new Error('events is required and must be a string or array of strings');
}
if (options.callback && typeof options.callback !== 'function') {
throw new Error('callback must be a function and must return a Promise');
}
if (options.newConnectionsTimeout !== undefined && (isNaN(options.newConnectionsTimeout) || Number(options.newConnectionsTimeout) <= 0)) {
throw new Error('newConnectionsTimeout must be a positive number');
}
if (isNaN(options.shutdownTimeout) || Number(options.shutdownTimeout) <= 0) {
throw new Error('shutdownTimeout is required and must be a number greater then 0');
}
if (!options.server) {
throw new Error('server is required and must be an express instance');
}
let loggerImplementsRequiredFunctions = _.every(REQUIRED_LOGGER_IMPLEMENTATIONS, (implementation) => {
return typeof options.logger[implementation] === 'function';
});
if (!options.logger || !loggerImplementsRequiredFunctions) {
throw new Error(`logger with required implementations is required [${REQUIRED_LOGGER_IMPLEMENTATIONS}]`);
}
}
function shutdown() {
// No need to execute the function if we already initiated the shutdown process
if (shuttingDown) return;
shuttingDown = true;
logger.info(`Shut down process initiated with graceful timeout of ${shutDownTimeout + newConnectionsTimeout} ms`);
setTimeout(() => {
logger.info('Server close event initiated. service wont except new connections now.');
startGracefulShutdownPeriod();
// Close express connections
server.shutdown(function () {
logger.info('All connections were closed gracefully');
clearTimeout(closeAllConnectionsTimeoutId);
tearDown();
});
}, newConnectionsTimeout);
}
function tearDown() {
if (callback) {
return callback()
.then(() => {
logger.info('Callback function executed successfully');
exit(0);
})
.catch((error) => {
logger.error(error, 'Callback function execution failed');
exit(1);
});
} else {
exit(0);
}
}
function startGracefulShutdownPeriod() {
closeAllConnectionsTimeoutId = setTimeout(function (){
logger.info('Not all connections were closed within the grace time. Closing all remaining connections forcefully');
tearDown();
}, shutDownTimeout);
}
function exit(code) {
logger.info('Shut down process completed');
process.exit(code);
}
module.exports = {
registerShutdownEvent: registerShutdownEvent
};