S
SolidJS16mo ago
Martnart

Building with SSR false starts (and doesn't close) listeners

In my app I create a WebsocketServer on Port 3001. If I run npm run build with default SSR setting (ssr: true) everything works like a charm. However, turning off ssr, the WebsocketServer gets started and not closed => EADDRINUSE error. Cannot run dev or start afterwards. After forcefully terminating the process, I can run start with the built files and it works. Any suggestions on how to avoid this?
10 Replies
Martnart
MartnartOP16mo ago
I found the problem. In the build step for spaClient a shell is spawned as a child_process. This shell then runs the vite command, creating a subprocess within a child_process. On kill of the shell, the SIGTERM does not get forwarded into the Vite process, therefore there is no graceful shutdown. I've created a somewhat unsatisfying PR against the solid-start repo. https://github.com/solidjs/solid-start/pull/1033 Unsatisfying in that it only fixes the issue for Linux afaict. Anyone reading this who would like to help out with this PR please feel free to contribute 🙏
Edivado
Edivado16mo ago
Left a suggestion on the PR.
Martnart
MartnartOP16mo ago
Thank you! I saw it and that's kind of what I was hoping for 🙂 Great work. Would you like to open a new PR from your repo and I'll close the current one as superseded?
Edivado
Edivado16mo ago
Sure but If you could help me understand / reproduce the issue that would be cool. I don't quite know how to reproduce EADDRINUSE on my Windows machine. Any hint/steps woulb be nice. Like where do I need to add the long running listener or was this referring to vite?
Martnart
MartnartOP16mo ago
Of course, I think with these simple steps it should be easily reproducible: 1. install ws package 2. in entry-server.tsx add this code:
import { WebSocketServer } from 'ws'

const ws = new WebSocketServer({ port: 3001 }) // could be any port, but having a static port is important, since it would occupy this specific port
import { WebSocketServer } from 'ws'

const ws = new WebSocketServer({ port: 3001 }) // could be any port, but having a static port is important, since it would occupy this specific port
This should already do it. With this in place, dev works normally actually. So when you interrupt the process, the port should be released. But running build spawns the process that never ends. From here, start, dev or even a new build all fail. In my case we have the Socket logic in a different file and import it into entry-server but I guess for an easy reproduction this step is not relevant. Edit: there probably are even easier ways to reproduce this behavior, I'm just going with what caused the issue in our case. Edit2: I forgot to mention but it should be obvious: set solid({ ssr: false }) in vite.config The whole picture comes together when you actually have a graceful shutdown in place. I'm not sure what the default behavior here is, actually. But again for this simple scenario it should suffice to add
process.on('SIGTERM', () => {
ws.close()
process.exit()
})
process.on('SIGTERM', () => {
ws.close()
process.exit()
})
New development: the PR was just now merged as is. This does satisfy my specific use-case. But I would like to encourage you to go ahead to open a separate PR
Edivado
Edivado16mo ago
Thats good, better then waiting and I want to reproduce the error first. Thanks for the detailed steps I will try it out.
Martnart
MartnartOP16mo ago
Tbh I wish I could have brought a solution like yours to the table, but I just don't feel comfortable enough to go beyond a single digit line change. 😄 I think your work would really bring a positive outcome for countless other devs who might face this issue 🙂 Regardless of the platform they're using
Edivado
Edivado16mo ago
My suggestion has a similar problem which I expected after seeing the server initialization. Building works but the websocket server will block until its explicitly closed (ctrl+c). 🤣 Regarding this: Edit: as a side note I was also able to "fix" this by changing the stdio option. But in this specific case they bring their own bugs Was this with detached set to true? I didn't notice any difference by just changing stdio. Anyway... in this specific case I don't see any other solution beside what you did and try to terminate all related processes. Only thing I see avoiding this is providing an index.html in the project root so the build process does not run entry-server.
Martnart
MartnartOP16mo ago
No this was with settings "as-is", without detached option. Unfortunately, this doesn't actually fix the problem on Windows, though. I also thought avoiding a shell intermediate process would probably be the best way to go, but seeing that spaClient is the only build option that has that, I assumed there's a reason for why it is needed. :/ If there is a way along the lines you propose, I guess it would be the best after all Edit: ahh wait, I misunderstood. I meant avoiding the shell like you did in your PR. I guess for a proper server build to go through, there is no way to circumvent entry-server it's basically the prime (and only) entrypoint where you can initialize stuff on the server. Without that it would most likely break the functionality of the server runtime altogether. Btw, unrelated: I just wanted to quickly thank you for being such an active contributor. I noticed that you had your hands in a lot of changes recently and I, as a surrogate for the whole community, would like to voice my appreciation 🙂 💪
Edivado
Edivado16mo ago
Aww I don't feel like I have done much but totally appreciate the kind words. 😄
Regarding the current spa build process. The longer I think about it and look at the code it's starting to seem insane to call the server (through vite dev or directly) to generate the index.html. At the end its just the entry point for the client build (single script tag pointing to entry-client). I feel like I am missing something but it looks to me as if generating the index.html from a hardcoded string would be the same. It doesn't seem like its creating anything dynamically or using something provided by the user. nvm I kind of understand now why it is like that. I am also not too sure about creating the websocket server in entry-server but thats probably me not knowing the use case. My first thought would be to run it separately or trying to run it as a middleware if possible.
Want results from more Discord servers?
Add your server