refactor(api): TOML 配置 SSOT、统一错误契约、Auth/事务加固与可观测性 (#33)

配置 SSOT(TOML + .env)
统一错误契约
Auth 与事务边界
Redis / Celery 可靠性:业务 Redis(DB/0)与 Celery broker/backend(DB/1)显式拆分;连接池、sync client
可观测性(OpenTelemetry + LGTM)
This commit is contained in:
Sully
2026-05-22 13:44:50 +08:00
committed by GitHub
parent f09ae248f9
commit 53e0065e3e
298 changed files with 15247 additions and 4344 deletions

View File

@@ -6,16 +6,32 @@
事务规则:
- get_async_db() 只负责创建和关闭 session不自动 commit/rollback。
- 事务提交由 service 层显式调用 await db.commit()
- service / Celery task 层优先使用 transactional() / transactional_sync() 管理多步写操作
- repo 层禁止调用 commit() / rollback()。
transactional 语义:
- transactional() / transactional_sync() 是顶层事务边界;成功 exit 时 commit 整个 session异常时 rollback 整个 session。
- 不支持嵌套自身:同一 session 上连续两次 transactional() = 两次独立 commitWS pipeline 分段持久化属于此模式)。
- 需要嵌套回滚时:在已开启的事务内使用 transactional_nested() / transactional_nested_sync()(基于 SQLAlchemy begin_nested() savepoint
- 选择指南:单步/整段业务原子提交 → transactional长生命周期 session 内局部试错、可独立回滚的子步骤 → transactional_nested必须在外层事务 active 期间)。
transactional_nested 示例(外层提交、内层失败仅回滚 savepoint::
async with transactional(session):
session.add(parent_row)
try:
async with transactional_nested(session):
await attempt_optional_side_effect(session)
except RecoverableError:
pass # savepoint rolled back; parent_row still commits
"""
from contextlib import contextmanager
from contextlib import asynccontextmanager, contextmanager
from typing import AsyncGenerator
from sqlalchemy import create_engine, text
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase, sessionmaker
from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker
from app.core.config import settings
@@ -69,6 +85,32 @@ async def get_async_db() -> AsyncGenerator[AsyncSession, None]:
await session.close()
@asynccontextmanager
async def transactional(session: AsyncSession):
"""Top-level async transaction: commit on success, rollback on any exception.
Do not nest transactional() on the same session; each call commits independently.
For partial rollback within an active transaction, use transactional_nested().
"""
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
@asynccontextmanager
async def transactional_nested(session: AsyncSession):
"""Savepoint boundary; roll back only this block on error.
Must be used while the session already has an active transaction (e.g. inside
transactional() before it commits, or after autobegin from a prior write).
"""
async with session.begin_nested():
yield session
# ── Sync engine & session (Celery, Alembic, scripts) ─────────
sync_engine = create_engine(
@@ -100,6 +142,28 @@ def init_db_schema() -> None:
Base.metadata.create_all(bind=sync_engine)
@contextmanager
def transactional_sync(session: Session):
"""Top-level sync transaction: commit on success, rollback on any exception.
Do not nest transactional_sync() on the same session; each call commits independently.
For partial rollback within an active transaction, use transactional_nested_sync().
"""
try:
yield session
session.commit()
except Exception:
session.rollback()
raise
@contextmanager
def transactional_nested_sync(session: Session):
"""Savepoint boundary for sync Celery / scripts; roll back only this block on error."""
with session.begin_nested():
yield session
@contextmanager
def get_sync_db():
"""Context-managed synchronous session for Celery tasks."""