90 lines
2.4 KiB
Python
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))
|