Jul 31 - Fix database issue which leads to the app dies

This commit is contained in:
2026-07-31 10:07:07 -04:00
parent 24d48f5d34
commit a7655810d8
3 changed files with 72 additions and 8 deletions
+50 -8
View File
@@ -314,18 +314,60 @@ def create_app() -> Flask:
# ------------------------------------------------------------------
# Startup initialization (runs under gunicorn and flask run alike)
# ------------------------------------------------------------------
with app.app_context():
try:
create_tables()
update_existing_qr_codes()
except Exception as e:
from extensions import logger_handler as _startup_lh
_startup_lh.logger.error(f"Startup initialization failed: {e}", exc_info=True)
raise
_run_startup_initialization(app)
return app
def _run_startup_initialization(app) -> bool:
"""Run create_tables() + update_existing_qr_codes(), tolerating a DB that
is not up yet.
After a server reboot gunicorn and mysqld start in parallel, so the first
connection attempt can be refused. Retry for a short bounded window, then
boot the app anyway instead of re-raising: a worker that refuses to start
takes the whole site down permanently (supervisor exhausts its start
retries within seconds and gives up), while a booted worker recovers on its
own once MySQL accepts connections — pool_pre_ping discards the dead
connections. Startup work that was skipped is idempotent and runs on the
next successful restart.
"""
from extensions import logger_handler as _startup_lh
attempts = app.config.get('DB_STARTUP_RETRY_ATTEMPTS', 5)
delay = app.config.get('DB_STARTUP_RETRY_DELAY', 3)
last_error = None
for attempt in range(1, attempts + 1):
with app.app_context():
try:
create_tables()
update_existing_qr_codes()
if attempt > 1:
_startup_lh.logger.info(
f"Startup initialization succeeded on attempt {attempt}/{attempts}"
)
return True
except Exception as e:
last_error = e
try:
db.session.rollback()
except Exception:
pass
_startup_lh.logger.warning(
f"Startup initialization attempt {attempt}/{attempts} failed: {e}"
)
if attempt < attempts:
_time.sleep(delay)
_startup_lh.logger.error(
f"Startup initialization failed after {attempts} attempts; starting anyway "
f"so workers can serve once the database recovers: {last_error}",
exc_info=True
)
return False
# ---------------------------------------------------------------------------
# Database initialization helpers (called at startup)
# ---------------------------------------------------------------------------