Files
OSIT-AE-API-FastAPI/app/lib_sql_core.py
Scott Idem 6ca79e9a02 chore(api): stabilize SQL core and enhance searchability
- Refactor SQL CRUD to use engine.connect() context managers for thread safety
- Optimize connection pooling in lib_sql_core
- Clean up app/routers/api.py to fix duplicate definitions and OpenAPI KeyError
- Add 'default_qry_str' to searchable_fields for Event, Session, Presentation, Presenter, Badge, and Journal
- Add 'event_location_name' to searchable_fields for Event Session
- Verified 20/20 E2E success via repro_intermittent_errors.py
2026-01-21 15:23:04 -05:00

80 lines
2.7 KiB
Python

"""
Foundational SQL connection management for the Aether API.
Isolates the SQLAlchemy engine and global connection state to prevent circular imports.
"""
import logging
import threading
from typing import Any, Optional
from sqlalchemy import create_engine
from app.config import settings
log = logging.getLogger('root')
log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
# 1. Thread-local storage for capturing last SQL error message
_sql_error_state = threading.local()
def get_last_sql_error() -> Optional[str]:
"""Retrieves and clears the last captured SQL error message."""
error = getattr(_sql_error_state, 'last_error', None)
_sql_error_state.last_error = None
return error
def set_last_sql_error(error: Any):
"""Sets the last captured SQL error message."""
_sql_error_state.last_error = str(error)
# 2. Initial Engine Setup
db_uri = settings.SQLALCHEMY_DB_URI
def create_ae_engine(uri: str):
return create_engine(
url = uri,
echo = False,
pool_size = settings.DB.get('pool_size', 10),
max_overflow = settings.DB.get('max_overflow', 20),
pool_use_lifo = True,
pool_pre_ping = True,
pool_recycle = settings.DB['pool_recycle'],
isolation_level = 'READ COMMITTED',
connect_args = {'connect_timeout': settings.DB['connect_timeout']}
)
engine = create_ae_engine(db_uri)
# DEPRECATED: Global shared 'db' connection. Use engine.connect() in context managers instead.
# Keeping for legacy compatibility but will phase out usage in crud lib.
db = engine.connect()
log.info('DB SQL Core: Initializing engine...')
# 3. Connection Management Logic
def reconnect_db() -> bool:
"""
Re-initializes the global database engine using current settings.
Useful after bootstrapping new credentials from the 'cfg' table.
"""
global engine, db, db_uri
log.info("DB SQL Core: Refreshing database connection engine...")
try:
if engine:
engine.dispose()
log.info("DB SQL Core: Disposed of previous database engine.")
db_uri = settings.SQLALCHEMY_DB_URI
engine = create_ae_engine(db_uri)
db = engine.connect()
log.info(f"DB SQL Core: Database engine re-established successfully: {db_uri}")
return True
except Exception:
log.exception("DB SQL Core: FAILED to refresh database engine!")
return False
def sql_connect(current_db=None, log_lvl: int = logging.INFO) -> bool:
"""Refreshes the global database connection."""
log.setLevel(log_lvl)
log.info('DB SQL Core: Refreshing database connection via sql_connect...')
return reconnect_db()