from __future__ import annotations from sqlalchemy.ext.asyncio import AsyncAttrs, async_sessionmaker, create_async_engine from sqlalchemy.orm import DeclarativeBase from app.core.config import settings # Без явных таймаутов зависший/протухший сокет к Postgres (например, тихо # оборванное NAT/LB соединение после долгого простоя — ровно то, что могло # случиться во время получасовой обработки батча из 230 файлов) блокирует # asyncpg НАВСЕГДА: ни ошибки, ни лога, просто вечное ожидание чтения # сокета. Это тот же класс проблемы с asyncio.run()-за-asyncio.run() вокруг # общего пула соединений, что уже ловили раньше (см. комментарий про # engine.dispose() в opencloud_token_store.py) — тут решаем его с другой # стороны: даём зависшему соединению упасть с понятной ошибкой вместо # бесконечного ожидания. _CONNECT_TIMEOUT_SECONDS = 10 _COMMAND_TIMEOUT_SECONDS = 15 connect_args: dict = {} if settings.DATABASE_SCHEMA: connect_args["server_settings"] = {"search_path": settings.DATABASE_SCHEMA} if "sqlite" in settings.DATABASE_URL: connect_args["check_same_thread"] = False else: # timeout/command_timeout — параметры asyncpg.connect(), не SQLAlchemy; # aiosqlite их не понимает, поэтому только для не-sqlite URL. connect_args["timeout"] = _CONNECT_TIMEOUT_SECONDS connect_args["command_timeout"] = _COMMAND_TIMEOUT_SECONDS engine = create_async_engine( settings.DATABASE_URL, pool_pre_ping=True, pool_size=5, max_overflow=10, pool_timeout=_CONNECT_TIMEOUT_SECONDS, connect_args=connect_args, ) SessionLocal = async_sessionmaker( bind=engine, autoflush=False, expire_on_commit=False, ) class Base(AsyncAttrs, DeclarativeBase): pass