"""Process-wide singleton async engine + session helper. Previously every API blueprint lazily built its own engine via make_engine(), so the process ran N connection pools. This module owns ONE engine for the whole app; create_app() disposes it on shutdown via after_serving. """ from contextlib import asynccontextmanager from sqlalchemy.ext.asyncio import ( AsyncEngine, async_sessionmaker, create_async_engine, ) from .config import get_config _engine: AsyncEngine | None = None _sessionmaker: async_sessionmaker | None = None def get_engine() -> AsyncEngine: global _engine, _sessionmaker if _engine is None: _engine = create_async_engine( get_config().database_url, pool_pre_ping=True, future=True ) _sessionmaker = async_sessionmaker(_engine, expire_on_commit=False) return _engine def _get_sessionmaker() -> async_sessionmaker: if _sessionmaker is None: get_engine() assert _sessionmaker is not None return _sessionmaker @asynccontextmanager async def get_session(): """`async with get_session() as session:` — one shared pool.""" Session = _get_sessionmaker() async with Session() as session: yield session async def dispose_engine() -> None: global _engine, _sessionmaker if _engine is not None: await _engine.dispose() _engine = None _sessionmaker = None