Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server start (next start) uses HTTP to communicate between threads #52702

Closed
1 task done
davidbarratt opened this issue Jul 15, 2023 · 6 comments · Fixed by #54813
Closed
1 task done

Server start (next start) uses HTTP to communicate between threads #52702

davidbarratt opened this issue Jul 15, 2023 · 6 comments · Fixed by #54813
Labels
bug Issue was opened via the bug report template. locked Pages Router Related to Pages Router. Runtime Related to Node.js or Edge Runtime with Next.js.

Comments

@davidbarratt
Copy link

davidbarratt commented Jul 15, 2023

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 22.5.0: Thu Jun  8 22:22:20 PDT 2023; root:xnu-8796.121.3~7/RELEASE_ARM64_T6000
Binaries:
  Node: 18.16.1
  npm: 9.5.1
  Yarn: 1.22.19
  pnpm: N/A
Relevant packages:
  next: 13.4.7
  eslint-config-next: 13.4.7
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.1.6

Which area(s) of Next.js are affected? (leave empty if unsure)

App Router, CLI (create-next-app), Data fetching (gS(S)P, getInitialProps), Middleware / Edge (API routes, runtime)

Link to the code that reproduces this issue or a replay of the bug

next start

To Reproduce

Run next-start and with something to instrument outbound HTTP requests (i.e. New Relic) and you should see something like this:
Screenshot 2023-07-14 at 9 35 22 PM

Describe the Bug

In fdacca8 next start started using workers in order to seperate out the rendering and routing logic into seperate threads for the purposes of isolation. This seems sensible, however, the the mechanism chosen to message between the threads is.... HTTP. While that is a wonderful protocol to message between applications/processes, it incures a significant amount of overhead that is completely unnecessary to communicate between threads of the same process.

Expected Behavior

I expected Next.js to listen to HTTP requests on the single, specified port and use worker.postMessage() to forward a request to a specific worker and then listen for a response with the message event. The main thread will need to keep track of it's sent requests so it can match them up with respones (which may come back in any order). Alternatively, it's probably accaptable to create a new thread for each incoming request, which might be better anyways for handling the high CPU usage of SSR.

If the goal of this code is isolation rather than multithreading, it might be better to use a Node.js VM in which you could pass an event emitter or even the promise resolve callback. This is how Cloudflare isolates Workers from each other.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

@davidbarratt davidbarratt added the bug Issue was opened via the bug report template. label Jul 15, 2023
@github-actions github-actions bot added create-next-app Related to our CLI tool for quickly starting a new Next.js application. Pages Router Related to Pages Router. Runtime Related to Node.js or Edge Runtime with Next.js. labels Jul 15, 2023
@balazsorban44 balazsorban44 removed the create-next-app Related to our CLI tool for quickly starting a new Next.js application. label Jul 15, 2023
@davidbarratt
Copy link
Author

Ping @ijjk

@colonder
Copy link

colonder commented Sep 7, 2023

This has been resolved with next Next.js versions, if you upgrade to the newest one, it works with specifying just one port

@davidbarratt
Copy link
Author

@colonder which change fixed it?

@colonder
Copy link

colonder commented Sep 7, 2023

@davidbarratt When I said "upgrade to the newest one" I meant that previously I used 13.4.6 and now I upgraded to 13.4.19 so I can't really tell which change precisely fixed it. I can check which version is the first one that started working as expected.

@colonder
Copy link

colonder commented Sep 7, 2023

@davidbarratt I found that 13.4.13 is the first stable version that works as expected, anything before that has this issue with an additional random port. That doesn't mean that everything is cool though - some hostings (like cPanel with Phusion Passenger) still report that there have been multiple calls to server.listen() and some kind of proxy is still required. That is still an improvement though since it's much easier to work around having to proxy just one port instead of an additional random one. For anyone coming across this issue, I'm posting below how I implemented such proxy with node-http-proxy (for specific use with Phusion Passenger in this case)

if (typeof(PhusionPassenger) !== 'undefined') {
    PhusionPassenger.configure({ autoInstall: false });
}

const path = require("path");
const httpProxy = require("http-proxy")
const nextPath = path.join(__dirname, "node_modules", ".bin", "next");

// https://www.phusionpassenger.com/library/indepth/nodejs/reverse_port_binding.html
const port = process.env.NEXT_PORT || 3000;
process.env.PORT = port;
// Create proxy server
// MODIFIED!!!
httpProxy.createProxyServer({forward: `http://localhost:${port}`}).listen(typeof(PhusionPassenger) !== 'undefined' ? "passenger": 3000)

// create target server
process.argv.length = 1;
process.argv.push(nextPath, "start");
require(nextPath);

timneutkens added a commit that referenced this issue Sep 11, 2023
Currently we create separate workers to isolate `pages` and `app`
routers due to differing react versions being used between the two. This
adds overhead and complexity in the rendering process which we can avoid
by leveraging an `esm-loader` similar to our `require-hook` to properly
alias `pages` router to the bundled react version to match `app` router
when both are leveraged together.

This aims to seamlessly inject the `esm-loader` by restarting the
process with the loader arg added whenever `next` is imported so that
this also works with custom-servers and fixes the issue with custom
req/res fields not working after upgrading.


x-ref: #53883
x-ref: #54288
x-ref: #54129
x-ref: #54435
closes: #54440
closes: #52702
x-ref: [slack
thread](https://vercel.slack.com/archives/C03KAR5DCKC/p1693348443932499?thread_ts=1693275196.347509&cid=C03KAR5DCKC)

---------

Co-authored-by: Tim Neutkens <tim@timneutkens.nl>
Co-authored-by: Zack Tanner <zacktanner@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
@github-actions
Copy link
Contributor

This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 26, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Issue was opened via the bug report template. locked Pages Router Related to Pages Router. Runtime Related to Node.js or Edge Runtime with Next.js.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants