文档:开发计划 §10.6 回写死锁重试后补(43cf2a1)执行记录

This commit is contained in:
2026-09-10 20:32:44 +08:00
parent 43cf2a1372
commit 3b781f8823
@@ -1511,6 +1511,27 @@ sqlite 无 gap lock,故该分支由 `tests/test_convert_core.py` 用注入点
> 已恢复全部突变,`grep -rn "MUTATION\|1 = 1" app/ tests/ scripts/` **无残留**。
### 10.6 T-13 后补:死锁 1213 服务端自动重试(2026-09-10 同日用户拍板 · 已落地 `43cf2a1`)
§10.2 遗留的「服务端是否应重试死锁」已拍板:**服务端在数据访问层自动重试**(依据:Oracle MySQL 官方博客
错误码表 1213 = "Retry the entire transaction")。
**改动**(`app/gateway/convert_core_repository.py` + `app/config/settings.py`):
- `apply_convert` 改为重试外壳 + `_apply_convert_once` 事务体:死锁方被整体回滚、库内零残留,
重跑即重新读库(满足「重试前重新取数」);
- 只重试 **1213**(`_is_deadlock` 取底层 DBAPI `orig.args[0]` 判定);**1205 不在范围**
(其重试语义受 `innodb_rollback_on_timeout` 影响,官方口径为「默认重试语句」,与整事务重试不通用);
- 阈值走 settings:`convert_deadlock_retry_max=2` / `convert_deadlock_retry_base_ms=50`,
退避 = base·2^(n-1) + [0, base) 随机抖动(架构 §11「阈值不得硬编码」);
- 重试安全:单事务回滚零残留 + 幂等锚点 `out_trade_id` 在 service 层不受影响。
**验证**
- sqlite 单测 +3:重试后落库成功(调 3 次、退避递增)/ 预算耗尽上抛(零残留)/ 1205 不重试;
- **突变 2 组精准命中后恢复**:① `_is_deadlock` 恒 False → 重试用例红;② 预算判断 `>=`→`>` → 耗尽用例红;
- 全量 `pytest -q` → **739 passed / 10 skipped**(+3);
- `CONVERT_STRESS=1` 并发 **9 passed**;**8 客户并发实测 1213×4 → 服务端重试全部第 1 次收敛、外部兜底 0 次**;
- 真库脚本复跑零回归:apply **24/24** / service **35/35**。
---
## 11. 测试增量与基线预测