fix(retention): T12-D acceptance — OrdersDAO conn ownership

- OrdersDAO.__init__ 新增 owns_conn=False 参数
- __exit__ 仅在 owns_conn=True 时 close
- connect() classmethod 创建的 conn 自动 owns_conn=True
- 修复 retention_cleanup 一次命中 ≥2 笔终端态订单时
  第二笔起 Cannot operate on a closed database 的 bug
- 新增 test_retention_cleanup_apply_anonymizes_multiple_old_orders_in_sequence
  锁住多订单连续 anonymize 契约
- runbook §8 新增 T12-D 端到端本地 acceptance 步骤 + 部署前 checklist
- CHANGELOG 加 v2.1.1 (2026-06-20) 段
This commit is contained in:
hermes
2026-06-20 14:25:19 +08:00
parent 2893a45d8b
commit bcb1e68587
4 changed files with 185 additions and 4 deletions

View File

@@ -178,8 +178,18 @@ class OrdersDAO:
退出上下文时自动 commit/close。
"""
def __init__(self, conn: sqlite3.Connection) -> None:
def __init__(self, conn: sqlite3.Connection, *, owns_conn: bool = False) -> None:
"""构造 DAO。
- ``owns_conn=False``(默认): ``conn`` 由调用方持有与关闭,``__exit__``
只 commit/rollback**不** close。这允许上层 service 把自己的连接
包成 DAO 走 with-block典型场景循环里多次复用同一连接执行多个
写操作,再由 service 统一 commit/close。T12-D regression。
- ``owns_conn=True``: ``OrdersDAO.connect()`` 内部 ``apply_schema`` 创建
的连接由 DAO 自己关闭,``__exit__`` 在 commit/rollback 后再 close。
"""
self._conn = conn
self._owns_conn = owns_conn
self._tx_depth = 0 # 嵌套事务深度0 = 顶层)
# DAO 假设 conn 已启用 foreign_keys不强制重设调用方控制
@@ -206,7 +216,8 @@ class OrdersDAO:
conn = apply_schema(db_path)
if row_factory:
conn.row_factory = sqlite3.Row
return cls(conn)
# ``connect`` 自己创建了连接DAO 拥有所有权__exit__ 必须 close。
return cls(conn, owns_conn=True)
@property
def conn(self) -> sqlite3.Connection:
@@ -920,7 +931,11 @@ class OrdersDAO:
else:
self._conn.rollback()
finally:
self._conn.close()
# 只在 DAO 自己拥有连接时才关闭;外部传入的连接由调用方负责生命周期。
# T12-D regression: deletion_service.anonymize_order 多次复用 service
# 持有的 self._conn若 DAO 退出时 close 会导致后续所有 DAO 操作崩。
if self._owns_conn:
self._conn.close()
__all__ = [