#!/usr/bin/env python3 # -*- coding: utf-8 -*- """M11b 离线自检脚本:单事务事件+状态写入、幂等、回滚、提交后广播、3s 轮询兜底。 不连真实数据库(用内存 SQL 替身),验证的是**代码行为**而非环境: P1 单事务写入:事件 1 行 + 实体状态 1 行,提交后可见 P2 幂等:同 idem_key 二次调用不重复写、不重复广播(dedup=True) P3 回滚:状态写入抛错 → 事件行不落库(无脏数据) P4 提交后广播:广播只在 commit 之后发生(顺序断言) P5 广播通道异常不撤销事实,轮询兜底仍可取到事件 P6 缺租户 fail-closed P7 客户端写服务端权威字段被拒 P8 乐观锁 if_state_version 冲突返回 PBL_RT_STATE_CONFLICT P9 时延字段与 SLA 预算齐备(裁决/广播/端到端 + 3s 兜底周期) P10 append-only 守卫 + 按月分区算法 用法:python3 scripts/m11b_selftest.py (退出码 0=PASS) """ import os import re import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from pbl_runtime_ext import rtx_db as R # noqa: E402 from pbl_runtime_ext import tx_write as W # noqa: E402 from pbl_runtime_ext import broadcast as B # noqa: E402 PH = re.compile(r"\$\{(\w+)\}\$") COLS = re.compile(r"INSERT\s+INTO\s+`?(\w+)`?\s*\(([^)]*)\)", re.IGNORECASE) class Store(object): def __init__(self): self.tables = {} self.seq = 0 self.commits = [] self.rollbacks = [] self.order = [] # 事件序列(commit / broadcast / insert) def rows(self, t): return self.tables.setdefault(t, []) def insert(self, table, row): self.seq += 1 row = dict(row) row["id"] = self.seq self.rows(table).append(row) self.order.append("insert:%s" % table) return self.seq class FakeTx(object): """事务替身:exec/query 打到内存 Store,退出时 commit 或 rollback。""" def __init__(self, store, boom_on=None): self.store = store self.boom_on = boom_on self.pending = [] def __enter__(self): return self def __exit__(self, et, ev, tb): if et is None: self.store.tables = getattr(self, "_final", self.store.tables) self.store.commits.append(len(self.store.rows("pbl_runtime_event"))) self.store.order.append("commit") else: for t, rows in getattr(self, "_snapshot", {}).items(): self.store.tables[t] = rows self.store.rollbacks.append(str(ev)[:80]) self.store.order.append("rollback") return False def _begin(self): self._snapshot = {k: list(v) for k, v in self.store.tables.items()} def exec(self, sql, params=None): params = params or {} if self.boom_on and self.boom_on in sql: raise RuntimeError("injected failure on %s" % self.boom_on) m = COLS.search(sql) if m: table = m.group(1) cols = [c.strip().strip("`") for c in m.group(2).split(",")] ph = PH.findall(sql.split("VALUES", 1)[1]) if "VALUES" in sql else [] row = {} for i, c in enumerate(cols): row[c] = params.get(ph[i]) if i < len(ph) else None if table == "pbl_entity_state": key = (row.get("tenant_id"), row.get("session_id"), row.get("entity_id"), row.get("state_key")) for r in self.store.rows(table): if (r.get("tenant_id"), r.get("session_id"), r.get("entity_id"), r.get("state_key")) == key: r.update(row) self.store.order.append("insert:pbl_entity_state") return 2 self.pending.append(key) return self.store.insert(table, row) return 1 def query(self, sql, params=None): params = params or {} self._begin() if "MAX(`seq_no`)" in sql: vals = [int(r.get("seq_no") or 0) for r in self.store.rows("pbl_runtime_event") if str(r.get("tenant_id")) == str(params.get("t")) and str(r.get("session_id")) == str(params.get("s"))] return [{"s": max(vals) if vals else 0}] if "MAX(`state_version`)" in sql: vals = [int(r.get("state_version") or 0) for r in self.store.rows("pbl_entity_state") if str(r.get("tenant_id")) == str(params.get("t")) and str(r.get("session_id")) == str(params.get("s"))] return [{"v": max(vals) if vals else 0}] if "LAST_INSERT_ID" in sql: return [{"i": self.store.seq}] if "FROM `pbl_entity_state`" in sql: out = [] for r in self.store.rows("pbl_entity_state"): if (str(r.get("tenant_id")) == str(params.get("t")) and str(r.get("session_id")) == str(params.get("s")) and str(r.get("entity_id")) == str(params.get("e")) and str(r.get("state_key")) == str(params.get("k"))): out.append({"state_value": r.get("state_value"), "state_version": r.get("state_version")}) return out[:1] return [] def install_store(store): """把 rtx_db / tx_write 的 DB 出入口全部换成内存替身。""" def fake_transaction(label="runtime_tx"): tx = FakeTx(store, boom_on=install_store.boom) tx._begin() return tx def fake_q_one(sql, params=None): params = params or {} if "FROM `pbl_runtime_event`" in sql and "idem_key" in sql: for r in store.rows("pbl_runtime_event"): if (str(r.get("tenant_id")) == str(params.get("t")) and str(r.get("idem_key")) == str(params.get("i"))): return dict(r) return None def fake_q_all(sql, params=None): params = params or {} if "FROM `pbl_runtime_event`" in sql: rows = [r for r in store.rows("pbl_runtime_event") if str(r.get("tenant_id")) == str(params.get("t")) and str(r.get("session_id")) == str(params.get("s")) and int(r.get("seq_no") or 0) > int(params.get("q") or 0)] return sorted(rows, key=lambda r: int(r.get("seq_no") or 0))[:int(params.get("n") or 100)] if "FROM `pbl_entity_state`" in sql: return [dict(r) for r in store.rows("pbl_entity_state") if str(r.get("tenant_id")) == str(params.get("t")) and str(r.get("session_id")) == str(params.get("s"))] return [] def fake_sql_exec(sql, params=None): return 1 fake_transaction.__call__ = fake_transaction install_store.boom = getattr(install_store, "boom", None) W.transaction = fake_transaction R.q_one = fake_q_one R.q_all = fake_q_all R.sql_exec = fake_sql_exec W.ensure_partition_ready = lambda force=False: {"ok": True, "created": ["pbl_runtime_event_202609"], "skipped": [], "errors": []} B.set_hub(B.BroadcastHub(workers=0)) return B.get_hub() RESULTS = [] def check(name, cond, detail=""): RESULTS.append((bool(cond), name, detail)) print("%s %s %s" % ("PASS" if cond else "FAIL", name, detail)) def main(): hub = None # ---------------- P1 单事务写入 store = Store() hub = install_store(store) install_store.boom = None pushed = [] hub.register_push(lambda sk, ev: pushed.append((sk, ev)) or 1, name="probe") r1 = W.apply_runtime_event(tenant_id="T1", session_id=11, world_id=7, entity_id=101, event_type="move", payload={"x": 1, "y": 2}, state_updates=[{"entity_id": 101, "state_key": "pos", "state_value": {"x": 1, "y": 2}}], snapshot=True, actor_id=9) check("P1 单事务写入 ok", r1.get("ok") and r1.get("transaction") == "single", "seq_no=%s state_version=%s" % (r1.get("seq_no"), r1.get("state_version"))) check("P1 事件 1 行 + 状态 1 行 + 快照 1 行", len(store.rows("pbl_runtime_event")) == 1 and len(store.rows("pbl_entity_state")) == 1 and len(store.rows("pbl_world_state_snapshot")) == 1, "ev=%d st=%d sn=%d" % (len(store.rows("pbl_runtime_event")), len(store.rows("pbl_entity_state")), len(store.rows("pbl_world_state_snapshot")))) check("P1 提交发生", len(store.commits) == 1, "commits=%d" % len(store.commits)) # ---------------- P4 提交后广播(顺序) check("P4 提交后广播", "commit" in store.order and store.order.index("commit") < len(store.order) and pushed and store.order.index("commit") < len([o for o in store.order if o.startswith("insert")]) + 1, "order=%s pushed=%d" % (store.order[-2:], len(pushed))) check("P4 广播帧含 state_version", bool(pushed) and pushed[0][1].get("state_version") == r1.get("state_version")) # ---------------- P2 幂等 r2 = W.apply_runtime_event(tenant_id="T1", session_id=11, world_id=7, entity_id=101, event_type="move", payload={"x": 1, "y": 2}, state_updates=[{"entity_id": 101, "state_key": "pos", "state_value": {"x": 1, "y": 2}}], actor_id=9) check("P2 幂等命中 dedup", r2.get("dedup") is True, "idem=%s" % str(r2.get("idem_key"))[:12]) check("P2 未重复写事件", len(store.rows("pbl_runtime_event")) == 1) check("P2 未重复广播", len(pushed) == 1, "pushed=%d" % len(pushed)) # ---------------- P3 回滚无脏数据 store2 = Store() hub2 = install_store(store2) install_store.boom = "pbl_entity_state" try: W.apply_runtime_event(tenant_id="T2", session_id=21, event_type="move", payload={"a": 1}, state_updates=[{"entity_id": 5, "state_key": "pos", "state_value": {"a": 1}}]) check("P3 状态写失败必须抛错", False, "未抛异常") except R.PblRtError as exc: check("P3 状态写失败抛 TX_FAILED", exc.code == "PBL_RT_TX_FAILED", "code=%s" % exc.code) except Exception as exc: # noqa: BLE001 check("P3 状态写失败抛错", True, "%r" % exc) check("P3 回滚后事件不落库(无脏数据)", len(store2.rows("pbl_runtime_event")) == 0 and len(store2.rollbacks) > 0, "ev=%d rollbacks=%d" % (len(store2.rows("pbl_runtime_event")), len(store2.rollbacks))) install_store.boom = None # ---------------- P5 广播通道异常不撤销事实 + 轮询兜底 store3 = Store() hub3 = install_store(store3) hub3.register_push(lambda sk, ev: (_ for _ in ()).throw(RuntimeError("ws down")), name="broken") r5 = W.apply_runtime_event(tenant_id="T3", session_id=31, event_type="hit", payload={"hp": 5}, state_updates=[{"entity_id": 8, "state_key": "hp", "state_value": {"hp": 5}}]) poll = W.poll_events("T3", 31, since_seq=0, limit=50, try_buffer=True) check("P5 广播失败但事件已提交", r5.get("ok") and len(store3.rows("pbl_runtime_event")) == 1) check("P5 轮询兜底取到事件", poll.get("count") == 1 and poll["events"][0]["seq_no"] == r5["seq_no"], "count=%s source=%s" % (poll.get("count"), poll.get("source"))) check("P5 兜底周期 3s", poll.get("poll_interval_ms") == 3000) # ---------------- P6 缺租户 fail-closed try: W.apply_runtime_event(tenant_id="", session_id=41, event_type="x") check("P6 缺租户必须拒", False, "未抛异常") except R.PblRtError as exc: check("P6 缺租户必须拒", exc.code == "PBL-TENANT-0001", "code=%s" % exc.code) # ---------------- P7 客户端写权威字段被拒 try: W.apply_runtime_event(tenant_id="T7", session_id=51, event_type="x", client_fields={"state_version": 99}) check("P7 客户端写 state_version 被拒", False, "未抛异常") except R.PblRtError as exc: check("P7 客户端写 state_version 被拒", exc.code == "PBL_RT_BAD_PAYLOAD", "code=%s" % exc.code) # ---------------- P8 乐观锁冲突 store8 = Store() install_store(store8) W.apply_runtime_event(tenant_id="T8", session_id=61, event_type="move", payload={"x": 1}, state_updates=[{"entity_id": 3, "state_key": "pos", "state_value": {"x": 1}}]) try: W.apply_runtime_event(tenant_id="T8", session_id=61, event_type="move", payload={"x": 2}, state_updates=[{"entity_id": 3, "state_key": "pos", "state_value": {"x": 2}, "if_state_version": 999}]) check("P8 乐观锁冲突必须拒", False, "未抛异常") except R.PblRtError as exc: check("P8 乐观锁冲突必须拒", exc.code == "PBL_RT_STATE_CONFLICT", "code=%s" % exc.code) check("P8 冲突后状态版本未推进", len(store8.rows("pbl_entity_state")) == 1 and int(store8.rows("pbl_entity_state")[0]["state_version"]) == 1) # ---------------- P9 时延与 SLA lat = r1.get("latency") or {} sla = lat.get("sla") or {} check("P9 时延字段齐备", all(k in lat for k in ("adjudicate_ms", "broadcast_ms", "e2e_ms")), "keys=%s" % sorted(lat.keys())) check("P9 SLA 预算 200/300/1000 + 3s 兜底", sla.get("adjudicate_ms") == 200 and sla.get("broadcast_ms") == 300 and sla.get("e2e_ms") == 1000 and sla.get("poll_fallback_interval_ms") == 3000, "sla=%s" % sla) check("P9 实测在预算内", r1.get("within_sla") is True, "adjudicate=%sms broadcast=%sms e2e=%sms" % (lat.get("adjudicate_ms"), lat.get("broadcast_ms"), lat.get("e2e_ms"))) # ---------------- P10 append-only 守卫 + 分区算法 ok_tx, msgs_tx = W.self_check() check("P10 tx_write 自检(append-only/分区/幂等键)", ok_tx, "; ".join([m for m in msgs_tx if "PASS" in m][:3])) from pbl_runtime_ext import partitions as P ddl = P.ddl_create_partition(__import__("datetime").date(2026, 10, 5), "mariadb") check("P10 月分区 DDL", "pbl_runtime_event_202610" in ddl and "TO_DAYS('2026-11-01')" in ddl, ddl[:70]) mig = P.partition_migration_ddl(2, "mariadb") check("P10 分区改造 DDL 含复合主键+MAXVALUE 兜底", any("ADD PRIMARY KEY (`id`, `created_at`)" in s for s in mig) and any("MAXVALUE" in s for s in mig)) try: P.ddl_drop_partition("pbl_runtime_event_202610") P.ddl_drop_partition("pbl_entity_state") check("P10 拒绝删除非事件月分区", False) except ValueError as exc: check("P10 拒绝删除非事件月分区", True, str(exc)[:40]) fails = [r for r in RESULTS if not r[0]] print("\n==== M11b SELFTEST: %d/%d PASS ====" % (len(RESULTS) - len(fails), len(RESULTS))) for _ok, name, detail in fails: print(" FAIL %s %s" % (name, detail)) return 1 if fails else 0 if __name__ == "__main__": sys.exit(main())