| from sqlalchemy import create_engine | |
| from sqlalchemy.ext.declarative import declarative_base | |
| from sqlalchemy.orm import sessionmaker | |
| from .config import settings | |
| # For Azure SQL, we use the DATABASE_URL from .env | |
| # If not provided, fallback to a local sqlite for safety (optional) | |
| SQLALCHEMY_DATABASE_URL = settings.DATABASE_URL or "sqlite:///./temp.db" | |
| # Create engine with stability settings for Azure SQL | |
| engine = create_engine( | |
| SQLALCHEMY_DATABASE_URL, | |
| pool_pre_ping=True, # Check connection health before every query | |
| pool_recycle=1800, # 30 minutes (best practice) | |
| pool_timeout=30, # Wait up to 30 seconds for a connection | |
| pool_size=15, # Maintain a slightly larger pool | |
| max_overflow=25, # Allow more overflow if busy | |
| connect_args={ | |
| "timeout": 30 # 30 second timeout for queries/connections | |
| } | |
| ) | |
| SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) | |
| Base = declarative_base() | |
| def get_db(): | |
| db = SessionLocal() | |
| try: | |
| yield db | |
| finally: | |
| db.close() | |
| def init_db(): | |
| import models.db_models # Ensure models are loaded | |
| import time | |
| from sqlalchemy.exc import DBAPIError | |
| max_retries = 5 | |
| retry_delay = 5 # seconds | |
| for attempt in range(max_retries): | |
| try: | |
| print(f"Connecting to database (Attempt {attempt + 1}/{max_retries})...") | |
| Base.metadata.create_all(bind=engine) | |
| print("Database initialized successfully!") | |
| break | |
| except DBAPIError as e: | |
| if attempt < max_retries - 1: | |
| print(f"Database is waking up or unavailable. Retrying in {retry_delay}s...") | |
| time.sleep(retry_delay) | |
| else: | |
| print("Failed to connect to database after multiple attempts.") | |
| raise e | |