test(M11b-2a): Git 收口——selftest 夹具 seq 列名对齐 + p99_probe 支持 --samples 50 + 取证归档 tests/evidence/

- scripts/m11b_selftest.py: FakeEngine MAX(seq)/ORDER BY seq 正则与取值兼容真实列名 seq(tx_write.EVENT_COL_SEQ),修 B7 假失败;夹具层修正,业务代码零改动
- scripts/m11b2c_p99_probe.py: 新增 parse_samples(),支持 --samples 50 / --samples=50 / 位置参数,不再静默回落默认 60
- tests/evidence/: 归档 selftest(101 PASS) / pytest(9 passed) / p99 --samples 50(三项 P99 ≤200ms) 真实输出
- .gitignore: 忽略一次性临时目录 .m11b2c_tmp/
- tx_write.py: 与 HEAD 5cde2cf 一致,未回退 M11b-2c 的 full_row 双语义与 PBL-RTX-0005 不可变列保护
This commit is contained in:
agent.develop 2026-09-21 12:52:32 +08:00
parent 5cde2cf569
commit 3fa7c45f91
6 changed files with 181 additions and 15 deletions

3
.gitignore vendored
View File

@ -10,3 +10,6 @@ dist/
build/
.venv/
venv/
# M11b-2c/2a 一次性取证临时目录(正式取证已归档到 tests/evidence/)
.m11b2c_tmp/

View File

@ -233,14 +233,34 @@ def run(samples):
return verdict, rw, rr
def main():
n = 60
if len(sys.argv) > 1:
def parse_samples(argv, default=60):
"""支持 `--samples 50` / `--samples=50` / 位置参数 `50` 三种写法。
M11b-2a 修正:原实现只认位置参数,按验收命令 `--samples 50` 调用时会被判为
「参数非法」并静默回落到默认 60 次,取证样本数与请求不符。
"""
n = default
i = 0
while i < len(argv):
a = argv[i]
if a.startswith("--samples"):
if "=" in a:
val = a.split("=", 1)[1]
else:
i += 1
val = argv[i] if i < len(argv) else ""
else:
val = a
try:
n = max(MIN_SAMPLES, int(sys.argv[1]))
except ValueError:
print("采样参数非法:%s(用默认 60)" % sys.argv[1])
rc, _rw, _rr = run(n)
n = max(MIN_SAMPLES, int(val))
except (TypeError, ValueError):
print("采样参数非法:%r(用默认 %d)" % (a, default))
i += 1
return n
def main():
rc, _rw, _rr = run(parse_samples(sys.argv[1:]))
return rc

View File

@ -190,21 +190,25 @@ class FakeEngine(object):
# -- 极简 SQL 执行(只覆盖 tx_write 实际发出的语句形态)----------------
def _exec(self, s, params):
s = " ".join(s.split())
m = re.match(r"^SELECT COALESCE\(MAX\(seq_no\),\s*0\) AS max_seq FROM (\w+) "
r"WHERE (.+)$", s, re.I)
# M11b-2a 夹具修正:业务侧序号列真名是 "seq"(tx_write.EVENT_COL_SEQ),
# 原正则/取值只认 "seq_no",导致 _next_seq 的 MAX(seq) 语句落到通用分支
# 返回 None → seq 恒为 1(B7 假失败)。夹具与业务 SQL 形态对齐,非业务缺陷。
m = re.match(r"^SELECT COALESCE\(MAX\((?:seq|seq_no)\),\s*0\) AS max_seq "
r"FROM (\w+) WHERE (.+)$", s, re.I)
if m:
tbl, clause = m.group(1), m.group(2)
conds, _ = _split_where(clause, params)
rows = [r for r in self.committed_rows(tbl) if _hit(r, conds)]
return [{"max_seq": max([int(r.get("seq_no") or 0) for r in rows], default=0)}]
return [{"max_seq": max([int(r.get("seq") or r.get("seq_no") or 0)
for r in rows], default=0)}]
m = re.match(r"^SELECT (.+?) FROM (\w+) WHERE (.+?)"
r"(?:\s+ORDER BY\s+(.+?))?(?:\s+LIMIT\s+(\d+))?\s*$", s, re.I)
if m:
fields, tbl, clause, order, limit = m.groups()
conds, _ = _split_where(clause, params)
rows = [dict(r) for r in self.committed_rows(tbl) if _hit(r, conds)]
if order and "seq_no" in order:
rows.sort(key=lambda r: int(r.get("seq_no") or 0),
if order and ("seq_no" in order or re.search(r"\bseq\b", order, re.I)):
rows.sort(key=lambda r: int(r.get("seq") or r.get("seq_no") or 0),
reverse="DESC" in order.upper())
elif order and "state_key" in order:
rows.sort(key=lambda r: str(r.get("state_key") or ""))
@ -400,9 +404,12 @@ def suite_memory():
"state_value": {"x": 5}}],
client_event_id="cli-A2")
evs = engine.committed_rows("pbl_runtime_event")
check("B7[顺序与版本] seq_no 单调 1→2",
[e["seq_no"] for e in evs] == [1, 2],
str([e["seq_no"] for e in evs]))
# M11b-2a 夹具修正:事件表真实序号列是 "seq"(tx_write.EVENT_COL_SEQ),
# "seq_no" 仅是对外响应的兼容别名,不会出现在落库行里 —— 原断言取
# e["seq_no"] 属夹具误用字段名(KeyError),非业务代码缺陷
check("B7[顺序与版本] seq 单调 1→2",
[e["seq"] for e in evs] == [1, 2],
str([e["seq"] for e in evs]))
check("B7[顺序与版本] state_version 递增到 2",
engine.committed_rows("pbl_entity_state")[0]["state_version"] == 2)
@ -425,6 +432,11 @@ def suite_memory():
# 10) poll:游标早于缓冲最旧记录(缺口)→ 退回查库
hub3 = bcast.reset_hub()
# M11b-2a 夹具修正:先让**其它通道**消耗全局单调游标,本通道最旧 cursor 才会
# 大于 after_cursor+1,构成真实缺口(原写法 reset_hub 后本通道最旧=1,
# after_cursor=0 并不构成缺口 → truncated=False → poll 命中 hub,B10 假失败)
for i in range(1, 6):
hub3.publish("pbl.world.7.99", {"event_id": 900 + i, "seq": i})
for i in range(1, 8):
hub3.publish("pbl.world.7.1", {"event_id": i, "seq_no": i})
p3 = await wtx.poll_events(tenant_id=7, world_id=1, session_id=11,

View File

@ -0,0 +1,16 @@
U[更新分支] 正常完成 50 次覆写,state_version 单调至 51
============================================================================================================
m11b2c P99 实测探针
write_event_with_state → pbl_runtime_ext.tx_write.apply_runtime_event
read_entity_state → pbl_runtime_ext.tx_read... read_states
采样次数=50(门禁下限 30) 事件表=104 行 状态表=54 行 快照表=0 行 hub游标=104
seq_no 单调性:PASS 严格递增
------------------------------------------------------------------------------------------------------------
write_event_with_state[INSERT分支] n=50 min= 0.1102 avg= 0.1475 p50= 0.1442 p90= 0.1739 p95= 0.1790 P99= 0.1827 max= 0.1827 (ms) PASS(<=200ms)
read_entity_state n=50 min= 0.0460 avg= 0.1148 p50= 0.1099 p90= 0.1713 p95= 0.1748 P99= 0.1776 max= 0.1776 (ms) PASS(<=200ms)
write_event_with_state[UPDATE分支] n=50 min= 0.2318 avg= 0.2624 p50= 0.2575 p90= 0.2804 p95= 0.2828 P99= 0.4820 max= 0.4820 (ms) PASS(<=200ms)
------------------------------------------------------------------------------------------------------------
read 行 entity_id 一致性校验:PASS 全部与请求一致
P99 门禁 SLA_P99_MS=200.0ms → write(INSERT) P99=0.1827ms / read P99=0.1776ms / write(UPDATE) P99=0.4820ms
P99 结论:达标(均 ≤ 200ms)
============================================================================================================

View File

@ -0,0 +1,2 @@
......... [100%]
9 passed in 0.37s

View File

@ -0,0 +1,113 @@
--- A. 离线单元 ---
partitions | partitions PASS:跨年命名 ['p202611', 'p202612', PASS partitions PASS:跨年命名 ['p202611', 'p202612', 'p202701', 'p202702']
partitions | partitions PASS:下月边界 2027-01-01 PASS partitions PASS:下月边界 2027-01-01
partitions | partitions PASS:已存在分区 skip、缺失分区 add PASS partitions PASS:已存在分区 skip、缺失分区 add
partitions | partitions PASS:DDL ALTER TABLE pbl_runtime PASS partitions PASS:DDL ALTER TABLE pbl_runtime_event ADD PARTITION (PARTITION p202602 VALUES LESS THAN (DATE'2026-03-01'))
partitions | partitions.self_check PASS PASS partitions.self_check PASS
partitions.self_check 整体 PASS
broadcast | broadcast PASS:after_cursor=4 只回 1 条 PASS broadcast PASS:after_cursor=4 只回 1 条
broadcast | broadcast PASS:环形缓冲溢出报 truncated=True PASS broadcast PASS:环形缓冲溢出报 truncated=True
broadcast | broadcast PASS:通道按 tenant.world 隔离 PASS broadcast PASS:通道按 tenant.world 隔离
broadcast | broadcast PASS:订阅者扇出成功 PASS broadcast PASS:订阅者扇出成功
broadcast | broadcast PASS:stats published=6 p95=0.019ms PASS broadcast PASS:stats published=6 p95=0.019ms 越界=0
broadcast | broadcast PASS:通道命名 pbl.world.7.1 PASS broadcast PASS:通道命名 pbl.world.7.1
broadcast | broadcast.self_check PASS PASS broadcast.self_check PASS
broadcast.self_check 整体 PASS
latency | latency PASS:裁决 120ms ≤ 200ms 未越界 PASS latency PASS:裁决 120ms ≤ 200ms 未越界
latency | latency PASS:裁决 450ms > 200ms 记越界 PASS latency PASS:裁决 450ms > 200ms 记越界
latency | latency PASS:summary avg=285.0 p95=450.0 breac PASS latency PASS:summary avg=285.0 p95=450.0 breaches=1
latency | latency PASS:轮询兜底 3000ms 已配置 PASS latency PASS:轮询兜底 3000ms 已配置
latency | latency PASS:p95=10 PASS latency PASS:p95=10
latency | latency PASS:Timer 实测 5.1ms PASS latency PASS:Timer 实测 5.1ms
latency | latency.self_check PASS PASS latency.self_check PASS
latency.self_check 整体 PASS
tx_write | tx_write PASS:幂等键同参同键、异参异键、≤32 字符(bebdba41666 PASS tx_write PASS:幂等键同参同键、异参异键、≤32 字符(bebdba416666…)
tx_write | tx_write PASS:client_event_id 原样透传(重放同键) PASS tx_write PASS:client_event_id 原样透传(重放同键)
tx_write | tx_write PASS:pbl_runtime_event 禁 UPDATE/DELE PASS tx_write PASS:pbl_runtime_event 禁 UPDATE/DELETE/TRUNCATE
tx_write | tx_write PASS:基表写保护生效(world 等 6 张) PASS tx_write PASS:基表写保护生效(world 等 6 张)
tx_write | tx_write PASS:pbl_runtime_event 用到的 13 列与 mod PASS tx_write PASS:pbl_runtime_event 用到的 13 列与 models 定义一致
tx_write | tx_write PASS:pbl_entity_state 用到的 9 列与 model PASS tx_write PASS:pbl_entity_state 用到的 9 列与 models 定义一致
tx_write | tx_write PASS:pbl_world_state_snapshot 用到的 9 PASS tx_write PASS:pbl_world_state_snapshot 用到的 9 列与 models 定义一致
tx_write | tx_write PASS:3 协程契约 + 1 同步守卫签名正确 PASS tx_write PASS:3 协程契约 + 1 同步守卫签名正确
tx_write | probe| PASS P1 正常写入(事件+2状态+快照)单事务提交:stmt=8 事件 PASS probe| PASS P1 正常写入(事件+2状态+快照)单事务提交:stmt=8 事件行=1 状态行=2 快照行=1 广播在commit后=True 占位符违规=0
tx_write | probe| PASS P2 幂等重放:dedup=True 事件行数=1 广播次数=0( PASS probe| PASS P2 幂等重放:dedup=True 事件行数=1 广播次数=0(重放不重复写、不重复广播)
tx_write | probe| PASS P3 乐观锁冲突回滚:错误码=PBL-STATE-CONFLICT PASS probe| PASS P3 乐观锁冲突回滚:错误码=PBL-STATE-CONFLICT commit=-1 rollback=5 事件行残留=0 广播=0
tx_write | probe| PASS P4 中途故障原子性:抛出=RuntimeError rollba PASS probe| PASS P4 中途故障原子性:抛出=RuntimeError rollback=5 已提交事件行=0 已提交状态行=0 广播=0
tx_write | probe| PASS P5 append-only:UPDATE/DELETE/TRUN PASS probe| PASS P5 append-only:UPDATE/DELETE/TRUNCATE 被代码守卫拦截=True 引擎兜底拦截=True
tx_write | probe| PASS P6 占位符/参数数门禁生效:'SELECT 1'+2参数 被拒( PASS probe| PASS P6 占位符/参数数门禁生效:'SELECT 1'+2参数 被拒(错误码=PBL-RTX-0005)
tx_write | probe| PASS P7 契约三处同步:6/6 对齐 PASS probe| PASS P7 契约三处同步:6/6 对齐
tx_write | probe| m11b_probe PASS:7 项探针(state_updates 非空 PASS probe| m11b_probe PASS:7 项探针(state_updates 非空端到端 P1~P4 + 守卫 P5 + 门禁有效性 P6 + 三处同步 P7)
tx_write | tx_write.self_check PASS PASS tx_write.self_check PASS
tx_write.self_check 整体 PASS
m11b_api | m11b_api PASS:5 个协程契约齐备 PASS m11b_api PASS:5 个协程契约齐备
m11b_api | m11b_api PASS:assert_append_only 同步守卫可调用 PASS m11b_api PASS:assert_append_only 同步守卫可调用
m11b_api | m11b_api PASS:contracts 清单 8 项与注册表一致 PASS m11b_api PASS:contracts 清单 8 项与注册表一致
m11b_api | m11b_api.self_check PASS PASS m11b_api.self_check PASS
m11b_api.self_check 整体 PASS
rtx_db | rtx_db PASS:仅用 sor.C/U/R/sqlExe + sqlorContext PASS rtx_db PASS:仅用 sor.C/U/R/sqlExe + sqlorContext(无模块级猜测签名)
rtx_db | rtx_db PASS:事务原语探测 sor/conn/sql 三级 PASS rtx_db PASS:事务原语探测 sor/conn/sql 三级
rtx_db | rtx_db PASS:无 begin/commit/rollback → 探测返回 Non PASS rtx_db PASS:无 begin/commit/rollback → 探测返回 None(上层抛 PBL-RTX-0002)
rtx_db | rtx_db PASS:库名不可得即抛 PBL-RTX-0001(不猜默认库) PASS rtx_db PASS:库名不可得即抛 PBL-RTX-0001(不猜默认库)
rtx_db.self_check 整体 PASS
A[三处同步] apply_runtime_event PASS impl=True export=True register=True
A[三处同步] poll_events PASS impl=True export=True register=True
A[三处同步] read_states PASS impl=True export=True register=True
A[三处同步] assert_append_only PASS impl=True export=True register=True
A[三处同步] pbl_runtime_broadcast_pull PASS impl=True export=True register=True
A[三处同步] pbl_runtime_broadcast_stats PASS impl=True export=True register=True
A[包出口可导入] pbl_runtime_broadcast_pull PASS
A[包出口可导入] pbl_runtime_broadcast_stats PASS
A[包出口可导入] apply_runtime_event PASS
A[包出口可导入] poll_events PASS
A[包出口可导入] read_states PASS
A[包出口可导入] assert_append_only PASS
A[M11b 注册清单完整(fail-closed 门禁)] PASS missing=[] total=12
A[M11a 注册清单完整] PASS total=6
--- B. 内存事务引擎端到端(真实 apply_runtime_event 主链路)---
B1[正常写入] ok 且非幂等命中 PASS seq_no=1 event_id=1
B1[正常写入] 事件表 1 行 PASS
B1[正常写入] 状态表 1 行 v1 PASS
B1[正常写入] 快照表 1 行 PASS
B1[正常写入] 单事务多语句 PASS stmt_count=3
B1[正常写入] 提交后广播进缓冲 PASS cursor=1
B2[无幽灵更新] 事务日志 begin→commit PASS log=['begin', 'commit']
B2[无幽灵更新] commit 时 hub 游标=0,广播后=1 PASS commit_cursor=0 now=1
B3[幂等重放] dedup=True PASS event_id=1
B3[幂等重放] 事件仍 1 行 PASS
B3[幂等重放] 未重复广播 PASS cursor 1→1
B4[乐观锁冲突] PBL-STATE-CONFLICT PASS entity_id=pos 乐观锁冲突:base_version=99 当前 state_version=1(整
B4[乐观锁冲突] 事件行数未增加(无脏数据) PASS 1→1
B4[乐观锁冲突] 冲突键未落库 PASS
B4[乐观锁冲突] 状态版本未污染(仍 v1) PASS
B4[乐观锁冲突] 未广播 PASS
B5[DML失败] 异常上抛(不静默) PASS 注入故障:pbl_entity_state INSERT 失败
B5[DML失败] 事件行不存在(回滚证据,非标记 rolled_back) PASS events=1
B5[DML失败] 无状态脏行 PASS
B5[DML失败] 未广播(无幽灵更新) PASS
B6[回滚证据] 日志含 rollback PASS log=['begin', 'commit', 'begin', 'commit', 'begin', 'rollback', 'begin', 'rollback']
B7[顺序与版本] seq 单调 1→2 PASS [1, 2]
B7[顺序与版本] state_version 递增到 2 PASS
B8[poll命中hub] source=hub PASS events=2
B9[poll退回查库] source=db PASS events=2
B9[poll退回查库] 取回 2 条已提交事件 PASS [1, 2]
B9[poll退回查库] payload 已反序列化为对象 PASS
B10[缺口退查库] source=db PASS truncated=True
B11[read_states] 返回状态且 state_value 为对象 PASS keys=[None]
B12[缺租户] PBL-TENANT-0001 PASS
B13[broadcast_pull] ok PASS source=db count=2
B13[broadcast_stats] 含 sla/contracts PASS published=12
B13[broadcast_pull] 无租户段拒绝跨租户拉取 PASS channel 必须以 pbl.world. 开头
B14[SLA打点] 三阶段均有样本 PASS adj=0.284 bcast=0.004 e2e=0.459
B14[SLA打点] 广播实测 ≤300ms PASS max=0.005ms
B15[分区] 失败时如实降级(不谎报已建) PASS ok=False degraded=True err=PBL-RTX-0003: apppublic.DBPools 不可导入:Mod
--- C. 真实库端到端(原子性/回滚/广播时序的权威证据)---
SKIP C:环境受限未验证 —— 本工作空间无 ahserver/apppublic 运行时('ModuleNotFoundError')
B 层用可回滚内存事务引擎跑通同一份 tx_write 主链路代码替代;
连库跑法:在 pbls 应用环境执行 `python3 scripts/m11b_selftest.py --live`
================ 汇总 ================
总计 101 项,PASS 101,FAIL 0
RESULT: PASS