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