111 lines
3.7 KiB
Python

import os
import time
import sqlite3
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
# Define database filepath - use DB_DIR from config
db_file = DB_DIR / "db.sqlite"
print(f"Database file path: {db_file}")
# Try to touch the database file to ensure it exists
try:
if not db_file.exists():
# Try to create an empty database file
db_file.touch()
print(f"Database file created or verified: {db_file}")
except Exception as e:
print(f"Warning: Could not create database file: {e}")
# Configure SQLite connection with simplified, 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
},
# Minimal pool settings for stability
pool_pre_ping=True, # Verify connections before usage
echo=True, # Log all SQL for debugging
)
# Add essential SQLite optimizations
@event.listens_for(engine, "connect")
def optimize_sqlite_connection(dbapi_connection, connection_record):
"""Configure SQLite connection for better reliability."""
try:
# These are essential for SQLite stability
dbapi_connection.execute("PRAGMA journal_mode=WAL")
dbapi_connection.execute("PRAGMA synchronous=NORMAL")
except Exception as e:
print(f"Warning: Could not configure SQLite connection: {e}")
# Simplified Session factory
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# More robust database access with retry logic and error printing
def get_db() -> Generator:
"""
Get a database session with retry logic for transient errors.
"""
db = None
retries = 3
for attempt in range(retries):
try:
db = SessionLocal()
# Log connection attempt
print(f"Database connection attempt {attempt+1}")
# Test connection with a simple query
db.execute("SELECT 1")
# Connection succeeded
print("Database connection successful")
yield db
break
except Exception as e:
# Close failed connection
if db:
db.close()
db = None
error_msg = f"Database connection attempt {attempt+1} failed: {e}"
print(error_msg)
# Log critical error details
import traceback
print(f"Error traceback: {traceback.format_exc()}")
# Check if we can directly access database
try:
# Try direct sqlite3 connection as a test
direct_conn = sqlite3.connect(str(db_file))
direct_conn.execute("SELECT 1")
direct_conn.close()
print("Direct SQLite connection succeeded but SQLAlchemy failed")
except Exception as direct_e:
print(f"Direct SQLite connection also failed: {direct_e}")
# Last attempt - raise the error to return 500 status
if attempt == retries - 1:
print("All database connection attempts failed")
raise
# Otherwise sleep and retry
time.sleep(1)
# Always ensure db is closed
try:
if db:
print("Closing database connection")
db.close()
except Exception as e:
print(f"Error closing database: {e}")