I have a node application that runs long running tasks so whenever a task runs a child process is forked to run the task. The code creates a fork for the task to be run and sends a message to the child process to start.
Originally, when the task was complete, I was sending a message back to the parent process and the parent process would call .kill() on the child process. I noticed in my activity monitor that the node processes weren't being removed. All the child processes were hanging around. So, instead of sending a message to the parent and calling .kill(), I called process.exit() in the child process code once the task was complete.
The second approach seems to work fine and I see the node processes being removed from the activity monitor but I'm wondering if there is a downside to this approach that I don't know about. Is one method better than the other? What's the difference between the 2 methods?
My code looks like this for the messaging approach.
//Parent process
const forked = fork('./app/jobs/onlineConcurrency.js');
forked.send({clientId: clientData.clientId,
schoolYear: schoolYear
});
forked.on("message", (msg) => {
console.log("message", msg);
forked.kill();
});
//child Process
process.on('message', (data) => {
console.log("Message recieved");
onlineConcurrencyJob(data.clientId, data.schoolYear, function() {
console.log("Killing process");
process.send("done");
});
})
The code looks like this for the child process when just exiting
//child Process
process.on('message', (data) => {
console.log("Message received");
onlineConcurrencyJob(data.clientId, data.schoolYear, function() {
console.log("Killing process");
process.exit();
});
})
kill sends a signal to the child process. Without an argument, it sends a SIGTERM (where TERM is short for "termination"), which typically, as the name suggests, terminates the process.
However, sending a signal like that is a forced method of stopping a process. If the process is performing tasks like writing to a file, and it receives a termination signal, it might cause file corruption because the process doesn't get a chance to write all data to the file, and close it (there are mitigations for this, like installing a signal handler that can be used to "catch" signals and ignore them, or finish all tasks before exiting, but this requires explicit code to be added to the child process).
Whereas with process.exit(), the process exits itself. And typically, it does so at a point where it knows that there are no more pending tasks, so it can exit cleanly. This is generally speaking the best way to stop a (child) process.
As for why the processes aren't being removed, I'm not sure. It could be that the parent process isn't cleaning up the resources for the child processes, but I would expect that to happen automatically (I don't even think you can perform so-called "child reaping" explicitly in Node.js).
Calling process.exit(0) is the best mechanism, though there are cases where you might want to .kill from the parent (eg. A distributed search where one node returning means all nodes can stop).
.kill is probably failing due to some handling of the signal it is getting. Try .kill('SIGTERM'), or even 'SIGKILL'.
Also note that subprocesses which aren't killed when the parent process exits will be moved to the grandparent process. See here for more info and a proposed workaround: https://github.com/nodejs/node/issues/13538
In summary, this is default Unix behavior, and the workaround is to process.on("exit", () => child.kill())
Related
I'm setting up error handling for a child process in my node application, and I'm wondering about these two options:
childProcess.on('error', err => {
// omitted
});
// do we need both this and the preceding handler?
childProcess.stderr.on('data', data => {
// omitted
});
What's the difference? Are both needed?
The child process error event is fired when node has a problem starting, stopping or managing the child process.
stderr is one of the output channels for the child process once it's running. The data event can be fired many times in normal operation of a process and can often include important information about error conditions of the process. You would normally handle stdout in a similar fashion to stderr.
The exit events code value can also be important for a child process. Often processes will exit with a non 0 return code to signal an issue occurred.
I thought if I run this and then hit control-C, the program should exit after displaying "Exiting...". It does not.
Of course, I want to do a lot more than console.log in the real application.
process.on('SIGINT', function() {
console.log("Exiting...");
process.exit();
});
while(1);
It does catch but does not exit. I have to kill the process separately.
Node version 8.x LTS
EDIT:
The edit is to make one of my comments below clear.As is made clear in the accepted answer, my signal-handler was overwriting the default one but it was NEVER getting executed. The fact that Cntl-C was not killing the process gave me the impression that the signal-handler was actually executing. It had merely overwritten the built-in handler. THE ANSWER IS TRULY INFORMATIVE - PACKED WITH INFO IN A FEW WORDS.
while(1) is hanging onto the process. Change it to:
setInterval(() => {}, 1000);
And it behaves as you would like.
I presume you used while(1) as a placeholder for a running program, but it's not an accurate representation. A normal node app would not hold the process synchronously like that.
It's probably worth noting that when you execute process.on('SIGINT', ... you are pre-empting node's normal SIGINT handler, which would have exited on ctrl-C even if while(1) was holding the process. By adding your own handler, your code will run when node gets to it, which would be after the current synchronous event cycle, which in this case never happens.
I have an application that's (basically) has a parent and child process. The child process does several steps (in this case runs a test) and writes to STDOUT whenever a step of the test completes. This way, the parent process will always have the 'latest' results, so if the child exits pre-maturely for some reason the parent will know the last step completed.
In child process
console.log(doStep1());
console.log(doStep2());
console.log(doStep3());
process.exit();
In the parent process
c.stdout.on("data", () => storeLatestResultsInVariable())
c.on("exit", () => indicateTestExitedAndAlertUserToLatestResults());
The issue I'm noticing, is that the onExit callback is being triggered before the storeLatestResultsInVariable() callback is completed for the final console.log. So in some cases the user is alerted of the results of doStep2 and not doStep3.
It's a little more complicated than that because the 'steps' are all asynchronous and process.exitis called in a function that runs if all 'steps' are completed or on an 'error', so I need to use process.exit to ensure the process completely closes out.
My main question: Is there any way for me to get Node to wait until the storeLatestResultsInAVariable() function completes before calling the c.on("exit", ...) code? Otherwise, is there a way to pass back data with process.exit so that I can use that data in my c.on("exit", ...) callback?
Really appreciate the help, hopefully the problem description was clear enough!
I read node.js docs. It says:
Even though the name of this function is process.kill(), it is really just a signal sender, as the kill system call. The signal sent may do something other than killing the target process.
console.log('current process id: ', process.pid);
process.on('SIGHUP', function() {
console.log('Got SIGHUP signal');
});
setTimeout(() => {
console.log('Exiting...');
process.exit(0); //kill process
console.log('Process id that has exited: ', process.pid); //does not output
}, 1000);
process.kill(process.pid, 'SIGHUP'); //does not kill process
console.log('Id of the process exiting: ', process.pid); //so this output normally
output:
current process id: 64520
Id of the process exiting: 64520
Got SIGHUP signal
Exiting...
It seems process.exit(0) is the one which kills node.js process.
It all depends on the situation that you're in. Like Gospal Joshi said process.exit([code]) is useful to end the process very quickly. But in other cases you may want to pass a signal to that process before shutdown for cleanup/graceful shutdown. For example if you are listening to signal events such as:
process.on('SIGINT', () => {
//do something
}
it allows you to run cleanup or gracefully shutdown the process vs exiting instantly without doing anything.
Also, note that Node.js establishes signal handlers for SIGINT and SIGTERM and Node.js processes will not terminate immediately due to receipt of those signals. Rather, Node.js will perform a sequence of cleanup actions and then will re-raise the handled signal - Node Documentation
process.kill(pid, [ code ]) shines on concurrent applications with multiple processes (since you can just plug in the pid of the process you wish to kill and it does it).
process.exit() is sufficient and most common if you dont have a usecase that requires killing any other process than the main node process.
Personally, I recommend you to use process.exit() unless you really have to kill another process (pid different than process.pid)
Use process.exit(), It ends the process with the specified code. Calling process.exit() will force the process to exit as quickly as possible even if there are still asynchronous operations pending
Syntax:
process.exit([code])
Links:
Exit codes
Exit() Documentation
Im creating NodeJS application, that creates quite a few child processes. They are started by both spawn and exec (based on lib implementation). Some examples may be GraphicsMagick (gm) for image manipulation or Tesseract (node-tesseract) for OCR. Now I would like to gracefully end my application so I created shutdown hook:
function exitHandler() {
killer.waitForShutdown().then(function(){
logger.logInfo("Exited successfully.");
process.exit();
}).catch(function(err) {
logger.logError(err, "Error during server shutdown.");
process.exit();
});
}
process.on('exit', exitHandler);
process.on('SIGINT', exitHandler);
process.on('SIGTERM', exitHandler);
Exit handling itself works fine, it is waiting well and so on, but there is a catch. All "native" (gm, tesseract, ...) processes that run at that time are also killed. Exception messages only consists of "Command failed" and then content of command which failed e.g.
"Command failed: /bin/sh -c tesseract tempfiles/W1KwFSdz7MKdJQQnUifQFKdfTRDvBF4VkdJgEvxZGITng7JZWcyPYw6imrw8JFVv/ocr_0.png /tmp/node-tesseract-49073e55-0ef6-482d-8e73-1d70161ce91a -l eng -psm 3\nTesseract Open Source OCR Engine v3.03 with Leptonica"
So at least for me, they do not tell anything useful. I'm also queuing process execution, so PC don't get overloaded by 50 processes at one time. When running processes are killed by SIGINT, new processes that were queued are started just fine and finishes successfully. I have problem only with those few running at the time of receiving SIGINT. This behavior is same on Linux (Debian 8) and Windows (W10). From what I read here, people usually have opposite problem (to kill child processes). I tried to search if stdin gets somehow piped into child processes but I can't find it. So is this how its supposed to work? Is there any trick to prevent this behavior?
The reason this happens is because, by default, the detached option is set to false. If detached is false, the signals will also be sent to the child processes, regardless of whether you setup an event listener.
To stop this happening, you need to change your spawn calls to use the third argument in order to specify detached; for example:
spawn('ls', ['-l'], { detached: true })
From the Node documentation:
On Windows, setting options.detached to true makes it possible for the
child process to continue running after the parent exits. The child
will have its own console window. Once enabled for a child process, it
cannot be disabled.
On non-Windows platforms, if options.detached is set to true, the
child process will be made the leader of a new process group and
session. Note that child processes may continue running after the
parent exits regardless of whether they are detached or not. See
setsid(2) for more information.