Jul 31 - Fix database issue which leads to the app dies
This commit is contained in:
@@ -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)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user