Repository navigation
Fix /_/health reporting stale degraded status after schema recovers - #73
DennisAlund wants to merge 1 commit into
Conversation
handleHealth() seeded its error from the isolate's remembered lastSchemaFailure() but never cleared it when the live schemaStatus() read succeeded. An isolate that once failed to migrate, then saw the schema actually get applied by another path (a different isolate racing the same cold start, or its own later attempt outside the health/setup exemption), kept answering 503 "degraded" with the old error indefinitely, contradicting its own schema.ready: true in the same response body. health.ts and setup.ts are exempt from the normal per-request ensureSchema() call, so nothing else clears the memo. A live read that reports every migration applied is direct proof the schema is fine right now, so clear the stale error in that case.
|
Flagged, not fixed:
The gap: if D1 is ever reset or rebound to an empty database out-of-band (disaster recovery, a redeploy that reuses the old KV namespace with a fresh D1) while KV still remembers the old target version, I didn't fix this because the correct scope is a judgment call: should the manual retry path ( Generated by Claude Code |
|
Flagged, not fixed: "Add to bundle" modal has no ownership gating on deselect ( Per the authorization model (CLAUDE.md rule 7 / The link page's "Add to bundle" modal doesn't. I didn't fix this because it needs a UX decision (disable the row for bundles the viewer doesn't own? show a lock icon/tooltip explaining why? silently keep it selected and skip it from the diff?) and, more substantially, Generated by Claude Code |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
shrtnr | b024859 | Sep 25 2026, 07:23 PM |
Weekly defect-hunting review. One fix qualified to commit directly; two other findings need developer judgment and are posted as PR comments instead of being fixed here.
Fix:
/_/healthstuck reporting "degraded" after the schema actually recoversWhat was wrong:
handleHealth()(src/api/health.ts) seeded itserrorfield from the isolate's rememberedlastSchemaFailure()before running a liveschemaStatus()read, and never cleared it when that live read succeeded.Impact: An isolate that once failed a migration attempt keeps answering
503 degradedwith the stale error forever, even after the schema is genuinely fully applied (e.g. another isolate raced the same cold start and finished it, or a later attempt outside the health/setup exemption succeeded)./_/healthandPOST /_/setupare both exempt from the normal per-requestensureSchema()call, so nothing else clears the isolate's memo on that path. The response ends up self-contradictory:schema.ready: truealongsidestatus: "degraded". A monitor polling only/_/healthsees a false outage after the database is actually healthy.Fix: when the live
schemaStatus()read succeeds and reports every migration applied, clear the staleerrorbefore deciding the response status — a liveready: trueis direct proof there's no current problem, regardless of what an earlier attempt on this isolate remembered.Test:
src/__tests__/handler/schema-guard.test.ts— new case "recovers once a fresh live read reports the schema ready, even with a failure remembered from before". Simulates a transient failure that sets the isolate's memo, then applies the schema for real viamigrate()directly (bypassingensureSchema(), exactly as a racing isolate would), and asserts/_/healthnow reports200 okinstead of the previously stale503 degraded. Confirmed failing (503 instead of 200) before the fix, passing after.Full suite:
yarn test— 1597 tests passing.yarn tsc --noEmitclean. No changes undersrc/pages/,src/admin/,src/client.ts,src/styles.ts, orsrc/index.tsxadmin routes, soyarn e2eisn't required for this change.Flagged for developer judgment (posted as PR comments, not fixed here)
src/db/migrate.ts—migrate()'s KV fast-path can makePOST /_/setup's documented "forgets the memo and migrates again" recovery a silent no-op if D1 is ever reset or rebound out-of-band while the KV namespace persists. Fixing this means deciding whether the manual retry path should ever bypass the intentional (and explicitly tested) KV cache, and that's a real behavior-boundary call, not a slam-dunk local patch.src/client.ts— the "Add to bundle" modal lets a viewer deselect a bundle they don't own; the server correctly 403s the resulting DELETE, but the UI has no ownership gating (unlike the bundle detail page, which hides "Remove from bundle" for non-owners) and shows a generic failure toast while any other successful adds in the same batch silently persist. A real fix needs a UX decision and a way to expose current-user identity toclient.ts, which none of the pages currently do.🤖 Generated with Claude Code
https://claude.ai/code/session_0181pUqPZvD9NsRhda8LfoAr
Generated by Claude Code