yeg
2026-09-17 be4d426a48d303bfa58de001b36ea0ad06b67bff
docs(scratch): 留档交易确认补偿用例被测试库遗留行挤满

PaymentFundConfirmationTests 有一项在 09-17 变成确定性失败:补偿任务全库扫描
order by id asc limit 50,而测试库符合条件(已核销+待确认)的遗留行已有 60 条,
用例自己新建的单 id 最大、永远排在第 50 名之外,于是状态停在 pending。
最早一条遗留是 09-14 留下的,与本轮改动无关(本轮只碰 merchant_onboarding_application 的读取)。

按按域分工只做只读排查:不改用例、不动测试库数据,现象/根因/证据/建议改法整理成
.scratch/payment-fund-confirmation-retry/issues/01 交接。建议首选让用例自己把窗口腾出来,
清理遗留只能缓解。
1个文件已添加
71 ■■■■■ 已修改文件
.scratch/payment-fund-confirmation-retry/issues/01-retry-limit-blocked-by-accumulated-leftovers.md 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.scratch/payment-fund-confirmation-retry/issues/01-retry-limit-blocked-by-accumulated-leftovers.md
New file
@@ -0,0 +1,71 @@
# 01 — 交易确认补偿被测试库里累计的遗留行挤满,用例恒失败
状态:**仅排查,未改动**。现象出在交易确认补偿(属支付链路),按仓库约定整理后交接,未动代码与测试数据。
## 现象
`docs/agents/testing.md` 记录的 3 项基线失败之外,多出一项:
```
PaymentFundConfirmationTests.aPendingConfirmationOfAVerifiedOrderIsRetriedWithoutAnotherVerification:173
expected: "confirmed"
 but was: "pending"
```
2026-09-17 全量跑(319 项)时出现;单独跑这个类(21 项)同样失败——**确定性失败,不是偶发**。
## 根因
补偿任务扫「已核销 + 待确认」订单时是**全库扫描 + limit**:
`PaymentFundConfirmationRetry.java:54-64`
```sql
select distinct o.id as order_id
from benefit_order o join payment_transaction p on p.order_id = o.id
where o.deleted = 0 and o.order_status = 1 and o.verify_status = 1 and o.refund_status = 0
  and p.status = 'SUCCEEDED' and p.funds_confirmation_status in ('pending', 'confirming')
order by o.id asc
limit ?
```
用例传的是 `retry(50)`(`PaymentFundConfirmationTests.java:172`),而它自己那单是**新建**的(id 最大),
排在 `order by o.id asc` 的最后。测试库是共享且从不清理的,符合条件的遗留行一旦累计到 50 条以上,
用例这单就永远轮不到 → 状态停在 `pending`。
## 证据(2026-09-17 13:1x 对 `talent_card_test` 实测)
```
eligible=60  oldestId=906  newestId=6549  firstSeen=2026-09-14 12:18:17
benefit_order total=6543
payment_transaction: pending=242  confirming=113  confirmed=506  reverting=44
```
- 符合扫描条件的行 **60 条 > limit 50**;最早的一条是 **09-14** 留下的,早于本轮任何改动。
- `newestId=6549` 正是该用例自己刚插入的那单——它在 60 条里排最后,被 limit 截掉。
- 这些行不会被排空:被处理过的行大多落在 `confirming`(在扫描条件内),所以每轮都会重新占住名额,
  集合是**饱和**的——也就是说,这个用例从今往后每跑必红,直到有人清理或改用例。
- 无关本轮工作:本轮只改了 `merchant_onboarding_application` 相关读取
  (`MerchantPaymentReadinessService`、`MerchantOnboardingProgressService`),
  新用例也没有插入 `benefit_order`/`payment_transaction`。
旁证:同文件相邻用例的注释已经写明这个危险——「补偿任务扫的是整库,会顺带处理其它用例留下的
「已核销 + 待确认」订单」(`PaymentFundConfirmationTests.java` 中 `theRetrySkipsOrdersWhoseFundsAreAlreadyConfirmed`)。
## 建议改法(二选一,请不要只做第二个)
1. **让用例自己把窗口腾出来**(推荐,`docs/agents/testing.md`「测试库的状态是共享的」给的就是这个办法):
   在用例准备阶段,把本次之外的符合条件行挪出时间窗口/状态窗口(例如把它们置成 `confirmed`,
   或按 id 条件排除),再断言本单。这样不再依赖"库里符合条件的行少于 50"这个未被写下来的前提。
2. **清理测试库遗留**(只能缓解,不能治本):把 50 条老行处理掉,例如
   `update payment_transaction set funds_confirmation_status='confirmed' where order_id in (...)`,
   或干脆 `scripts/reset-test-db.ps1` 重建——注意它是共享库,跑之前要确认没有别的会话在跑测试。
   不改用例的话,累计到 50 条以上还会复发。
也建议把这条顺手写进 `docs/agents/testing.md` 的已知失败表,说明它是环境相关、可自愈的那一类。
## Comments
- 2026-09-17 由商户端「收款能力」那一轮的实现会话发现并留档。**没有**改这个用例、**没有**动测试库数据:
  两者都在支付链路范围内,按 `AGENTS.md` 的按域分工只做只读排查后交接。排查期间的本地全量结果为
  「319 项,2 failures / 2 errors = 上述 1 项 + `docs/agents/testing.md` 记录的 3 项基线」。