fix: resolve database and session initialization issues
- 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 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e # Exit on any error
|
set -e # Exit on any error
|
||||||
|
|
||||||
|
# Ensure data directory exists
|
||||||
|
mkdir -p /app/data
|
||||||
|
|
||||||
echo "Running database migrations..."
|
echo "Running database migrations..."
|
||||||
if uv run alembic upgrade head; then
|
if uv run alembic upgrade head; then
|
||||||
echo "Database migrations completed successfully"
|
echo "Database migrations completed successfully"
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ This module configures Alembic to work with the Flask application
|
|||||||
and SQLAlchemy models.
|
and SQLAlchemy models.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
from logging.config import fileConfig
|
from logging.config import fileConfig
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from alembic import context
|
from alembic import context
|
||||||
|
from flask import Flask
|
||||||
# Import Flask app and database before configuring Alembic
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
from src.app import create_app, db
|
|
||||||
|
|
||||||
# this is the Alembic Config object, which provides
|
# this is the Alembic Config object, which provides
|
||||||
# access to the values within the .ini file in use.
|
# 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:
|
if config.config_file_name is not None:
|
||||||
fileConfig(config.config_file_name)
|
fileConfig(config.config_file_name)
|
||||||
|
|
||||||
# Configure database URL from Flask config
|
# Create minimal Flask app for migrations (without session initialization)
|
||||||
app = create_app()
|
# 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"])
|
config.set_main_option("sqlalchemy.url", app.config["SQLALCHEMY_DATABASE_URI"])
|
||||||
|
|
||||||
# Add your model's MetaData object here for 'autogenerate' support
|
# Add your model's MetaData object here for 'autogenerate' support
|
||||||
|
|||||||
@@ -51,8 +51,10 @@ def create_app(config_name: str | None = None) -> Flask:
|
|||||||
bcrypt.init_app(app)
|
bcrypt.init_app(app)
|
||||||
csrf.init_app(app)
|
csrf.init_app(app)
|
||||||
|
|
||||||
# Initialize session with SQLAlchemy backend
|
# Initialize session with filesystem backend
|
||||||
app.config["SESSION_SQLALCHEMY"] = db
|
# 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)
|
session.init_app(app)
|
||||||
|
|
||||||
# Register blueprints
|
# Register blueprints
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ class Config:
|
|||||||
)
|
)
|
||||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||||
|
|
||||||
# Session management
|
# Session management (filesystem-based to avoid race conditions with SQLAlchemy)
|
||||||
SESSION_TYPE = "sqlalchemy"
|
SESSION_TYPE = "filesystem"
|
||||||
|
SESSION_FILE_DIR = DATA_DIR / "sessions"
|
||||||
SESSION_PERMANENT = True
|
SESSION_PERMANENT = True
|
||||||
SESSION_USE_SIGNER = True
|
SESSION_USE_SIGNER = True
|
||||||
SESSION_KEY_PREFIX = "sk:"
|
SESSION_KEY_PREFIX = "sk:"
|
||||||
@@ -90,6 +91,10 @@ class TestConfig(Config):
|
|||||||
# Use in-memory database for tests
|
# Use in-memory database for tests
|
||||||
SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:"
|
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
|
# Disable CSRF for easier testing
|
||||||
WTF_CSRF_ENABLED = False
|
WTF_CSRF_ENABLED = False
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user