跳转至

消息持久化与完整客户端完成度审计

日期:2026-08-23 范围:main、store/redis、store/scylla、当前工作树、服务端持久化链、SDK/宿主/绑定与发布门禁。

历史快照说明:本文保留审计当时的反证与分工,不再作为当前完成度首页。 Scylla 当前状态以 delta_20260823_scylla_current_store_contract.md 和最新真实 RF3 门禁为准;Redis retention v2、qsession topic identity、客户端修复与发布阻断 也以各自后续 delta/diagnosis 及 CLAUDE.md 为准。文中的 Kafka transaction、 transactional.id 与“事务 backlog”结论是当时快照,已被 ADR-0012 的无事务 durable-delivery → source-checkpoint 顺序取代。

1. 判定口径

本审计把状态严格分为四类:

  • 已证明:当前代码与契约一致,并有可复现的目标层级测试证据。
  • 代码完成、门禁缺失:静态实现或模拟测试存在,但 TestPlan 要求的真实进程/真实后端故障证据未执行。
  • 修复中:已确认根因并已分配实现,完成前仍按发布阻断处理。
  • 未完成/被反证:当前实现或门禁与契约直接冲突。

单元测试、内存 fake、30 秒冒烟或文档中的 [x] 不能替代真实后端、真实进程崩溃、分支 ref 与目标硬件证据。

2. 分支与可复现性

对象 当前事实 判定
main ref 为 5715ac0;durable commit、Redis MessageStore、Redpanda、Fanout 与新客户端主要存在于未提交工作树 未完成
store/redis main...store/redis = 11 / 0,是旧祖先且无 Redis 独有提交;关键新文件不在 ref 未完成
store/scylla main...store/scylla = 11 / 16;仍是旧提交模型 未完成
fix/store-scylla-persistence-20260823 main...fix = 11 / 17;有旧模型 LWT 修复,但不实现当前 MessageStore/MailboxStore 契约 不可合并
当前工作树 98 个已跟踪文件变化、55 个未跟踪文件;关键实现尚未形成可复现提交 发布阻断

安全同步顺序只能是:先在干净可审计的 main 提交当前共享实现并过门禁,再把 main 单向合入两条存储线;不能把旧 Scylla API 反向并回主干。

3. Must Have 矩阵

ID 当前状态 证据与缺口
M-COMMIT-01 代码完成、门禁缺失 DurableMessageCommitter 只在 MessageRecord 与 Outbox 完成后返回 CommittedMessage;缺“独立进程在 ACK 前读取两项事实”的发布测试
M-COMMIT-02 未完成 有 Reserved 恢复向量;无 dedup 占位后真实 kill -9 writer 门禁
M-COMMIT-03 未完成 有 Recorded 恢复向量;无 MessageRecord 后、Outbox 前真实进程崩溃门禁
M-COMMIT-04 未完成 有 Outboxed 恢复与重复日志收敛测试;无 Outbox 后、COMMITTED 前真实进程崩溃门禁
M-COMMIT-05 已证明 gateway 严格拒绝空、短、全零 cmid/conversation id,并有协议/发送测试
M-LOG-01 已证明 事务 consumer/fake 故障测试与真实 Redpanda ignored 门禁覆盖不前移源位点
M-LOG-02 已证明 确定性 dispatch id/内容向量与重复 Outbox 测试覆盖
M-MB-01 代码完成、门禁缺失 重放复用 canonical mailbox seq 的内存/Redis 测试通过;缺真实 mailbox 进程崩溃门禁
M-MB-02 代码完成、门禁缺失 append 失败不标 chunk、不推进 W 的测试通过;缺真实故障恢复集合证明
M-MB-03 代码完成、门禁缺失 启动按 min(W)+1、W/W_floor 自检已实现;缺保留 Redis、丢消费位点后的真实接管门禁
M-REDIS-01 已证明 epoch=32 相邻 offset 精确排序测试通过
M-REDIS-02 已证明 跨日归并与逆序 created_at 测试通过
M-REDIS-03 已证明 损坏编码整页失败、游标不推进测试通过
M-REDIS-04 未完成 standalone 启动保护已实现;没有三节点 Cluster 端点下服务非零退出、readiness 不绿、零写入的真实负向门禁
M-SCYLLA-01 修复中 旧分支有 LWT/CAS,但不符合当前共享契约;当前工作树正在重做后端与并发初始化测试
M-SCYLLA-02 修复中 旧段租约模型与当前水位契约不兼容;当前实现要求不以未发号段制造永久洞
M-SCYLLA-03 未完成 当前 cgroup 500 MB,三节点 RF3 + LOCAL_QUORUM 门禁至少约需 1.021 GB;不能以纯 Rust 测试替代
M-HIST-01 Redis 已证明,Scylla 修复中 Redis/SDK 覆盖 TEXT/CUSTOM/MEDIA、未知 raw type、正文与 sender;Scylla 当前共享实现尚未完成
M-HIST-02 Redis 已证明,Scylla修复中 Redis OLDER/NEWER、limit/max_bytes 与损坏失败有向量;Scylla待同一 conformance 套件
M-AUTH-01 已证明 history store 边界和 PullMembers 均重新鉴权;非成员测试通过
M-AUTH-02 已证明 CreateGroup 与受信 GroupAdd 分流,已有群非成员不能自加
M-CLIENT-01 修复中 SQLite 重开已有覆盖;新发现 apply_batch 失败时内存 dedup 先提交会使重放空批推进游标,正在修复并补回归
M-CLIENT-02 修复中 ACK 丢失对账已有覆盖;新发现 cmid 同毫秒重启/时钟回拨可重号,正在改为持久原子高水位
M-CLIENT-03 客户端路径完成,生产认证修复中 稳定 endpoint/tenant/user 路径与显式连接已完成;GUI/CLI 正式 token、TLS/SNI 与独立 device 输入正在补齐
M-CLIENT-04 已证明 SDK tracked request id 与 GUI/desktop/FFI V2/UniFFI V2 三元终态关联测试通过
M-BRANCH-01 未完成 三个 ref 未包含同一实现,不能各自运行同一 E2E
M-PERF-01 被反证、修复中 当前脚本把 socket write 当实际到达率、没有统一起跑与 ACK/apply 覆盖率门禁;正在修正测量口径

4. 新确认的发布阻断

4.1 SDK 本地持久化与去重非原子

Machine::accept 在 SQLite apply_batch 前修改进程内 dedup。若落库失败,重连从旧 cursor 重拉同批会被全部判重,空批仍可能推进游标,造成永久本地漏消息。修复必须让 SQLite/local_dedup 成为权威,并只在事务成功后提交内存 dedup,或可靠回滚本批变化。

4.2 SDK client_message_id 会在重启/回拨时复用

当前 seed 由 device_id + startup_wall_ms 生成,计数只在内存。相同毫秒重启或系统时钟回拨会复用 cmid,服务端合法地回放旧 ACK 而不 fanout 新正文。修复必须在 LocalStore 原子保留每设备高水位,失败时不得创建 pending 或发送。

4.3 保留期错误没有到达 SDK REBUILD

Redis 正确返回 CursorExpired,但 mailbox 旧路径只记录 warning 后断开,没有回协议 CURSOR_EXPIRED;未 ready/dirty 也没有明确 code。修复必须可靠回错误帧、不推进游标,SDK 对 code 1 进入 REBUILD、对 code 4 保持游标并重试。

4.4 会话分页链路丢字段

gateway 当前丢弃 snapshot_revision/page_cursor/limit,session 永远读取首页,gateway 下行也没有回传 continuation。5000 会话场景最多证明“至少 201 条”,REBUILD 可能在未取全时错误完成。修复必须提供稳定快照/continuation、完整多页测试与快照变化处理。

4.5 性能门禁会假绿

当前实际到达率是 socket 写成功次数除以固定时长;各客户端没有共同起跑点,ACK 与端到端样本覆盖率不作门禁。T-HEAVY 未验证 5000 个唯一会话,五服务 /healthz//metrics 也未抓取。完整门禁必须分别记录 send/committed/applied,固定全局窗口并校验覆盖率。

4.6 客户端仍有生产宿主缺口

GUI/CLI 唯一完整登录路径依赖开发 token 端点和明文;CLI 误把 committed ACK 显示为“已送达”并自行累加未读;Tauri 以 lossy UTF-8 破坏二进制正文;C ABI V2 能力不完整。多设备服务端还把 device 当 user,导致同用户不同设备互踢。

4.7 Fanout 位点落后保留窗口时会静默跳过

旧 consumer 使用 Offset::Stored + auto.offset.reset=earliest。若 fanout 停机超过 Outbox 保留期,已提交位点低于 broker low watermark 时会从新的 earliest 继续,writer 已 COMMITTED 的过期消息没有任何补偿源。修复已改为显式读取 committed offset/low/high、禁用自动 reset,并对缺失或越界位点 fail closed;仍需以 broker 真实 topic 配置而非仅部署声明值验证 retention.ms >= recovery window,并运行真实 Redpanda 过期位点门禁。

4.8 SessionProjection 是易失的,会永久漏会话

Mailbox 在 durable append/W 完成后只给 session 50 ms 入队机会,session Redis 整批写失败也只记 warning。两侧都没有持久重试或重建 worker;某用户第一条新会话投影丢失后,邮箱和历史有消息但会话列表永远缺项。冻结方案是让 qsession 独立消费 durable dispatch 日志,以 Redis 持久每 partition checkpoint;先对全部真实 recipients 幂等 ZADD GT,成功后才推进 checkpoint,启动 catch-up 后才开放列表服务。

5. 已有可复现证据

  • cargo fmt --all -- --check、workspace check/clippy、契约脚本曾在当前工作树通过。
  • 隔离 standalone Redis + 单节点 Redpanda 下 workspace tests 与五服务功能 smoke 15/15 曾通过。
  • 真实 Redpanda 64 分区 readiness 与 24 条事务 backlog 两项 ignored 门禁 2/2 通过,耗时 1.31 秒。
  • Redis MessageStore/MailboxStore 隔离测试 58 项通过;测试实例已精确关闭。
  • SDK、FFI、UniFFI、desktop 定向测试与 wasm no-default check 曾通过。

这些证据只能证明对应层级;不能替代当前修复后的重新回归、三分支 ref、三节点 Redis/Scylla、真实 kill -9、目标硬件 180 秒与混沌门禁。

6. 完成顺序

  1. 修复 SDK dedup/cmid、mailbox cursor error、会话分页与多设备身份。
  2. 完成当前共享契约下的 Scylla MessageStore/MailboxStore 与 conformance 测试。
  3. 修正性能门禁口径并运行短时非发布诊断,验证仪表不会假绿。
  4. 增加真实 writer/mailbox kill-point、Redis Cluster 负向、Scylla RF3 与完整恢复集合门禁。
  5. 由改动所有者确认工作树内容后形成可复现 main 提交,再单向同步两条存储分支并分别运行同一套 E2E。
  6. 仅在用户提供经审计目标速率与目标硬件后运行 180 秒/T-HEAVY/混沌;没有该输入不得宣称容量通过。