Files

90 lines
2.4 KiB
Python

"""SQLAlchemy 异步引擎与会话。"""
from typing import Any
from sqlalchemy.ext.asyncio import (
AsyncSession,
AsyncEngine,
async_sessionmaker,
create_async_engine,
)
from sqlalchemy.orm import DeclarativeBase
from app.config import settings
def _build_engine() -> AsyncEngine:
return create_async_engine(
settings.database_url,
echo=settings.debug,
future=True,
)
def _build_session_maker(bind_engine: AsyncEngine) -> async_sessionmaker[AsyncSession]:
return async_sessionmaker(
bind_engine,
class_=AsyncSession,
expire_on_commit=False,
autocommit=False,
autoflush=False,
)
class _AsyncSessionMakerProxy:
"""可重绑定的 sessionmaker 代理。
Celery prefork 模式下,worker 子进程不能继续复用父进程里初始化的
async engine / sessionmaker。这里保持导入对象稳定,同时允许在子进程
启动后重新绑定底层 sessionmaker。
"""
def __init__(self, maker: async_sessionmaker[AsyncSession]) -> None:
self._maker = maker
def configure(self, maker: async_sessionmaker[AsyncSession]) -> None:
self._maker = maker
def __call__(self, *args: Any, **kwargs: Any) -> AsyncSession:
return self._maker(*args, **kwargs)
engine = _build_engine()
async_session_maker = _AsyncSessionMakerProxy(_build_session_maker(engine))
class Base(DeclarativeBase):
"""所有 ORM 模型的基类。"""
pass
async def init_db() -> None:
"""创建所有表(开发/迁移用)。"""
# 确保 ORM 模型已导入,从而注册到 Base.metadata
import app.models.llm # noqa: F401 # pylint: disable=unused-import
import app.models.studio # noqa: F401
import app.models.task # noqa: F401
import app.models.task_links # noqa: F401
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async def close_db() -> None:
"""关闭数据库连接。"""
await engine.dispose()
def reset_db_runtime() -> None:
"""在 Celery worker 子进程中重建 engine 与 sessionmaker。
这样可以避免 prefork 继承父进程中的 async engine,导致连接对象和事件循环
绑定错乱,触发 Future attached to a different loop。
"""
global engine
engine = _build_engine()
async_session_maker.configure(_build_session_maker(engine))