fix(idle): serve an auto-refreshing waking page for suspended host-proxy sites#673
Conversation
…oxy sites Idle-suspend stops a host-proxy site's dev server (its "app" worker), but a host-proxy site has no FPM behind the proxy: the dev server is its only request-serving process. With it stopped the proxy vhost points at a dead port, so every request returns 502 from nginx. The "resume on next request" design never recovers gracefully either: the request 502s at nginx before the dev server (often a slow Vite/Nuxt warmup) is back, and nginx's 502 page carries no refresh, so the reload loop is broken. On suspend of a host-proxy site, swap the proxy vhost to an auto-refreshing "waking up" landing page instead of leaving the dead proxy. The access-log hit that loads it already drives idle-resume; on resume, wait for the dev server to accept connections again, then restore the proxy vhost so the page's meta refresh lands on the live app. PHP sites are unaffected (idle-suspend leaves their FPM serving; only background workers stop). The paused and waking vhosts now share landingVhostConf/writeLandingVhost, so GeneratePausedVhost is output-identical to before.
|
This reads well and it mirrors the approach from the host-proxy stop fix nicely, reusing RegenerateSiteVhost for the restore and folding the paused and waking vhosts into a shared landing renderer is the right call. I traced the resume path and the 60s port wait is fine since resume already runs in a background goroutine behind the inFlight guard, so it never stalls the watcher loop and concurrent access hits coalesce. Build, vet, gofmt and the nginx, cli and watcher suites all pass here. Two things before this goes in. There's a stray word in the comment inside restoreHostProxyVhostAfterIdle, the line starts with "ponytail: poll the dev-server port", that looks like an artifact and should just be dropped. And the cli side is a bit light on tests compared to the stop fix, the only cli test is a string check that the waking HTML has a meta refresh, so the host-proxy only gating in swapHostProxyVhostToWaking and restoreHostProxyVhostAfterIdle isn't pinned anywhere. Factoring the decision into a small pure helper the way hostProxyAppLifecycleOp was done in the other PR and table testing it would lock that in. |
…stray word Address review: factor the host-proxy-only gate shared by swapHostProxyVhostToWaking and restoreHostProxyVhostAfterIdle into a pure hostProxyVhostSwapApplies(isHostProxy, workers) helper and table-test it, so the "only a host-proxy site whose suspended/resumed set includes the app worker" decision is pinned. Also drop the stray "ponytail:" prefix from the restore comment.
|
Both addressed in e688cb4:
|
Fixes #672.
Problem
When idle-suspend sleeps a host-proxy site, the site returns 502 Bad Gateway and does not recover gracefully on the next request.
A PHP site keeps serving while idle-suspended: the engine stops only its background workers (queue, horizon) and FPM stays up. A host-proxy site has no FPM, the dev server (the
appworker) is its only request-serving process. Stopping it leaves the proxy vhost pointing at a dead port, so nginx 502s."Resume on next request" can't recover it cleanly either: the request 502s at nginx before the dev server (often a slow Vite/Nuxt warmup) is back, and nginx's 502 page carries no auto-refresh, so the reload loop never lands on the live app.
Fix
On suspend of a host-proxy site, swap its proxy vhost to an auto-refreshing "waking up" landing page instead of leaving the dead proxy:
SuspendWorkersForIdle: when the suspended set includes the host-proxyappworker, writewaking.htmland swap to the waking vhost (GenerateWakingVhost) + reload.ResumeWorkersForIdle: after restarting the dev server, wait (bounded, 60s) for its port to accept connections, then restore the proxy vhost viasiteops.RegenerateSiteVhost+ reload. The wait keeps the auto-refreshing page serving through the dev server's warmup so the reload lands on the live app rather than flashing nginx's refresh-less 502.The access-log hit that loads the waking page already drives idle-resume (the existing
OnActivitypath), so no extra wake wiring is needed. PHP sites are unaffected.The paused and waking vhosts now share
landingVhostConf/writeLandingVhost, soGeneratePausedVhostis output-identical to before.Test
internal/nginx:GenerateWakingVhostserveswaking.html(not a proxy_pass), and on a secured site removes the separate-ssl.conf; a regression test pinsGeneratePausedVhoststill servingpaused.htmlafter the refactor.internal/cli: the waking page carries ameta http-equiv="refresh"so a resumed site reloads on its own.go build,go vet ./internal/cli ./internal/nginx, and thenginx+cli+watchersuites pass.Notes
The port-wait is bounded to 60s and skipped when the proxy has no fixed port (worst case a brief 502 flash on warmup). Scope is the parent host-proxy site; worktree host-proxy dev servers are out of scope here.