gilamran/tsc-watch

onSuccess doesn't kill previous processes

chris-schmitz opened this issue · 8 comments

I feel like this is probably me misunderstanding the docs, but I can't quite figure it out so I dont' know if it's me or a bug.

I have a node/typescript project (node version 15.7.0, typescript version 4.1.3) where I'm building a server primarily for websocket communication.

I've added the following script to allow for a typescript build and, on success, a node hosting of the server:

    "dev": "tsc-watch --onSuccess \"node ./dist/server/index.js\""

And I have the following tsconfig.json:

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "moduleResolution": "node",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

When I use this, the first time the compiler runs and node loads it works fine, but on subsequent saves the compiler runs fine, the onSuccess handler is called fine, but when the node process is loaded I get an error already in use for the port:

Screen Shot 2021-01-27 at 9 18 31 AM

From here i have to run an lsof to find the id of the node process and kill it manually.

I find that, from time to time, the previous node process will have been killed successfully, freeing up the board, and the new node process does grab the port successfully, but this has been rare.

I also thought that it may be one of the websocket clients still hanging on to the connection, but the same thing still happens if I just start the process and save twice.

Here's a full look:
tsc watch doesn't kill process

I've used tsc-watch in the past successfully and I don't see other open issues with this problem, so I suspect that there's something I'm doing wrong, but as far as I can tell everything should be fine.

The codebase I'm working on is for a talk I'm going to give, so it's all open source if digging through the code helps at all: https://github.com/chris-schmitz/binary-operations-presentation/tree/main/multi-player-server

Can you try without the WebsocketManager and GameManager maybe something there? just express

BTW: This is very interesting talk! I'm right now implementing multi player server with WS, would be great to see it if it's recorded.

Tried that on Windows and on Mac, I didn't see your issue...
Was I correct by doing: printf "\x07\xff\x00\xff" | websocat -b ws://localhost:3000 ?

Hmm, So I hopped into my index.ts file and commented out both of the managers, started my dev script, saw it load fine, did a save in index.ts without any changes, and got the EADDRINUSE message again.

and yep, the message I'm using is:

 printf "\x07\xff\x00\xff" | websocat -b ws://localhost:3000

I can't stop to try it right now, but I'm going to set up a vanilla typescript/node project, setup tsc-watch and try it there to completely remove my custom code from the equation, because like i said before I've used tsc-watch in this same kind of a setup before without a problem, so I'm sure it's something I'm doing wrong that I can't quite put my finger on. I'll update the thread when I do the test.

And thanks :D yeah this demo codebase has gotten waaaaay bigger than I originally intended and it's def more than what's needed to give the talk, but it's been so much fun building it out that I haven't been able to stop myself :P I think I'll be able to get a couple of talk topics out of it which is good, plus the rgb led matrix that I fabricated is going to end up in my daughter's bedroom so there will be something functional that comes out of all of this madness :P

It's cool to hear you're doing a multi-player server with websockets. I don't know if what I'm doing is right by any means (in fact I don't know that given a real application I would be doing some of the binary stuff I am in this codebase, I'm leaning heavy into it just for the purposes of the talk and my own edification). At my last job I used websockets and mqtt a bunch for art installations and company promotion stuff. It's really cool to do that live and seemingly instantaneous communication. Hopefully your game comes out good!

The talk will def be recorded, I'll shoot you a link :)

Hey, I finally got a chance to do a fresh test of tsc-watch:

tscwatch-working-fine

It's working fine with a simple express rest API example. Now I'm even more sure it's something I goofed up in my other project.

Now that I have this known good isolated version I'm going to start comparing it to my project to see what the difference is. I'll let you know what I find.

I did some testing and I'm going to do a bit more after work, but it seems like it may center around adding source maps in my tsconfig, though I'm not able to definitively prove it yet.

Enabling source maps in the tsconfig just seems to be the only thing I can change where I see the most consistent changes. When they're enabled I get a higher rate of processes not being killed, like almost every change -> reload causes it, but if I disable source maps it almost never happens. I just can't say it's 100% the cause because every once in a while I do get a process hanging on even with source maps disabled.

I did the tests with my existing code, but I also copy/pasted the index file from the known good project into my existing project and confirmed that it's still happening. All of the dependency version numbers between projects match and the only difference in the tsconfig is the source map value:

Screen Shot 2021-02-02 at 8 41 25 AM

So yeah, I feel like I'm closer to knowing what's happening, but still not totally sure.

That's very interesting, maybe typescript is creating another process from the source maps... Keep me posted

So I didn't figure out the issue, but I figured out a get-past-the-issue-so-I-can-keep-building.

  "scripts": {
    "dev": "tsc-watch --onSuccess 'npm run sync&host'",
    "sync&host": "npm run rsync:public && npm run host",
    "host": "npx kill-port 3000 && node ./dist/server/index.js",
    "rsync:public": "rsync -avzhP --exclude '*.ts' src/public/* dist/public"
  },

with npm run dev being the starting point.

The key part is the kill-port node module. When my onSuccess script gets to the point where it needs to restart node, kill-port will kill the process using the port so node can start back up.

It feels pretty heavy handed, but at the moment it works enough to get me going :P

If I have a chance to dig further in after this project is done I'll let you know. If nothing else this can be a workaround for anyone else hitting the same snag.