From b38fc328f9a13054bed1c535bb476bf9637dbeb1 Mon Sep 17 00:00:00 2001 From: Phil Skentelbery Date: Mon, 22 Dec 2025 18:57:57 -0700 Subject: [PATCH] fix: resolve database and session initialization issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Switch from SQLAlchemy sessions to filesystem sessions to avoid race conditions with multiple gunicorn workers trying to create the sessions table simultaneously - Update Alembic env.py to use a minimal Flask app without Flask-Session to prevent session table creation during migrations - Add data directory creation to entrypoint.sh for clean container starts - Configure test environment to use filesystem sessions with temp directory 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- entrypoint.sh | 3 +++ migrations/env.py | 34 +++++++++++++++++++++++++++++----- src/app.py | 6 ++++-- src/config.py | 9 +++++++-- 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 3317bdc..78028b3 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,6 +1,9 @@ #!/bin/bash set -e # Exit on any error +# Ensure data directory exists +mkdir -p /app/data + echo "Running database migrations..." if uv run alembic upgrade head; then echo "Database migrations completed successfully" diff --git a/migrations/env.py b/migrations/env.py index 36a8965..1c8b403 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -4,12 +4,13 @@ This module configures Alembic to work with the Flask application and SQLAlchemy models. """ +import os from logging.config import fileConfig +from pathlib import Path from alembic import context - -# Import Flask app and database before configuring Alembic -from src.app import create_app, db +from flask import Flask +from flask_sqlalchemy import SQLAlchemy # this is the Alembic Config object, which provides # access to the values within the .ini file in use. @@ -20,8 +21,31 @@ config = context.config if config.config_file_name is not None: fileConfig(config.config_file_name) -# Configure database URL from Flask config -app = create_app() +# Create minimal Flask app for migrations (without session initialization) +# This avoids Flask-Session trying to create tables before migrations run +app = Flask(__name__) +BASE_DIR = Path(__file__).parent.parent +DATA_DIR = BASE_DIR / "data" +DATA_DIR.mkdir(parents=True, exist_ok=True) + +app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( + "DATABASE_URL", f"sqlite:///{DATA_DIR / 'sneaky-klaus.db'}" +) +app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False + +db = SQLAlchemy() +db.init_app(app) + +# Import all models to register them with SQLAlchemy metadata +with app.app_context(): + from src.models import ( # noqa: F401 + Admin, + Exchange, + MagicToken, + Participant, + RateLimit, + ) + config.set_main_option("sqlalchemy.url", app.config["SQLALCHEMY_DATABASE_URI"]) # Add your model's MetaData object here for 'autogenerate' support diff --git a/src/app.py b/src/app.py index bbbac19..0b33386 100644 --- a/src/app.py +++ b/src/app.py @@ -51,8 +51,10 @@ def create_app(config_name: str | None = None) -> Flask: bcrypt.init_app(app) csrf.init_app(app) - # Initialize session with SQLAlchemy backend - app.config["SESSION_SQLALCHEMY"] = db + # Initialize session with filesystem backend + # Ensure session directory exists + session_dir = Path(app.config.get("SESSION_FILE_DIR", data_dir / "sessions")) + session_dir.mkdir(parents=True, exist_ok=True) session.init_app(app) # Register blueprints diff --git a/src/config.py b/src/config.py index dfccd38..bc93a25 100644 --- a/src/config.py +++ b/src/config.py @@ -25,8 +25,9 @@ class Config: ) SQLALCHEMY_TRACK_MODIFICATIONS = False - # Session management - SESSION_TYPE = "sqlalchemy" + # Session management (filesystem-based to avoid race conditions with SQLAlchemy) + SESSION_TYPE = "filesystem" + SESSION_FILE_DIR = DATA_DIR / "sessions" SESSION_PERMANENT = True SESSION_USE_SIGNER = True SESSION_KEY_PREFIX = "sk:" @@ -90,6 +91,10 @@ class TestConfig(Config): # Use in-memory database for tests SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:" + # Use filesystem session with temp directory for tests + SESSION_TYPE = "filesystem" + SESSION_FILE_DIR = Path("/tmp/sneaky-klaus-test-sessions") + # Disable CSRF for easier testing WTF_CSRF_ENABLED = False