child process doesn't exit - node.js

Following is my gulp task:
httpServer.on("close", (code, signal) => {
console.log(
`child process terminated due to receipt of signal ${signal}, code ${code}`
);
console.log("httpServer close");
resolve();
})
);
await new Promise((resolve) => setTimeout(resolve, 2e3));
httpServer.kill("SIGINT");
await httpServerClose;
console.log("here");
I got following output:
Available on:
http://127.0.0.1:5001
http://192.168.31.122:5001
http://172.21.96.1:5001
http://172.18.192.1:5001
Hit CTRL-C to stop the server
child process terminated due to receipt of signal SIGINT, code null
httpServer close
here
[13:32:57] Finished 'build:release' after 2.01 s
The problem is port 5001 is still in use.
I'm on windows 10, node v16.13.1

.kill() doesn't guarantees that the process will successfully terminate. You may check for the resulting boolean that kill() returns, but that only indicates wheter the signal was delivered or not.
childProcess.on('close', (code, signal) => {
console.log(
`child process terminated due to receipt of signal ${signal}`);
});
// Send SIGHUP to process.
const signalDelivered = childProcess.kill('SIGHUP');
console.log(signalDelivered)
For some reason, the childProcess is still running / crashed or the SIGINT was not handled correctly.
My suggestion is that you implement "Graceful shutdown".
Remember, if in your childProcess code you are handling process.on('SIGINT', ..),you need to close the server that is listening connections and then call process.exit(0).
const server = app.listen(5001)
process.on('SIGINT',function(){
server.close(err => {
//exit after server is not listening anymore
process.exit(err ? 1 : 0)
})
});

Related

NodeJS bug in Linux when executing child_process.fork?

I cannot reliably get a forked child process to send back a message to the parent that exceeds 219262 bytes.
The issue is only on Linux. In Windows, it works as expected. And this issue seems have been introduced between Node versions 1.0.1 and 1.0.2 - works fine on Node versions prior to 1.0.1 but not after.
(the maxBuffer option is not relevent for child_process.fork, it only applies to child_process.exec and child_process.execFile)
Below is the failing sample. Executing "node parent" on the command line will fail to output the child's "messageToParent" if it exceeds 219262 bytes on Linux.
parent.js is:
var cp = require('child_process');
var child = cp.fork('./child', [], {});
console.log('>>>PARENT ---> SENDING MESSAGE TO CHILD');
child.send({});
child.on('message', function(msg) {
console.log('>>>PARENT ---> MESSAGE RECEIVED FROM CHILD = ' + JSON.stringify(msg));
});
child.on('error', function(err) {
console.log('>>>PARENT ---> ERROR FROM CHILD. err = '+ err);
});
child.on('exit', function(code, signal) {
console.log('>>>PARENT ---> EXIT FROM CHILD. code='+code+' signal = '+ signal);
});
child.on('close', function(code, signal) {
console.log('>>>PARENT ---> CLOSE FROM CHILD. code='+code+' signal = '+signal);
});
child.on('disconnect', function() {
console.log('>>>PARENT ---> DISCONNECT FROM CHILD');
});
child.js is
process.on('message', function(messageFromParent) {
console.log('>>>>>>CHILD ---> RECEIVED MESSAGE FROM PARENT');
var messageToParent = "It would be too long to post on stackoverflow, but if I make this string longer than 219262 bytes, it fails to return to the parent in Linux. There is no such issue in Windows";
var ret = process.send(messageToParent);
console.log('>>>>>>CHILD ---> SENDING MESSAGE TO PARENT process.send returned ' + ret);
process.exit(0);
});
process.on('uncaughtException', function(err) {
process.send({ output: {ERROR:err} });
process.exit(-1);
});
Posting an answer in case anyone else stumbles into this issue (https://github.com/nodejs/node/issues/36268)
The above child.js works perfectly in Node versions prior to 1.0.1 since child_process.fork() used to be synchronous. So "process.send(messageToParent)", followed by "process.exit(0)" will always return messageToParent to parent.js.
In later versions of Node, however, process.send() is async. Therefore, the child must exit via process.exit() within a process.send callback, else a race condition is created between V8 javascript thread and IPC pipe.
Also - in Windows, the default IPC pipe buffer is large enough that the message is always returned to parent prior to child exiting. This is not the case in Linux. This explains why the above code works in Windows even with later versions of Node where process.send() is async.

Child process isn't logging when receiving SIGINT

I am trying to kill a previously forked child process in Node.js. I'm unsure if it's working because my console.log line is not being hit in the child process.
Here is what I'm calling from the parent:
console.log("sending SIGINT from main");
child.kill("SIGINT");
And in the child:
process.on("SIGINT", () => {
console.log("Killing bot");
DBS.Bot.destroy();
process.exit();
});
The only console log I see is from the parent.
When spawning a NodeJS child process, this child process' stdout is not the same as the parent process' stdout stream, which is why you do not see any logging output. You can add the following code to copy any output of your child process to your main process' stdout stream:
child.stdout.on ("data", (data) => {
console.log ("child process: " + data.toString ());
});
In some circumstances, it can be useful to also capture stderr output, which is the stream NodeJS' console.err () writes to:
child.stderr.on ("data", (data) => {
console.log ("child process error: " + data.toString ());
});
Also, to make sure a child process has exited with a non-error code, i.e. the exit code is 0, you can use NodeJS' child_process "exit" signal:
child.on ("exit", (code, signal) => {
if (signal !== null) console.log ("child exited due to signal '" + signal + "'");
else console.log ("child exited with code " + code);
});
For more information, make sure to check out NodeJS' documenation on child_process.

How to catch child_process restarting/forking itself

I am trying to get a node script to run another command with child_process, which starts up a server, but this server has 'live reload' functionality.
As far as I understand, as soon as anything changes, the child process restarts, and my parent process continues as if the child died/exited.
I want my parent process to re-connect to a newly forked/spawned child.
Is there any way to do this?
const command = 'server --listen 7070';
const process = child_process.exec(command);
process.stdout.on('data', (data) => {
console.log(data);
});
process.on('exit', (event) => {
console.log('The child process exited')
});
In this example, the server keeps running, but the stdout processing stops after the child process restarts itself for the first time.

Exit custom function with loop process on node.js shutdown signal

I have a job that is executed ones per day. My app is running on Heroku, and dyno is restarted ones a day.
So what can happen is that during job execution Heroku starts the restart of dyno.
That itself is not a problem as I can start job two times per day, but what is a problem is to stop the job in the middle of task when it is not in stable status.
I would like now somehow to send this signal to job function so I can break any loops and stop function execution in safe way.
I know how to get signal:
process
.on('SIGTERM', shutdown('SIGTERM'))
.on('SIGINT', shutdown('SIGINT'))
.on('uncaughtException', shutdown('uncaughtException'));
function shutdown(signal) {
console.log(`${ signal }...`);
return (err) => {
if (err) console.error(err.stack || err);
setTimeout(() => {
console.log('...waited 5s, exiting.');
process.exit(err ? 1 : 0);
}, 5000).unref();
};
}
but how to send this signal to my job function and to break from it safely?
Thank you.
So the best solution I came up with is following.
// Manage signals
let shutDownSignal = false;
process
.on('SIGTERM', shutdown('SIGTERM'))
.on('SIGINT', shutdown('SIGINT'))
.on('uncaughtException', shutdown('uncaughtException'));
function shutdown(signal) {
return (err) => {
shutDownSignal = true;
console.log(`Received signal: ${ signal }...`);
if (err) console.error(err.stack || err);
setTimeout(() => {
console.log('...waited 15s, exiting.');
process.exit(err ? 1 : 0);
}, 15000).unref();
};
}
module.exports.getShutDownSingnal = function(){ return shutDownSignal; }
then with getShutDownSingnal() anywhere I can check whether shutdown is initiated.
One more thing. It is necessary to put Procfile in app root with
web: node index.js
in it (or app.js depending what are you using).
This is necessary so that SIGTERM and SIGKILL signals are transferred correctly to node (for example if using npm, it will not transfer this signal correctly). More about this on Heroku docs
Maybe this will be useful for someone.

Doing a cleanup action just before Node.js exits

I want to tell Node.js to always do something just before it exits, for whatever reason — Ctrl+C, an exception, or any other reason.
I tried this:
process.on('exit', function (){
console.log('Goodbye!');
});
I started the process, killed it, and nothing happened. I started it again, pressed Ctrl+C, and still nothing happened...
UPDATE:
You can register a handler for `process.on('exit')` and in any other case(`SIGINT` or unhandled exception) to call `process.exit()`
process.stdin.resume();//so the program will not close instantly
function exitHandler(options, exitCode) {
if (options.cleanup) console.log('clean');
if (exitCode || exitCode === 0) console.log(exitCode);
if (options.exit) process.exit();
}
//do something when app is closing
process.on('exit', exitHandler.bind(null,{cleanup:true}));
//catches ctrl+c event
process.on('SIGINT', exitHandler.bind(null, {exit:true}));
// catches "kill pid" (for example: nodemon restart)
process.on('SIGUSR1', exitHandler.bind(null, {exit:true}));
process.on('SIGUSR2', exitHandler.bind(null, {exit:true}));
//catches uncaught exceptions
process.on('uncaughtException', exitHandler.bind(null, {exit:true}));
This only works if you call synchronous code inside the handler, otherwise it will call the handler indefinitely
The script below allows having a single handler for all exit conditions. It uses an app specific callback function to perform custom cleanup code.
cleanup.js
// Object to capture process exits and call app specific cleanup function
function noOp() {};
exports.Cleanup = function Cleanup(callback) {
// attach user callback to the process event emitter
// if no callback, it will still exit gracefully on Ctrl-C
callback = callback || noOp;
process.on('cleanup',callback);
// do app specific cleaning before exiting
process.on('exit', function () {
process.emit('cleanup');
});
// catch ctrl+c event and exit normally
process.on('SIGINT', function () {
console.log('Ctrl-C...');
process.exit(2);
});
//catch uncaught exceptions, trace, then exit normally
process.on('uncaughtException', function(e) {
console.log('Uncaught Exception...');
console.log(e.stack);
process.exit(99);
});
};
This code intercepts uncaught exceptions, Ctrl+C and normal exit events. It then calls a single optional user cleanup callback function before exiting, handling all exit conditions with a single object.
The module simply extends the process object instead of defining another event emitter. Without an app specific callback the cleanup defaults to a no op function. This was sufficient for my use where child processes were left running when exiting by Ctrl+C.
You can easily add other exit events such as SIGHUP as desired. Note: per NodeJS manual, SIGKILL cannot have a listener. The test code below demonstrates various ways of using cleanup.js
// test cleanup.js on version 0.10.21
// loads module and registers app specific cleanup callback...
var cleanup = require('./cleanup').Cleanup(myCleanup);
//var cleanup = require('./cleanup').Cleanup(); // will call noOp
// defines app specific callback...
function myCleanup() {
console.log('App specific cleanup code...');
};
// All of the following code is only needed for test demo
// Prevents the program from closing instantly
process.stdin.resume();
// Emits an uncaught exception when called because module does not exist
function error() {
console.log('error');
var x = require('');
};
// Try each of the following one at a time:
// Uncomment the next line to test exiting on an uncaught exception
//setTimeout(error,2000);
// Uncomment the next line to test exiting normally
//setTimeout(function(){process.exit(3)}, 2000);
// Type Ctrl-C to test forced exit
This catches every exit event I can find that can be handled. Seems quite reliable and clean so far.
[`exit`, `SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`].forEach((eventType) => {
process.on(eventType, cleanUpServer.bind(null, eventType));
})
"exit" is an event that gets triggered when node finish it's event loop internally, it's not triggered when you terminate the process externally.
What you're looking for is executing something on a SIGINT.
The docs at http://nodejs.org/api/process.html#process_signal_events give an example:
Example of listening for SIGINT:
// Start reading from stdin so we don't exit.
process.stdin.resume();
process.on('SIGINT', function () {
console.log('Got SIGINT. Press Control-D to exit.');
});
Note: this seems to interrupt the sigint and you would need to call process.exit() when you finish with your code.
function fnAsyncTest(callback) {
require('fs').writeFile('async.txt', 'bye!', callback);
}
function fnSyncTest() {
for (var i = 0; i < 10; i++) {}
}
function killProcess() {
if (process.exitTimeoutId) {
return;
}
process.exitTimeoutId = setTimeout(() => process.exit, 5000);
console.log('process will exit in 5 seconds');
fnAsyncTest(function() {
console.log('async op. done', arguments);
});
if (!fnSyncTest()) {
console.log('sync op. done');
}
}
// https://nodejs.org/api/process.html#process_signal_events
process.on('SIGTERM', killProcess);
process.on('SIGINT', killProcess);
process.on('uncaughtException', function(e) {
console.log('[uncaughtException] app will be terminated: ', e.stack);
killProcess();
/**
* #https://nodejs.org/api/process.html#process_event_uncaughtexception
*
* 'uncaughtException' should be used to perform synchronous cleanup before shutting down the process.
* It is not safe to resume normal operation after 'uncaughtException'.
* If you do use it, restart your application after every unhandled exception!
*
* You have been warned.
*/
});
console.log('App is running...');
console.log('Try to press CTRL+C or SIGNAL the process with PID: ', process.pid);
process.stdin.resume();
// just for testing
Just wanted to mention death package here: https://github.com/jprichardson/node-death
Example:
var ON_DEATH = require('death')({uncaughtException: true}); //this is intentionally ugly
ON_DEATH(function(signal, err) {
//clean up code here
})
async-exit-hook seems to be the most up-to-date solution for handling this problem. It's a forked/re-written version of exit-hook that supports async code before exiting.
I need to do an asynchronous cleanup action on exit, none of the answers in this question worked for me.
So I tried it myself, and finally found this:
process.once('uncaughtException', async () => {
await cleanup()
process.exit(0)
})
process.once('SIGINT', () => { throw new Error() })
After playing around with other answer, here is my solution for this task. Implementing this way helps me centralize cleanup in one place, preventing double handling the cleanup.
I would like to route all other exiting codes to 'exit' code.
const others = [`SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`]
others.forEach((eventType) => {
process.on(eventType, exitRouter.bind(null, { exit: true }));
})
What the exitRouter does is calling process.exit()
function exitRouter(options, exitCode) {
if (exitCode || exitCode === 0) console.log(`ExitCode ${exitCode}`);
if (options.exit) process.exit();
}
On 'exit', handle the clean up with a new function
function exitHandler(exitCode) {
console.log(`ExitCode ${exitCode}`);
console.log('Exiting finally...')
}
process.on('exit', exitHandler)
For the demo purpose, this is link to my gist. In the file, i add a setTimeout to fake the process running.
If you run node node-exit-demo.js and do nothing, then after 2 seconds, you see the log:
The service is finish after a while.
ExitCode 0
Exiting finally...
Else if before the service finish, you terminate by ctrl+C, you'll see:
^CExitCode SIGINT
ExitCode 0
Exiting finally...
What happened is the Node process exited initially with code SIGINT, then it routes to process.exit() and finally exited with exit code 0.
io.js has an exit and a beforeExit event, which do what you want.
In the case where the process was spawned by another node process, like:
var child = spawn('gulp', ['watch'], {
stdio: 'inherit',
});
And you try to kill it later, via:
child.kill();
This is how you handle the event [on the child]:
process.on('SIGTERM', function() {
console.log('Goodbye!');
});
Here's a nice hack for windows
process.on('exit', async () => {
require('fs').writeFileSync('./tmp.js', 'crash', 'utf-8')
});

Resources