共享 PG 上的保留期批量删除测试偶发红,污染合并前信号 #18
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
现象
tests/integration/test_retention_tool_pg.py::TestPlainTableBatches::test_apply_deletes_only_expired_rows_in_batches在整套件连跑时偶发失败,报assert 12 == 61;单独重跑与再次连跑均绿。发现于 1.3.1(issue #16/#17)合并前的独立验证:同一次会话里
pytest tests/integration -q连跑三次,第 1 次该条红,第 2、3 次全绿(54 passed, 6 skipped,EXIT=0)。与 1.3.1 无关
git diff main...feat/issue-16-17-thinking-observability -- tests/integration/test_retention_tool_pg.py tools/为空——该分支根本没碰这个测试与它测的工具。为什么值得单独处理
CLAUDE.md §4.6 定了一条判据:"成败取决于外部服务当下状态的测试一律标
slow",判据是"重跑一次可能就绿了"。这一条正好落在该定义里——它打的是与其他项目共用的那台 PostgreSQL(max_connections=100),行数断言依赖当时库里的数据状态。留在日常关卡里的代价不是这一次红,而是信号污染:同一份文档里已经写过这个机制——"pre-commit 关卡跑全套件,网关一抖就挡住与之无关的提交,久了会把'测试红了先怀疑网关'变成惯性,真 bug 也会被当成抖动重试掉"。一个会偶发的 PG 测试放在合并前关卡上,培养的是同一种惯性。
可能的方向(不主张任何一种)
slow,交由发布清单第 4 步统一跑——与四个 e2e 文件、Redis 时间语义变体同款处置判断哪种合适是维护者的事。前两种的区别在于:标
slow承认它依赖外部状态,隔离数据则是消除这个依赖——后者更彻底,但要看这个用例本来想验的是不是"真实共享库上的批量删除"。已在 1.3.2 修复。
诊断修正: issue 判定"行数断言依赖共享实例的当下状态",方向对,但它推荐的首选处置(标 slow)不解决问题。那条断言的失效是双向的——别人一写就假红,而外部插入恰好抵消掉一次误删时又会假绿,后一半守的正是"审计表被删空"。标 slow 只把假红挪出日常关卡,对假阴毫无作用。
根因三层:
_public_count是全套件唯一一处全表口径断言,而同文件的_RUN_PREFIX机制从设计上就假定多进程并行写同一张表——两套前提互斥。telemetry_retention.py的目标表由连接的 search_path 隐式决定,调用点无法声明"我要删哪张表"。处置:
tools/telemetry_retention.py新增--table <schema>.llm_calls,目标由参数精确解析、绕开 search_path;表名段锁死为 llm_calls(否则一次手误就把遥测清理器变成通用行删除器)。test_postgres_telemetry.py的 7 条用例迁出共享表,_RUN_PREFIX只删行隔离用途(连接池用例的 application_name 唯一性保留——连接是实例级资源,schema 隔离对它无效)。test_schema_has_frozen_columns_in_order查 information_schema 不带 schema 过滤。未做: 没有标 slow(改完之后这条用例的成败不再取决于外部服务状态);没有清理共享表里那 11 行历史孤儿行。
独立验证: 全新上下文的 verifier 复现了两条关键判据而非采信文档——最坏情况下脚本确实走到共享表面前才被权限拒绝(解析到的 oid 与共享表相同、
has_table_privilege五项全 False);并用 PG 的pg_stat_all_tables计数器测得,跑完全部 45 条 PG 用例前后共享表的 seq_scan/n_tup_ins/n_tup_del 一个单位都没动——现在是零访问,不是"访问了但没改坏"。合并前的两道审查各自独立撞上同一处阻断: 沙箱的凭据替换在 DSN 无内联 user:pass 时会静默退回管理连接,那会让最坏情况用例以超级用户身份真删共享表。已修(拒绝 + 连上去比对 current_user 两层)。
设计
research-wiki/designs/2026-08-26-issue18-pg-test-isolation-design.md计划
research-wiki/plans/2026-08-26-issue18-pg-test-isolation.md实测
research-wiki/findings/2026-08-26-issue18-shared-pg-test-isolation.md