tsc-watch: Child processes that don't die after SIGTERM are orphaned

I’m using tsc-watch with a command like this:

tsc-watch --pretty --preserveWatchOutput --onSuccess "node build"

In my application code I am handling SIGTERM like this:

process.on('SIGTERM', ...)

In that code I’ve discovered a bug which can result in the process never closing, never exiting. When this happens tsc-watch seems to just lose track of the process and not care about what happens. Later, if tsc-watch itself is killed that orphaned process continues running in the background forever.

It would be nice if tsc-watch would wait for the process to actually exit for a period of time, say 3 seconds? And if at that point it is still open then for it to send a second signal, say SIGQUIT?

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 7
  • Comments: 28 (9 by maintainers)

Commits related to this issue

Most upvoted comments

I came across an issue today that seems to be related to this one, although slightly different.

In my case, the process does die in response to SIGTERM, but sometimes it can take a while. I haven’t yet looked into the reason for the delay; maybe the process is doing some cleanup before it exits.

The problem is that if two “compilation complete” events happen in quick succession, the killProcesses() call from the first one may not have finished by the time the second one arrives. This means that killProcesses() will happen twice on the same process (with the second time being effectively a no-op) and then runOnCompilationComplete() will happen twice as well. So two new processes get started, and the first of them never gets killed.

I decided to have a go at fixing it, and have got a solution that seems to be working well, although I haven’t had a chance to test it thoroughly yet. Before calling runOnCompilationComplete(), I check whether the original event that triggered this has been superseded by a later event. I’ve only implemented this for the “compilation complete” event so far, but I could easily do the other events as well.

@gilamran, if you’re open to it, I’d be happy to tidy up my fix, do some more testing on it, and put together a pull request.

Incidentally, I also think we could improve the maintainability of this code by switching to async/await instead of Promise.then(), and would be happy to help with that too. (If you’re worried about compatibility with older Node versions, we could add a build step that transpiles it down to ES5.)

Of course, my solution doesn’t deal with the case where the process completely ignores SIGTERM, although it should at least stop it from “losing track” of the errant process. I guess a proper fix for that case would involve some sort of grace period (configurable?) and then a SIGKILL, as originally suggested by @justinmchase.

Also, if everything is ok I’ll publish v6 soon.

@WadePeterson published new dev version. you can install it by npm i tsc-watch@dev and you should be ok now

I’m having the same issue as @david-alexander where I end up with events happening too quickly and end up with multiple instances of the app running – it’s very frustrating 😕

Not meaning to complain, though, as this is still a great project and the best way (IMO) to use typescript in development; ts-node is too slow.

Sorry, I’m not familiar with the code base here, but I’ll suggest something like:

  • Do not start new process (--onSuccess COMMAND), until the existing one exits;
  • Only start one process (--onSuccess COMMAND), when there are multiple file changes detected within a short period of time;

I hope that make sense to you

This might be related, when using npm run [...] as your --onSuccess handler you might not get the behaviour you are looking for: https://lisk.io/blog/development/why-we-stopped-using-npm-start-child-processes. This is not an issue when you have the ps command available here because tsc-watch will just kill everything. i beleive that this behaviour should be consistent even when you don’t have ps available. (like in a light weight docker containers)