Fix memory leak in dev server
This commit is contained in:
Vendored
+46
-48
@@ -4,9 +4,8 @@ import grabDirNames from "../../utils/grab-dir-names";
|
||||
import writeErrorFile from "../../functions/write-error-file";
|
||||
import { existsSync, rmSync } from "fs";
|
||||
const { BUNDLER_CTX_MAP_JSON, SSR_BUNDLER_CTX_MAP_JSON, BUNX_BUNDLER_ERROR_EXIT_FILE, } = grabDirNames();
|
||||
let retries = 0;
|
||||
let timeout;
|
||||
const MAX_RETRIES = 5;
|
||||
const RETRY_RESET_AFTER_MS = 10000;
|
||||
export default function () {
|
||||
return new Command("dev")
|
||||
.description("Run development server")
|
||||
@@ -24,51 +23,50 @@ export default function () {
|
||||
});
|
||||
}
|
||||
async function dev() {
|
||||
clearTimeout(timeout);
|
||||
if (retries >= MAX_RETRIES) {
|
||||
console.error(`Dev server crashed ${MAX_RETRIES} times. Exiting.`);
|
||||
process.exit(1);
|
||||
// A loop, not recursion: each restart would otherwise keep the previous
|
||||
// call (and its subprocess) alive for the supervisor's whole lifetime.
|
||||
let retries = 0;
|
||||
while (true) {
|
||||
if (retries >= MAX_RETRIES) {
|
||||
console.error(`Dev server crashed ${MAX_RETRIES} times. Exiting.`);
|
||||
process.exit(1);
|
||||
}
|
||||
const dev_spawn_file = path.resolve(__dirname, "dev-spawn.ts");
|
||||
const dev_spawn_js_file = path.resolve(__dirname, "dev-spawn.js");
|
||||
const final_spawn_file = existsSync(dev_spawn_js_file)
|
||||
? dev_spawn_js_file
|
||||
: dev_spawn_file;
|
||||
const spawn_options = {
|
||||
cmd: ["bun", final_spawn_file],
|
||||
stdio: ["inherit", "inherit", "inherit"],
|
||||
async onExit(subprocess, exitCode, signalCode, error) {
|
||||
writeErrorFile({ exitCode, error });
|
||||
},
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_ENV: "development",
|
||||
},
|
||||
};
|
||||
const started_at = Date.now();
|
||||
let child_process;
|
||||
try {
|
||||
child_process = Bun.spawn(spawn_options);
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Failed to start dev process:`, error);
|
||||
retries++;
|
||||
continue;
|
||||
}
|
||||
const exited = await child_process.exited;
|
||||
// A null exit means the child was killed by a signal (e.g. Ctrl+C
|
||||
// in the shared terminal), which we must not restart.
|
||||
if (exited == null) {
|
||||
return;
|
||||
}
|
||||
// Restart on any numeric exit (0 included: the server should never
|
||||
// exit cleanly). Only consecutive quick crashes count toward the
|
||||
// limit; a child that ran for a while starts the count over.
|
||||
retries =
|
||||
Date.now() - started_at >= RETRY_RESET_AFTER_MS ? 1 : retries + 1;
|
||||
}
|
||||
const dev_spawn_file = path.resolve(__dirname, "dev-spawn.ts");
|
||||
const dev_spawn_js_file = path.resolve(__dirname, "dev-spawn.js");
|
||||
const final_spawn_file = existsSync(dev_spawn_js_file)
|
||||
? dev_spawn_js_file
|
||||
: dev_spawn_file;
|
||||
const spawn_options = {
|
||||
cmd: ["bun", final_spawn_file],
|
||||
stdio: ["inherit", "inherit", "inherit"],
|
||||
async onExit(subprocess, exitCode, signalCode, error) {
|
||||
writeErrorFile({ exitCode, error });
|
||||
},
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_ENV: "development",
|
||||
},
|
||||
};
|
||||
let dev_process;
|
||||
try {
|
||||
dev_process = Bun.spawn(spawn_options);
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Failed to start dev process:`, error);
|
||||
retries++;
|
||||
timeout = setTimeout(() => {
|
||||
retries = 0;
|
||||
}, 10000);
|
||||
return await dev();
|
||||
}
|
||||
const exited = await dev_process.exited;
|
||||
// Restart on any numeric exit (0 included: the server should never exit
|
||||
// cleanly). A null exit means the child was killed by a signal (e.g.
|
||||
// Ctrl+C in the shared terminal), which we must not restart.
|
||||
if (exited != null) {
|
||||
retries++;
|
||||
timeout = setTimeout(() => {
|
||||
retries = 0;
|
||||
}, 10000);
|
||||
return await dev();
|
||||
}
|
||||
timeout = setTimeout(() => {
|
||||
retries = 0;
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
Vendored
+46
-47
@@ -4,9 +4,8 @@ import writeErrorFile from "../../functions/write-error-file";
|
||||
import grabDirNames from "../../utils/grab-dir-names";
|
||||
import { existsSync, rmSync } from "fs";
|
||||
const { BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames();
|
||||
let retries = 0;
|
||||
let timeout;
|
||||
const MAX_RETRIES = 5;
|
||||
const RETRY_RESET_AFTER_MS = 10000;
|
||||
export default function () {
|
||||
return new Command("start")
|
||||
.description("Start production server")
|
||||
@@ -18,50 +17,50 @@ export default function () {
|
||||
});
|
||||
}
|
||||
async function start() {
|
||||
clearTimeout(timeout);
|
||||
if (retries >= MAX_RETRIES) {
|
||||
console.error(`Production server crashed ${MAX_RETRIES} times. Exiting.`);
|
||||
process.exit(1);
|
||||
// A loop, not recursion: each restart would otherwise keep the previous
|
||||
// call (and its subprocess) alive for the supervisor's whole lifetime.
|
||||
let retries = 0;
|
||||
while (true) {
|
||||
if (retries >= MAX_RETRIES) {
|
||||
console.error(`Production server crashed ${MAX_RETRIES} times. Exiting.`);
|
||||
process.exit(1);
|
||||
}
|
||||
const prod_spawn_file = path.resolve(__dirname, "prod-spawn.ts");
|
||||
const prod_spawn_js_file = path.resolve(__dirname, "prod-spawn.js");
|
||||
const final_spawn_file = existsSync(prod_spawn_js_file)
|
||||
? prod_spawn_js_file
|
||||
: prod_spawn_file;
|
||||
const spawn_options = {
|
||||
cmd: ["bun", final_spawn_file],
|
||||
stdio: ["inherit", "inherit", "inherit"],
|
||||
onExit(subprocess, exitCode, signalCode, error) {
|
||||
writeErrorFile({ exitCode, error });
|
||||
},
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_ENV: "production",
|
||||
},
|
||||
};
|
||||
const started_at = Date.now();
|
||||
let child_process;
|
||||
try {
|
||||
child_process = Bun.spawn(spawn_options);
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Failed to start production process:`, error);
|
||||
retries++;
|
||||
continue;
|
||||
}
|
||||
const exited = await child_process.exited;
|
||||
// A null exit means the child was killed by a signal (e.g. Ctrl+C
|
||||
// in the shared terminal), which we must not restart.
|
||||
if (exited == null) {
|
||||
return;
|
||||
}
|
||||
// Restart on any numeric exit (0 included: the server should never
|
||||
// exit cleanly). Only consecutive quick crashes count toward the
|
||||
// limit; a child that ran for a while starts the count over.
|
||||
retries =
|
||||
Date.now() - started_at >= RETRY_RESET_AFTER_MS ? 1 : retries + 1;
|
||||
}
|
||||
const prod_spawn_file = path.resolve(__dirname, "prod-spawn.ts");
|
||||
const prod_spawn_js_file = path.resolve(__dirname, "prod-spawn.js");
|
||||
const final_spawn_file = existsSync(prod_spawn_js_file)
|
||||
? prod_spawn_js_file
|
||||
: prod_spawn_file;
|
||||
const spawn_options = {
|
||||
cmd: ["bun", final_spawn_file],
|
||||
stdio: ["inherit", "inherit", "inherit"],
|
||||
onExit(subprocess, exitCode, signalCode, error) {
|
||||
writeErrorFile({ exitCode, error });
|
||||
},
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_ENV: "production",
|
||||
},
|
||||
};
|
||||
let dev_process;
|
||||
try {
|
||||
dev_process = Bun.spawn(spawn_options);
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`Failed to start production process:`, error);
|
||||
retries++;
|
||||
timeout = setTimeout(() => {
|
||||
retries = 0;
|
||||
}, 10000);
|
||||
return await start();
|
||||
}
|
||||
const exited = await dev_process.exited;
|
||||
// Restart on any numeric exit (0 included). A null exit means the child
|
||||
// was killed by a signal, which we must not restart.
|
||||
if (exited != null) {
|
||||
retries++;
|
||||
timeout = setTimeout(() => {
|
||||
retries = 0;
|
||||
}, 10000);
|
||||
return await start();
|
||||
}
|
||||
timeout = setTimeout(() => {
|
||||
retries = 0;
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user