You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
api: make readiness reflect database availability without restarting on database outages #727
After a database outage, the API continues to report healthy readiness while protected requests fail session lookup. Kubernetes can keep routing traffic to an unusable API.
Priority: High. Evidence: static inspection of upstream 04e41075cafc4c1a99bcb3723b0a8745ee810957 (2026-10-16). The failure scenarios below have not been reproduced in a running cluster.
api/internal/auth/sessions.go:127 performs SQL-backed session and permission lookup for protected requests; store errors become an unauthenticated response.
Start the API with PostgreSQL, interrupt database connectivity, and compare readiness with authenticated API requests. Existing game pods and their Kubernetes controllers are a separate availability domain; this report concerns the panel API.
Recommended fix
Add a separate bounded /readyz check. A minimal connectivity check in the existing router is:
Keep process liveness on /healthz and move the readiness probe to /readyz with a probe timeout longer than the DB-check deadline. Do not restart otherwise healthy API processes during an external database outage. Consider a bounded health-check cache or dedicated connection budget if probe traffic competes with the single SQLite connection.
Distinguish transient store failures from invalid sessions internally and return a generic service-unavailable response where appropriate, without exposing credentials, database addresses, or account existence. Preserve fail-closed authorization. Export dependency availability and recovery signals.
Acceptance criteria
A database outage makes readiness fail within a documented bounded interval while liveness remains healthy.
Recovery restores readiness and authenticated access without requiring an API restart.
A slow connection attempt cannot leave health handlers or probe work accumulating.
Invalid sessions remain unauthorized; DB outages do not grant access or reveal sensitive error details.
CI exercises the PostgreSQL outage/recovery path and the normal SQLite path.
Related: #707 adds PostgreSQL-backed API tests and provides a fixture for this failure-mode coverage.
Thanks, partly confirmed on master: both probes hit /healthz, which never touches the store (api/cmd/main.go:316-318, charts/gameplane/templates/api.yaml:501-502), so readiness doesn't reflect a database outage. Liveness already doesn't consult the database, so the "don't restart on DB outage" part already holds today. During an outage, session-store errors become 401 (api/internal/auth/sessions.go:135-137), which looks like a logout to users.
Go ahead with a PR:
Add /readyz with a bounded PingContext and point the readiness probe at it; keep liveness on /healthz. On SQLite the pool is one connection (api/internal/db/db.go:66), so keep the ping cheap and time-bounded.
Return 503 instead of 401 for transient session-store errors in Authenticate, with no detail in the response body. Access is still denied, but clients can tell an outage from a real logout.
Keep /login and unauthenticated responses generic, as the login-privacy rule requires.
Implemented in #758: bounded database readiness is separate from process liveness, transient session-store failures return a generic 503, and invalid sessions remain unauthorized. The follow-up blocks public /readyz at dashboard nginx. CI covers database outage and recovery without restarting the API.
Activity
ValgulNecron commentedon Oct 20, 2026
Thanks, partly confirmed on master: both probes hit
/healthz, which never touches the store (api/cmd/main.go:316-318,charts/gameplane/templates/api.yaml:501-502), so readiness doesn't reflect a database outage. Liveness already doesn't consult the database, so the "don't restart on DB outage" part already holds today. During an outage, session-store errors become 401 (api/internal/auth/sessions.go:135-137), which looks like a logout to users.Go ahead with a PR:
/readyzwith a boundedPingContextand point the readiness probe at it; keep liveness on/healthz. On SQLite the pool is one connection (api/internal/db/db.go:66), so keep the ping cheap and time-bounded.Authenticate, with no detail in the response body. Access is still denied, but clients can tell an outage from a real logout./loginand unauthenticated responses generic, as the login-privacy rule requires.Baylem commentedon Oct 25, 2026
Implemented in #758: bounded database readiness is separate from process liveness, transient session-store failures return a generic 503, and invalid sessions remain unauthorized. The follow-up blocks public
/readyzat dashboard nginx. CI covers database outage and recovery without restarting the API.