Database improvements: - Enable WAL mode for better concurrent read/write access - Add busy_timeout (30s) to wait for locked database - Add synchronous=NORMAL for balanced safety/performance - Configure check_same_thread=False for thread safety Logging improvements: - Add retry logic (3 attempts) for database locked errors - Silently skip logging on persistent failures to avoid spam - Properly rollback failed transactions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
69 lines
1.9 KiB
Python
69 lines
1.9 KiB
Python
# app/core/database.py
|
|
"""
|
|
Database configuration and session management.
|
|
|
|
This module provides classes and functions for:
|
|
- Database engine creation and configuration
|
|
- Session management with connection pooling
|
|
- Database dependency for FastAPI routes
|
|
"""
|
|
|
|
import logging
|
|
|
|
from sqlalchemy import create_engine, event
|
|
from sqlalchemy.orm import declarative_base, sessionmaker
|
|
|
|
from .config import settings
|
|
|
|
|
|
def _configure_sqlite_connection(dbapi_connection, connection_record):
|
|
"""Configure SQLite connection for better concurrency.
|
|
|
|
- WAL mode: Allows concurrent reads during writes
|
|
- busy_timeout: Wait up to 30 seconds if database is locked
|
|
- synchronous=NORMAL: Balance between safety and performance
|
|
"""
|
|
cursor = dbapi_connection.cursor()
|
|
cursor.execute("PRAGMA journal_mode=WAL")
|
|
cursor.execute("PRAGMA busy_timeout=30000")
|
|
cursor.execute("PRAGMA synchronous=NORMAL")
|
|
cursor.close()
|
|
|
|
|
|
# Create engine with SQLite-specific configuration
|
|
engine_kwargs = {}
|
|
|
|
# Add SQLite-specific settings for better concurrent access
|
|
if settings.database_url.startswith("sqlite"):
|
|
engine_kwargs["connect_args"] = {"check_same_thread": False}
|
|
|
|
engine = create_engine(settings.database_url, **engine_kwargs)
|
|
|
|
# Configure SQLite pragmas on connection
|
|
if settings.database_url.startswith("sqlite"):
|
|
event.listen(engine, "connect", _configure_sqlite_connection)
|
|
|
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
|
|
Base = declarative_base()
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def get_db():
|
|
"""
|
|
Database session dependency for FastAPI routes.
|
|
|
|
Yields a database session and ensures proper cleanup.
|
|
Handles exceptions and rolls back transactions on error.
|
|
"""
|
|
db = SessionLocal()
|
|
try:
|
|
yield db
|
|
except Exception as e:
|
|
logger.error(f"Database session error: {e}")
|
|
db.rollback()
|
|
raise
|
|
finally:
|
|
db.close()
|