Execute code on nodejs server shutdown with express - node.js

Is there a way to execute a piece of code in Node.js Express just before the node.js process exits, regardless whether it was due to an error being thrown, or pressing Ctrl+C, or any other reason?

You're looking for the exit event. From the documentation:
Emitted when the process is about to exit. This is a good hook to
perform constant time checks of the module's state (like for unit
tests). The main event loop will no longer be run after the 'exit'
callback finishes, so timers may not be scheduled.
And it would be implemented as
process.on('exit', function() {
console.log('About to close');
});
It's worth mentioning, that it's generally not a good idea to try to manipulate data if you don't know the reason for the exit or exception. If you don't know what's wrong, it's usually a better idea to start fresh, than try to accomplish something with something that may very well be FUBAR.

Related

Controlling Unix IPC signals in npm scripts

I'm working on implementing graceful shutdown for a node API. Typically, during development, this process is started using the common start script, but I have noticed that this causes some annoying behavior that I'm surprised I've never really noticed before in my 3 or so years as a node developer.
To begin shutdown during development, we simply hit ctrl+C in the bash terminal, which of course causes a SIGINT to be sent to the npm process and all of its child processes. I can capture this with a process.on handler to initiate a graceful shutdown from there-- closing off new requests, waiting for existing requests to finish, then killing the database connection, all that good stuff.
However, if the developer hits ctrl+C a second time, npm behaves differently. It seems to be sending a SIGTERM to the sh process it used to invoke my start script in the first place. This causes that sh process to print out Terminated and exit, returning control of the terminal to the user without waiting for the node process to exit.
This is really annoying because it gives the impression that the node process has been stopped, but of course it hasn't. It will continue until shutdown is complete, or it is forcibly killed with something like SIGKILL or SIGQUIT. If it happens to print anything out to the console, it will do so directly in the middle of whatever else the developer might now be running in that terminal.
For a trivial example, try this:
package.json:
{
"private": true,
"scripts": {
"start": "node index.js"
}
}
index.js:
async function waitForever() {
while(true) {
console.log('waiting...');
await new Promise((resolve) => {
setTimeout(resolve, 5000);
});
}
}
process.on('SIGINT', () => {
console.log('SIGINT recieved');
});
process.on('SIGTERM', () => {
console.log('SIGTERM recieved');
})
waitForever();
Run npm start in your terminal, then hit ctrl+c once. You'll see the signal make it through to node, but of course it won't exit. Now, do it a second time. You'll see the signal make it through again, but then you immediately see "Terminated" followed by your shell prompt. Until you find the process ID of the node process and kill it with kill -9 you'll keep seeing that waiting... message every five seconds.
I did some more fiddling around with this example and it very much seems like npm is completely responsible for this. If you send kill -2 directly to the npm process twice, the termination of the shell process occurs, without SIGINT ever being received by the node process.
So I have two main questions:
What the heck is going on here? Am I missing something about how my shell works, or is this some kind of feature built in to npm run-script? If so, where I can find information about this? npm help run-script shows nothing about it.
I know that start scripts are pretty common in projects like this, so it seems like someone else should be encountering this problem. How do people typically deal with it? I've Googled around a bunch and it's been hard to find a clear answer.
This isn't a huge deal, of course. The start script is just a convenience to make sure a TS compilation runs before starting up. I can have developers run the built app directly in their shell after building, or write a script that performs the build and start outside of an npm script. But it would be nice to not have to do this.
Really I'm just puzzled and would appreciate some assistance. Thanks!
To answer your 1 - looking at npm code this is expected behavior of handling SIGINT. First occurrence of signal is passed down to child process and also attaches one-time listener for subsequent SIGINT that will kill the npm parent process immediately. You can see code here.
I assume this is because npm start is meant as development stage shorthand only and there it makes sense to have a "handbrake" to kill process immediately in cases where you e.g. get signal handling wrong (unfortunately even that doesn't work in all cases as you found out).
I don't have answer for 2 but sometime ago there was a lengthy debate about this going in various npm issues regarding signal handling and npm start. Official NPM statement was mostly that npm start is not replacement for proper process manager (e.g. supervisor or systemd) and shouldn't be used in production environment like that.
EDIT: An answer for 2 from sripberger:
What I ended up doing follows. shutdown is a function that performs the shutdown, returning a promise. The first SIGINT will begin the shutdown, but a second will forcibly kill the process whether or not shutdown is finished. This does not prevent npm from terminating the shell process, but it does make sure the node process dies with it when this happens:
process.once('SIGINT', () => {
console.log('\nShutting down, please wait...');
// Begin the graceful shutdown.
shutdown().catch((err) => {
console.error('Could not shut down gracefully:', err);
});
// Attach a subsequent handler to force kill.
process.on('SIGINT', () => {
console.log('\nProcess killed.');
process.exit(0);
});
});
Of course, as noted by blami, it is not recommended to use npm scripts to control services in production. This is simply a convenience for development environments.

Will running Node.js scripts as CRON jobs without closing database connection eventually kill all the RAM?

I am building Node.js scripts that will be ran as CRON jobs. (full terminal scripts). These scripts fetch data from all around using APIs and MongoDB (native driver) is used. I don't didn't use db.close() statement and because of that script will never end by itself (or at least it looks like that way from the terminal), to finish it, it is necessary to press CTRL+C to cancel.
Back then when I was writing these scripts, someone from Stack overflow told me that it is not required to close connection anyway. So I let it be.
Now I wonder, do these scripts actually are still running? And as these would be ran as CRON jobs, with small intervals, does that mean that these scripts will eventually kill RAM from the server? Does that mean, there will be thousands scripts running and waiting for db.close() statement?
Example code:
MongoClient.connect(mongoUrl, (err, db) => {
if (err) {
console.log(err);
return;
}
var usersCollection = db.collection('users').find();
usersCollection.on('data', (doc) => {
console.log(doc);
});
Node scripts exit by themselves only when nothing listens for events any more.
With your scripts you probably know when nothing needs to be done after the main purpose of your script is achieved. For example, when the purpose of your script is to execute a certain code, print a summary etc. then you can add process.exit(0); after that command to make sure that the script finishes when it should.
In the case like this one when you don't have a one line of summary after which you can exit, you can listen for a certain event and exit when when it arrives. For example:
usersCollection.on('end', process.exit);
In addition to a process.exit in the right spot, you can also set a timeout to terminate the script after a certain time. For example:
setTimeout(process.exit, 20*1000);
will terminate the script after 20 seconds. This can be added just in case that somethings goes wrong with the database or the connection and you never get the 'end' event, or it takes too long to wait for it.
Sometimes adding process.exit(0); for scripts like this may be more convenient than closing all the database connections that may be open at that time or other things that may prevent your script from terminating.

What's the most efficient way to prevent a node.js script from terminating?

If I'm writing something simple and want it to run until explicitly terminated, is there a best practice to prevent script termination without causing blocking, using CPU time or preventing callbacks from working?
I'm assuming at that point I'd need some kind of event loop implementation or a way to unblock the execution of events that come in from other async handlers (network io, message queues)?
A specific example might be something along the lines of "I want my node script to sleep until a job is available via Beanstalkd".
I think the relevant counter-question is "How are you checking for the exit condition?".
If you're polling a web service, then the underlying setInterval() for the poll will keep it alive until cancelled. If you're taking in input from a stream, that should keep it alive until the stream closes, etc.
Basically, you must be monitoring something in order to know whether or not you should exit. That monitoring should be the thing keeping the script alive.
Node.js end when it have nothing else to do.
If you listen on a port, it have something to do, and a way to receive beanstalk command, so it will wait.
Create a function that close the port and you ll have your explicit exit, but it will wait for all current job to end before closing.

How to find out whether there are still events registered in the event loop (bonus: how many)

I am trying to write a Node.js program to execute and monitor javascript programs. I am looking for a way to find out whether the monitored program is still "running" i.e. doing anything useful.
In my current approach, when receiving code to test, I start a new child process and hand the code to it. The child process instruments the code creates a Sandbox using Contextify and executes the code using this sandbox.
After the sandbox.run(code) call returned I know that the blocking part of the code finished and can show that in the UI. However, I don't now whether the code registered any timers using setTimeouts or created any other event sources that would cause parts of the code to be exited later. So I don't know whether it's really "finished" yet.
Is there a way in Node.js to check whether there are still events on the event loop to be handled (or even better, how many are left)?
I found this other question, but it only talks about how to monitor the event loop to find out whether the performance of node is still fine. But I'm not interested in the performance (I don't care if the executing code is blocking for 10s or only doing something for 1ms every 2 minutes) and I don't want to use outside tools but find out about the state of the event loop from inside node itself. Is that possible?
I solved my problem in a way, although I didn't find a general answer to the question.
The idea here is that the process will exit by itself if it has executed all the code it was started with and no EventEmitters are registered anymore. This was basically what I wanted, since I wanted to be notified when the process was "done" and now I could just listen to the "exit" event of the child_process.
But my process that executed the code didn't exit by itself. This had two reasons:
I used a timer to regularly send the data gathered about the execution to the parent process. If such a timer is registered the process won't exit. You could unref the timer but I was afraid that would lead to data loss since the process could quit before the last bit of data was sent out (since it wouldn't wait for the timer to execute). So I changed my code to only schedule a timer if there was data to be sent out instead of regularly checking for data to be sent.
I used fork to create the child process. This also creates a communication channel between the parent and child process and since I needed to send the code to execute to the child_process I registered the child process for messages received from the parent using process.on("message", callback). However, now we have another EventEmitter registered which prevents the process from quitting. Luckily, I realized that I only needed one message from the parent process and no further messages so I could remove the event emitter after receiving that message. So instead of process.on() I used process.once() which will execute the callback only once and automatically remove the event emitter after that. Exactly what I wanted. Alternatively you could use process.removeListener().
Now I just wait for the child_process to exit and thus know that everything is finished and can notify the client.
So the solution here is to make sure none of your own EventEmitters keep the process alive and then just wait for it to exit.

Is there any way to launch async operation in node js upon exit?

Node js module during the operation requests some resources on remote service, that better be released when it exits. We know that there is very nice:
process.on('exit', function() {
// ...
});
But then it is said that it won't wait for any async operations to complete. So the question is if there's any workaround (there should be some, since it's quite widespread usage case)? Maybe one could start separate process or something?..
Only workaround I've seen is adding a wait loop and not finishing/returning from the .on('exit', function until a property has been updated globally.
Totally a bodge-job design-wise, very bad practice, but I've seen it work for short calls (I think there is some timeout but I never bothered to look into the details).
I think you could/should do clean-up before on('exit') by listening for ctrl-c signal like in this post.

Resources