from collections.abc import AsyncIterator from sqlalchemy.ext.asyncio import ( AsyncEngine, AsyncSession, async_sessionmaker, create_async_engine, ) from .settings import get_settings _engine: AsyncEngine | None = None _sessionmaker: async_sessionmaker[AsyncSession] | None = None def get_engine() -> AsyncEngine: global _engine, _sessionmaker if _engine is None: s = get_settings() # PH-016: explicit pool sizing. pool_pre_ping protects against stale # connections after PG restarts; pool_recycle bounds connection age. _engine = create_async_engine( s.database_url, pool_pre_ping=True, pool_size=s.db_pool_size, max_overflow=s.db_max_overflow, pool_timeout=s.db_pool_timeout_sec, pool_recycle=s.db_pool_recycle_sec, future=True, ) _sessionmaker = async_sessionmaker(_engine, expire_on_commit=False) return _engine def get_sessionmaker() -> async_sessionmaker[AsyncSession]: if _sessionmaker is None: get_engine() assert _sessionmaker is not None return _sessionmaker async def dispose_engine() -> None: global _engine, _sessionmaker if _engine is not None: await _engine.dispose() _engine = None _sessionmaker = None async def get_session() -> AsyncIterator[AsyncSession]: sm = get_sessionmaker() async with sm() as session: yield session