79 lines
2.8 KiB
Python
79 lines
2.8 KiB
Python
import os
|
|
import time
|
|
from typing import Generator
|
|
from pathlib import Path
|
|
|
|
from sqlalchemy import create_engine, event
|
|
from sqlalchemy.orm import sessionmaker
|
|
from sqlalchemy.exc import OperationalError, SQLAlchemyError
|
|
|
|
from app.core.config import settings, DB_DIR
|
|
|
|
# Ensure the database directory exists
|
|
db_file = DB_DIR / "db.sqlite"
|
|
print(f"Database file path: {db_file}")
|
|
|
|
# Configure SQLite connection with more robust settings
|
|
engine = create_engine(
|
|
settings.SQLALCHEMY_DATABASE_URL,
|
|
connect_args={
|
|
"check_same_thread": False,
|
|
"timeout": 30, # Wait up to 30 seconds for the lock
|
|
},
|
|
pool_recycle=3600, # Recycle connections after 1 hour
|
|
pool_pre_ping=True, # Verify connections before usage
|
|
pool_size=10, # Maximum 10 connections
|
|
max_overflow=20, # Allow up to 20 overflow connections
|
|
)
|
|
|
|
# Add SQLite optimizations
|
|
@event.listens_for(engine, "connect")
|
|
def optimize_sqlite_connection(dbapi_connection, connection_record):
|
|
"""Configure SQLite connection for better performance and reliability."""
|
|
# Enable WAL journal mode for better concurrency
|
|
dbapi_connection.execute("PRAGMA journal_mode=WAL")
|
|
# Ensure foreign keys are enforced
|
|
dbapi_connection.execute("PRAGMA foreign_keys=ON")
|
|
# Synchronous setting for better performance with decent safety
|
|
dbapi_connection.execute("PRAGMA synchronous=NORMAL")
|
|
# Cache settings
|
|
dbapi_connection.execute("PRAGMA cache_size=-64000") # 64MB cache
|
|
# Temp storage in memory
|
|
dbapi_connection.execute("PRAGMA temp_store=MEMORY")
|
|
|
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
|
|
# More robust database access with retry logic
|
|
def get_db() -> Generator:
|
|
"""
|
|
Get a database session with retry logic for transient errors.
|
|
Creates the database file and tables if they don't exist.
|
|
"""
|
|
db = None
|
|
retries = 3
|
|
retry_delay = 0.5 # Start with 0.5 second delay
|
|
|
|
for attempt in range(retries):
|
|
try:
|
|
db = SessionLocal()
|
|
# Test connection with a simple query
|
|
db.execute("SELECT 1")
|
|
# Yield the working connection
|
|
yield db
|
|
break
|
|
except (OperationalError, SQLAlchemyError) as e:
|
|
if db:
|
|
db.close()
|
|
|
|
# Only retry transient errors like "database is locked"
|
|
if attempt < retries - 1:
|
|
print(f"Database connection attempt {attempt+1} failed: {e}")
|
|
print(f"Retrying in {retry_delay} seconds...")
|
|
time.sleep(retry_delay)
|
|
retry_delay *= 2 # Exponential backoff
|
|
else:
|
|
print(f"All database connection attempts failed: {e}")
|
|
raise
|
|
finally:
|
|
if db:
|
|
db.close() |