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}")