The operator's 500 was NOT the startup gap I attributed it to. They found it by logging in and getting Steward's own error page -- which means create_app had completed and the app was serving, so the gaierror came from a request handler acquiring a connection, not from boot. Neither prior fix covers that: the startup retry never runs, and pool_pre_ping only helps once the database is back, since its replacement connect fails too while the container is gone. Adds a before_request gate that acquires a pooled connection with a short bounded retry (~1.75s over 4 tries) so a database restart is ridden out invisibly, and renders a distinct 503 "database unavailable" page when the budget is exhausted. The request budget is deliberately far shorter than the startup one: nobody watches a container boot, but somebody is watching this page load, and a page that hangs is worse than one that says what is wrong. /health and static are exempt -- a liveness probe must stay answerable while the database is down, or a dependency outage triggers a restart loop. Credential and missing-database errors still propagate rather than being reported as "unavailable", which would send the operator chasing the wrong problem entirely. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
17 lines
888 B
HTML
17 lines
888 B
HTML
{% extends "base.html" %}
|
|
{% block title %}Database unavailable — Steward{% endblock %}
|
|
{% block content %}
|
|
<div style="text-align:center; padding: 4rem 2rem; font-family: var(--font-serif);">
|
|
<h1 style="color: var(--yellow); font-size: 2.5rem; margin-bottom: 0.5rem;">503</h1>
|
|
<p style="color: var(--text); font-size: 1.2rem; margin-bottom: 0.25rem;">The records are out of reach.</p>
|
|
<p style="color: var(--text-dim); margin-bottom: 0.5rem; max-width: 34rem; margin-left:auto; margin-right:auto;">
|
|
Steward is running, but cannot reach its database. Nothing has been lost —
|
|
this page will work again as soon as the database is back.
|
|
</p>
|
|
<p style="color: var(--text-dim); font-size: 0.85rem; margin-bottom: 1.5rem;">
|
|
Steward already retried for a moment before showing this.
|
|
</p>
|
|
<a href="{{ request.path }}" class="btn">Try again</a>
|
|
</div>
|
|
{% endblock %}
|