Первоначальная версия VidConf
This commit is contained in:
38
workers/db.py
Normal file
38
workers/db.py
Normal file
@@ -0,0 +1,38 @@
|
||||
"""Асинхронный доступ к БД для Celery-задач: сессия на один вызов задачи.
|
||||
|
||||
Celery-воркер (`celery ... -B`) работает в prefork-пуле — глобальный async
|
||||
engine backend (`core.db.engine`), созданный один раз в родительском
|
||||
процессе, не переживает fork (сетевые соединения, открытые до fork, в
|
||||
дочернем процессе ведут к зависаниям/битым event loop'ам asyncpg). Поэтому
|
||||
каждый вызов задачи создаёт свой временный engine и гарантированно
|
||||
уничтожает его по завершении.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from collections.abc import AsyncGenerator, Callable, Coroutine
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Any, TypeVar
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||||
|
||||
from core.config import get_settings
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def open_session() -> AsyncGenerator[AsyncSession, None]:
|
||||
"""Открыть сессию БД с короткоживущим engine (закрывается по выходу из контекста)."""
|
||||
settings = get_settings()
|
||||
engine = create_async_engine(settings.database_url, pool_pre_ping=True)
|
||||
session_maker = async_sessionmaker(engine, expire_on_commit=False)
|
||||
try:
|
||||
async with session_maker() as session:
|
||||
yield session
|
||||
finally:
|
||||
await engine.dispose()
|
||||
|
||||
|
||||
def run_async(factory: Callable[[], Coroutine[Any, Any, T]]) -> T:
|
||||
"""Выполнить async-корутину в новом event loop — обёртка для sync Celery-задач."""
|
||||
return asyncio.run(factory())
|
||||
Reference in New Issue
Block a user