ForcePilot/backend/package/yuxi/storage/transactions/ports.py
Kris 41bd0a618c refactor: 统一将日志方法从 warn 改为 warning
将代码库中所有使用 logger.warn 的地方替换为标准的 logger.warning,对齐日志方法命名规范,修复多处测试和业务代码中的方法调用不匹配问题。
2026-07-13 17:32:29 +08:00

191 lines
7.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""事务控制端口契约。
本模块定义 yuxi 三大限界上下文channels / external_systems / scheduler
共享的事务控制端口协议 ``TransactionPort`` 与 ``TransactionContext``,作为
事务控制的单一事实源(替代各模块自行定义的 ``TransactionPort`` /
``UnitOfWork`` Protocol
契约吸收自 ``yuxi.channels.contract.ports.driven.transaction_port`` 的语义,
并新增 ``savepoint`` 能力(吸收自 ``external_systems`` 的 ``UnitOfWork``)。
事务透传机制C-I1被驱动适配器如 ``ChannelPersistenceAdapter`` /
``ContentReviewRepositoryAdapter`` / ``AgentRunAdapter``)为无状态协议
转换器,构造时仅注入 ``session_factory````Callable[[], AsyncSession]``
不持有 session 实例。适配器通过 ``_session_scope(tx)`` 统一管理 session
``tx`` 非空时通过 ``tx.get_session()`` 复用应用层主事务 sessionC-I1
透传,``commit=False``,由应用层统一提交);``tx`` 为 ``None`` 时通过
``session_factory`` 创建独立 session 并自主提交(``commit=True``)。
事务边界由应用层独占§10.1
- 事务边界 **必须** 定义在应用服务层。
- 领域核心 **不得** 持有事务上下文,**不得** 直接调用事务 API。
- 被驱动适配器 **不得** 自主开启跨调用的事务,事务范围 **必须**
由应用层控制;被驱动适配器 **不得** 自主调用 ``commit`` / ``rollback``。
"""
from __future__ import annotations
from contextlib import AbstractAsyncContextManager
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
if TYPE_CHECKING:
# 仅类型检查期导入,运行时本契约模块不依赖 SQLAlchemy§6.1)。
from sqlalchemy.ext.asyncio import AsyncSession
@runtime_checkable
class TransactionPort(Protocol):
"""事务被驱动端口。
提供事务边界控制能力,由应用层在管道或用例编排器中显式调用。
被驱动适配器通过共享事务上下文(``TransactionContext``)加入同一
事务,**不得** 自主提交。
约束§10.1
- 事务边界 **必须** 定义在应用服务层。
- 领域核心 **不得** 持有事务上下文,**不得** 直接调用事务 API。
- 被驱动适配器 **不得** 自主开启跨调用的事务,事务范围 **必须**
由应用层控制。
使用示例::
async with transaction.begin() as tx:
await persistence_port.save_channel_account(cmd, tx=tx)
await persistence_port.save_audit_log(audit_cmd, tx=tx)
# 退出 with 块时自动提交,异常时自动回滚
"""
def begin(self) -> TransactionContext:
"""开启一个新事务,返回事务上下文。
@pre
- 当前无活动事务(或适配器允许嵌套,由实现决定)
@post
- 返回事务上下文,被驱动适配器通过该上下文加入同一事务
- 上下文管理器退出时自动提交(无异常)或回滚(有异常)
@failure
- TransactionBeginError: 事务开启失败
@consistency
- Strong事务边界由 ``TransactionContext`` 管理
"""
...
class TransactionContext(Protocol):
"""事务上下文。
由 ``TransactionPort.begin()`` 创建,上下文管理器退出时自动提交
(无异常)或回滚(有异常)。``commit`` / ``rollback`` 幂等,可由
应用层显式调用;调用后事务对象引用清空,``__aexit__`` 跳过重复
提交/回滚。
事务透传机制C-I1无状态适配器通过 ``get_session()`` 获取底层
共享会话,复用主事务的 session避免独立提交产生孤儿记录。适配器
在 ``_session_scope(tx)`` 中判断 ``tx`` 非空且 ``get_session()``
返回 session 时使用该 session``commit=False``),否则创建独立
session``commit=True``)。
``savepoint`` 用于批量操作中隔离每次迭代,避免单条失败导致整个
会话中毒(``PendingRollbackError``::
async with transaction.begin() as tx:
for item in items:
async with tx.savepoint():
await repo.create(item)
被驱动适配器 **不得** 自主调用 ``commit`` / ``rollback``。
说明:本 Protocol 未声明 ``@runtime_checkable``,不参与 ``isinstance``
检查。``TransactionContext`` 仅作为 ``tx`` 参数的类型注解,运行时由
``TransactionPort.begin()`` 返回的具体实现承载,无需运行时类型校验。
"""
def get_session(self) -> AsyncSession | None:
"""返回底层共享会话,供无状态适配器复用主事务 session。
仅 SQL 实现返回真实 ``AsyncSession``,非 SQL 实现返回 ``None``
(由适配器回退到 ``session_factory`` 创建独立 session 的路径)。
@pre
- 事务已开启(``__aenter__`` 已调用)
@post
- 返回底层共享会话;非 SQL 实现返回 ``None``
@consistency
- 调用方通过此 session 写入的数据自动加入主事务,
由应用层统一提交/回滚C-I1
"""
...
async def commit(self) -> None:
"""提交当前事务(幂等)。
仅由应用层(管道或用例编排器)调用,被驱动适配器 **不得** 调用。
重复调用为 no-op。
@pre
- 事务已开启且未提交/回滚(否则 no-op
@post
- 事务内所有写操作持久化生效;事务对象引用清空
@failure
- TransactionCommitError: 提交失败(事务自动回滚)
@consistency
- Strong提交成功后写操作立即可见
"""
...
async def rollback(self) -> None:
"""回滚当前事务(幂等)。
仅由应用层(管道或用例编排器)调用,被驱动适配器 **不得** 调用。
重复调用为 no-op。
@pre
- 事务已开启且未提交/回滚(否则 no-op
@post
- 事务内所有写操作被撤销;事务对象引用清空
@failure
- TransactionRollbackError: 回滚失败(连接异常,需关闭会话)
@consistency
- Strong回滚后数据库恢复到事务开启前状态
"""
...
def savepoint(self) -> AbstractAsyncContextManager[None]:
"""创建 SAVEPOINT返回 async context manager。
进入时创建 SAVEPOINT正常退出时释放 SAVEPOINT异常时回滚到
SAVEPOINT不污染外层事务并向上抛出原异常。用于批量操作中
隔离每次迭代。
@pre
- 外层事务已开启(``__aenter__`` 已调用)
@post
- 正常退出SAVEPOINT 释放,外层事务继续
- 异常退出:回滚到 SAVEPOINT外层事务不受污染异常向上抛出
@consistency
- StrongSAVEPOINT 隔离单次迭代失败,不影响外层事务
"""
...
async def __aenter__(self) -> TransactionContext:
"""进入事务上下文。"""
...
async def __aexit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
"""退出事务上下文,自动提交(无异常)或回滚(有异常)。"""
...