Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dev-forward-fresh-connection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eve": patch
---

`eve dev` no longer answers an occasional request with 503 `socket hang up` or `read ECONNRESET` under heavy CPU load. Each request now reaches the dev worker on its own connection instead of a pooled one the worker may have just closed.
34 changes: 33 additions & 1 deletion packages/eve/scripts/vendor-compiled/env-runner.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,38 @@ const EVE_SHUTDOWN_SOURCE =
const ENV_RUNNER_EXIT_SOURCE = 'parentPort?.postMessage({ event: "exit" });';
const EVE_EXIT_SOURCE = `${ENV_RUNNER_EXIT_SOURCE}\n parentPort?.close();`;

// env-runner proxies through httpxy's shared keep-alive agent, which never
// expires idle sockets. A busy parent can then reuse one the worker already
// closed and fail the forward (#5001). `agent: false` opens a fresh loopback
// connection per request instead.
const ENV_RUNNER_FETCH_SOURCE =
"return proxyFetch(this._address, this._resolveFetchInput(input), init);";
const EVE_FETCH_SOURCE =
"return proxyFetch(this._address, this._resolveFetchInput(input), init, { agent: false });";

function createFreshWorkerConnectionPlugin() {
let patched = false;

return {
name: "eve-env-runner-fresh-worker-connection",
transform(code, id) {
if (!id.replaceAll("\\", "/").endsWith("/_chunks/common-base-runner.mjs")) {
return null;
}
if (!code.includes(ENV_RUNNER_FETCH_SOURCE)) {
throw new Error("env-runner's worker fetch contract changed.");
}
patched = true;
return { code: code.replace(ENV_RUNNER_FETCH_SOURCE, EVE_FETCH_SOURCE), map: null };
},
buildEnd() {
if (!patched) {
throw new Error("env-runner's worker fetch was not patched.");
}
},
};
}

function createGracefulNodeWorkerShutdownPlugin() {
let patched = false;

Expand Down Expand Up @@ -41,7 +73,7 @@ function createGracefulNodeWorkerShutdownPlugin() {
export default {
packageName: "env-runner",
compiledPath: "env-runner",
plugins: [createGracefulNodeWorkerShutdownPlugin()],
plugins: [createFreshWorkerConnectionPlugin(), createGracefulNodeWorkerShutdownPlugin()],
entries: [
{
entry: "dist/_chunks/common-base-runner.mjs",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ describe("drained Nitro dev server", () => {

// #5001: a busy parent can hand the next request to a pooled worker socket
// the worker already closed on its keep-alive timeout.
it.fails("forwards a request after the parent stalls past the worker keep-alive timeout", async () => {
it("forwards a request after the parent stalls past the worker keep-alive timeout", async () => {
const appRoot = await mkdtemp(join(tmpdir(), "eve-dev-keep-alive-"));
const entry = join(appRoot, "worker.mjs");
await writeFile(entry, 'export default { fetch: () => new Response("worker") };\n', "utf8");
Expand Down
Loading