From f2fb2b640222e49ed4046db0005863f8c0732e86 Mon Sep 17 00:00:00 2001 From: Arie Peterson <arie@greenhost.nl> Date: Tue, 24 Jan 2023 16:47:44 +0100 Subject: [PATCH] Do not run initialization code concurrently --- backend/app.py | 52 ++++++++++++++++++++++++---------------- backend/requirements.txt | 1 + 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/backend/app.py b/backend/app.py index ec18e9b6..62217372 100644 --- a/backend/app.py +++ b/backend/app.py @@ -3,6 +3,7 @@ from flask_cors import CORS from flask_jwt_extended import JWTManager import flask_migrate from jsonschema.exceptions import ValidationError +from NamedAtomicLock import NamedAtomicLock from werkzeug.exceptions import BadRequest # These imports are required @@ -68,27 +69,36 @@ app.logger.info("Starting dashboard backend.") cors = CORS(app) db.init_app(app) -with app.app_context(): - # We have reset the alembic migration history at Stackspin version 2.2. - # This checks whether we need to prepare the database to follow that - # change. - migration_reset.reset() -flask_migrate.Migrate(app, db) -try: - with app.app_context(): - flask_migrate.upgrade() -except Exception as e: - app.logger.info(f"upgrade failed: {type(e)}: {e}") - sys.exit(2) - -# We need this app context in order to talk the database, which is managed by -# flask-sqlalchemy, which assumes a flask app context. -with app.app_context(): - # Load the list of apps from a configmap and store any missing ones in the - # database. - cluster_config.populate_apps() - # Same for the list of oauthclients. - cluster_config.populate_oauthclients() + +# We'll now perform some initialization routines. Because these have to be done +# once at startup, not for every gunicorn worker, we take a machine-wide lock +# for this. +init_lock = NamedAtomicLock('dashboard_init') +if init_lock.acquire(): + try: + with app.app_context(): + # We have reset the alembic migration history at Stackspin version 2.2. + # This checks whether we need to prepare the database to follow that + # change. + migration_reset.reset() + flask_migrate.Migrate(app, db) + try: + with app.app_context(): + flask_migrate.upgrade() + except Exception as e: + app.logger.info(f"upgrade failed: {type(e)}: {e}") + sys.exit(2) + + # We need this app context in order to talk the database, which is managed by + # flask-sqlalchemy, which assumes a flask app context. + with app.app_context(): + # Load the list of apps from a configmap and store any missing ones in the + # database. + cluster_config.populate_apps() + # Same for the list of oauthclients. + cluster_config.populate_oauthclients() + finally: + init_lock.release() app.register_blueprint(api_v1) app.register_blueprint(web) diff --git a/backend/requirements.txt b/backend/requirements.txt index 9a625388..05715b0e 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -19,6 +19,7 @@ jinja2-base64-filters==0.1.4 kubernetes==24.2.0 MarkupSafe==2.1.1 mypy-extensions==0.4.3 +NamedAtomicLock==1.1.3 oauthlib==3.2.0 pathspec==0.9.0 platformdirs==2.5.1 -- GitLab