57

I have written a script to update my db table after reading data from db tables and solr. I am using asyn.waterfall module. The problem is that the script is not getting exited after successful completion of all operations. I have used db connection pool also thinking that may be creating the script to wait infinitly. I want to put this script in crontab and if it will not exit properly it would be creating a hell lot of instances unnecessarily.

Vickrant
  • 1,233
  • 2
  • 9
  • 15

5 Answers5

81

I just went through this issue.

The problem with just using process.exit() is that the program I am working on was creating handles, but never destroying them.

It was processing a directory and putting data into orientdb.

so some of the things that I have come to learn is that database connections need to be closed before getting rid of the reference. And that process.exit() does not solve all cases.

When my project processed 2,000 files. It would get down to about 500 left, and the extra handles would have filled up the available working memory. Which means it would not be able to continue. Therefore never reaching the process.exit at the end.

On the other hand, if you close the items that are requesting the app to stay open, you can solve the problem at its source.

The two "Undocumented Functions" that I was able to use, were

process._getActiveHandles();
process._getActiveRequests();

I am not sure what other functions will help with debugging these types of issues, but these ones were amazing.

They return an array, and you can determine a lot about what is going on in your process by using these methods.

starball
  • 20,030
  • 7
  • 43
  • 238
The Lazy Coder
  • 11,560
  • 4
  • 51
  • 69
  • 6
    Thanks, this was exactly what I needed to solve this problem. Solutions like calling `process.exit();` are hacks around the fact that you have some kind of active event listener, timeout, etc... These functions allow you to actually track them down and write appropriate cleanup code! – Steven May 25 '18 at 03:04
  • 8
    I would suggest to use an NPM module like [wtfnode](https://github.com/myndzi/wtfnode), [why-is-node-running](https://github.com/mafintosh/why-is-node-running) or [active-handles](https://www.npmjs.com/package/active-handles) to determine which piece of code is causing the issue. – cwouter Feb 25 '19 at 11:14
  • Doesn't work in TS 4.4.2. Or am I doing something wrong? – Konrad Gałęzowski Sep 16 '21 at 09:19
  • @KonradGałęzowski they are undocumented, you'll get TypeScript errors but those errors are just because TypeScript doesn't know that they are there. They work just fine. If you want to squash the TypeScript errors you'll have to augment the Node.js modules with these functions but it's not necessary if you're just going to use them to debug, just don't type them incorrectly. – John Leidegren Nov 19 '21 at 10:13
40

You have to tell it when you're done, by calling

process.exit();

More specifically, you'll want to call this in the callback from async.waterfall() (the second argument to that function). At that point, all your asynchronous code has executed, and your script should be ready to exit.

EDIT: As pointed out by @Aaron below, this likely has to do with something like a database connection being active, and not allowing the node process to end.

awinder
  • 513
  • 4
  • 4
  • 14
    It is not because of asynchronous routines, as evidenced by the fact that the program `setTimeout(function(){}, 1000)` terminates after 1 second. Node is absolutely smart enough to tell when you're done if you do asynchronous things. The problem is more likely because the db connection (a possible source of future events) is left open. – Aaron Dufour Feb 17 '14 at 22:30
  • 1
    process.exit() seems to be the most obvious thing to do. But why is it required to call it explicitly? Script should exit on its own – Vickrant Feb 18 '14 at 06:19
  • May be I am doing the things in a wrong way,I think I have figured out. Will update on the same let me fix it now..... – Vickrant Feb 18 '14 at 06:20
  • Hi I use process.exit() only and solved my problem by using asyn.each instead of async.waterfall. Because the problem which i was facing with async.waterfall was that the final call back function was getting called a number of times after each iteration so it was not clear when to can I call the process.exit(). And in asyn.each the final callback function is called only once and that was the key to my success. Thanks @awinder – Vickrant Feb 19 '14 at 14:12
  • 1
    @AaronDufour is right, this happens like if you create `redis` node client and does not disconnect at the end. – loretoparisi Jul 20 '16 at 18:03
  • 1
    I'm upvoting for the "edit" about the database connection being active, not the process.exit() part. I didn't realize I had to manually close my connection to mongoDB in order for my node process to end. Now it shuts down on its own as expected. – DustinA Oct 01 '16 at 15:56
34

You can use the node module why-is-node-running:

  1. Run npm install -D why-is-node-running

  2. Add import * as log from 'why-is-node-running'; in your code

  3. When you expect your program to exit, add a log statement:

afterAll(async () => {
  await app.close();
  log();
})

This will print a list of open handles with a stacktrace to find out where they originated:

There are 5 handle(s) keeping the process running
# Timeout
/home/maf/dev/node_modules/why-is-node-running/example.js:6  - setInterval(function () {}, 1000)
/home/maf/dev/node_modules/why-is-node-running/example.js:10 - createServer()

# TCPSERVERWRAP
/home/maf/dev/node_modules/why-is-node-running/example.js:7  - server.listen(0)
/home/maf/dev/node_modules/why-is-node-running/example.js:10 - createServer()
Kim Kern
  • 54,283
  • 17
  • 197
  • 195
  • 1
    Super useful module! I had to import it as `import log from 'why-is-node-running'` with `node --experimental-modules`, but it [did the job](https://github.com/firebase/firebase-admin-node/issues/484)! – Dan Dascalescu Mar 19 '19 at 08:48
  • Alternatively it's possible to run why-is-node-running stand alone (see docs) – user202729 Sep 07 '20 at 03:37
  • 1
    Should there be an additional `)` at the end of the first code snippet? – user202729 Sep 07 '20 at 03:37
  • 1
    If you're calling the script via `node file.js`, it's easier to use: `why-is-node-running file.js` like a debugger tool instead of requiring it in the application itself. – fregante Aug 02 '21 at 07:33
  • Found this very helpful even today. Just that there are CLI methods of calling as well. – Sudeep Sagar Dec 19 '21 at 21:07
  • Note that you must require the package in the beginning of your script. Doing `require("why-is-node-running")()` will display no handles. – SOFe May 04 '23 at 05:35
10

We can quit the execution by using:

connection.destroy();
Lusfikar Sheba
  • 101
  • 1
  • 2
5

If you use Visual Studio code, you can attach to an already running Node script directly from it.

First, run the Debug: Attached to Node Process command:

screenshot of a command palette

When you invoke the command, VS Code will prompt you which Node.js process to attach to:

A quick pick view with Node processes listed

Your terminal should display this message:

Debugger listening on ws://127.0.0.1:9229/<...>
For help, see: https://nodejs.org/en/docs/inspector
Debugger attached.

Debugger listening on ws://127.0.0.1:9229/0f06c9ce-35c1-4b12-95a8-003882ef97dd / For help, see: https://nodejs.org/en/docs/inspector / Debugger attached.

Then, inside your debug console, you can use the code from The Lazy Coder’s answer:

process._getActiveHandles();
process._getActiveRequests();

Debug console

Thai
  • 10,746
  • 2
  • 45
  • 57