fix(preview): zombie process cleanup on anatomy load
This commit is contained in:
@@ -792,7 +792,60 @@ async function loadPreviews(projectId: string): Promise<Preview[]> {
|
|||||||
WHERE project_id = $1 AND state != 'stopped'`,
|
WHERE project_id = $1 AND state != 'stopped'`,
|
||||||
[projectId],
|
[projectId],
|
||||||
);
|
);
|
||||||
return sortDevPreviewsFrontendFirst(rows).map((r) => ({
|
|
||||||
|
// Filter out zombies: if a server is marked 'running' but the URL returns a 50x
|
||||||
|
// Gateway error or times out, the process died. We mark it stopped so the
|
||||||
|
// UI can trigger an auto-restart.
|
||||||
|
const activePreviews: typeof rows = [];
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
rows.map(async (r) => {
|
||||||
|
if (r.state !== "running") {
|
||||||
|
activePreviews.push(r);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeout = setTimeout(() => controller.abort(), 2500); // Fast 2.5s timeout
|
||||||
|
const ping = await fetch(r.preview_url, {
|
||||||
|
method: "HEAD",
|
||||||
|
signal: controller.signal,
|
||||||
|
});
|
||||||
|
clearTimeout(timeout);
|
||||||
|
|
||||||
|
// 502/503/504 means Traefik is up but the container isn't answering.
|
||||||
|
// 404 means Traefik doesn't even know about the route.
|
||||||
|
if (
|
||||||
|
ping.status === 502 ||
|
||||||
|
ping.status === 503 ||
|
||||||
|
ping.status === 504 ||
|
||||||
|
ping.status === 404
|
||||||
|
) {
|
||||||
|
console.warn(
|
||||||
|
`[anatomy] Preview zombie detected for ${r.preview_url} (HTTP ${ping.status}). Marking stopped.`,
|
||||||
|
);
|
||||||
|
await query(
|
||||||
|
`UPDATE fs_dev_servers SET state = 'stopped' WHERE id = $1`,
|
||||||
|
[r.id],
|
||||||
|
).catch(() => {});
|
||||||
|
} else {
|
||||||
|
activePreviews.push(r);
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
// If the fetch completely fails (e.g. timeout, DNS failure), it's dead.
|
||||||
|
console.warn(
|
||||||
|
`[anatomy] Preview zombie detected for ${r.preview_url} (${e.message}). Marking stopped.`,
|
||||||
|
);
|
||||||
|
await query(
|
||||||
|
`UPDATE fs_dev_servers SET state = 'stopped' WHERE id = $1`,
|
||||||
|
[r.id],
|
||||||
|
).catch(() => {});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return sortDevPreviewsFrontendFirst(activePreviews).map((r) => ({
|
||||||
id: r.id,
|
id: r.id,
|
||||||
name: r.name,
|
name: r.name,
|
||||||
command: r.command ?? undefined,
|
command: r.command ?? undefined,
|
||||||
|
|||||||
Reference in New Issue
Block a user