Compare commits
2 Commits
955aab2152
...
891b08ecd8
| Author | SHA1 | Date | |
|---|---|---|---|
| 891b08ecd8 | |||
| 302ef63485 |
|
|
@ -415,7 +415,7 @@ function statusLabel(job: CronJob): string {
|
||||||
function formatDuration(ms?: number): string {
|
function formatDuration(ms?: number): string {
|
||||||
if (!ms) return '-'
|
if (!ms) return '-'
|
||||||
if (ms < 1000) return `${ms}ms`
|
if (ms < 1000) return `${ms}ms`
|
||||||
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`
|
if (ms < 60000) return `${Math.round(ms / 1000)}s`
|
||||||
return `${Math.round(ms / 60000)}m`
|
return `${Math.round(ms / 60000)}m`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -583,30 +583,19 @@ async function runApp(dir: string, port: number) {
|
||||||
|
|
||||||
app.proc = proc
|
app.proc = proc
|
||||||
|
|
||||||
// Check if process is alive using ps(1) - more reliable than Bun's API
|
|
||||||
const isProcessAlive = async (pid: number): Promise<boolean> => {
|
|
||||||
try {
|
|
||||||
const ps = Bun.spawn(['ps', '-p', String(pid)], { stdout: 'pipe', stderr: 'pipe' })
|
|
||||||
const code = await ps.exited
|
|
||||||
return code === 0
|
|
||||||
} catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Poll to verify app started - tries /ok for HTTP apps, falls back to survival check
|
// Poll to verify app started - tries /ok for HTTP apps, falls back to survival check
|
||||||
const pollStartup = async () => {
|
const pollStartup = async () => {
|
||||||
const pollInterval = 500
|
const pollInterval = 500
|
||||||
const survivalThreshold = 5000 // Consider non-HTTP apps running after 5s
|
const survivalThreshold = 5000 // Consider non-HTTP apps running after 5s
|
||||||
const startTime = Date.now()
|
const startTime = Date.now()
|
||||||
const pid = proc.pid
|
|
||||||
|
// Use proc.exited as the authoritative death signal instead of ps(1)
|
||||||
|
let processExited = false
|
||||||
|
proc.exited.then(() => { processExited = true })
|
||||||
|
|
||||||
while (app.state === 'starting' && app.proc === proc) {
|
while (app.state === 'starting' && app.proc === proc) {
|
||||||
// First check if process is still alive
|
if (processExited) {
|
||||||
const alive = await isProcessAlive(pid)
|
|
||||||
if (!alive) {
|
|
||||||
info(app, 'Process died during startup')
|
info(app, 'Process died during startup')
|
||||||
// proc.exited handler will clean up
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -636,6 +625,13 @@ async function runApp(dir: string, port: number) {
|
||||||
|
|
||||||
// If process survived long enough, consider it running (non-HTTP app)
|
// If process survived long enough, consider it running (non-HTTP app)
|
||||||
if (Date.now() - startTime >= survivalThreshold) {
|
if (Date.now() - startTime >= survivalThreshold) {
|
||||||
|
// One final check — process could have died between loop iterations
|
||||||
|
// Yield to let proc.exited handler run if pending
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 0))
|
||||||
|
if (processExited) {
|
||||||
|
info(app, 'Process died during startup')
|
||||||
|
return
|
||||||
|
}
|
||||||
info(app, 'No /ok endpoint, marking as running (process survived 5s)')
|
info(app, 'No /ok endpoint, marking as running (process survived 5s)')
|
||||||
markAsRunning(app, port, false)
|
markAsRunning(app, port, false)
|
||||||
return
|
return
|
||||||
|
|
@ -787,35 +783,10 @@ function startHealthChecks(app: App, port: number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function startProcessHealthChecks(app: App) {
|
function startProcessHealthChecks(app: App) {
|
||||||
// For non-HTTP apps, just verify process is still alive using ps(1)
|
// For non-HTTP apps, the proc.exited handler is the authoritative death signal.
|
||||||
app.healthCheckTimer = setInterval(async () => {
|
// No need to poll — when the process dies, proc.exited fires and cleans up.
|
||||||
if (app.state !== 'running') {
|
// This is a no-op; health checks only matter for HTTP apps where the process
|
||||||
if (app.healthCheckTimer) {
|
// can be alive but the server unresponsive.
|
||||||
clearInterval(app.healthCheckTimer)
|
|
||||||
app.healthCheckTimer = undefined
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const pid = app.proc?.pid
|
|
||||||
if (!pid) {
|
|
||||||
handleHealthCheckFailure(app)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const ps = Bun.spawn(['ps', '-p', String(pid)], { stdout: 'pipe', stderr: 'pipe' })
|
|
||||||
const code = await ps.exited
|
|
||||||
if (code === 0) {
|
|
||||||
// Process is alive
|
|
||||||
app.consecutiveHealthFailures = 0
|
|
||||||
} else {
|
|
||||||
handleHealthCheckFailure(app)
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
handleHealthCheckFailure(app)
|
|
||||||
}
|
|
||||||
}, HEALTH_CHECK_INTERVAL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function startShutdownTimeout(app: App) {
|
function startShutdownTimeout(app: App) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user