跳转至

Redis 保留证明与 appendfsync 发布门禁 Delta

日期:2026-08-23 状态:已实现,定向验证通过

行为变化

场景 旧行为 新行为
已过期日期桶的 max seq 直接抬高 user_trim_seq 并删除桶证明 不改标量 trim;保留证明并按当前 after_seq 判定 CursorExpired
seq=100 位于旧过期桶、seq=50 位于新存活桶 错误把 50 也表达为“已裁剪” 游标可能跨过旧桶时失败闭合,存活 50 不被标量 trim 伪称已删
V1/缺少桶证明 可能继续依赖已删除的证明 append/scan 拒绝,要求迁移或分发日志重放
appendfsync=no/未知/缺失 可能通过 AOF 启动检查 启动拒绝
CONFIG GET appendfsync ACL 拒绝 未执行该检查 启动拒绝
QIM_REDIS_ALLOW_UNSAFE_DEV=1 跳过 AOF/noeviction 同时跳过 appendfsync;仍先拒绝 Cluster,且只允许本地端点
slot 工具 对外导出且注释暗示可按槽拆分 仅编译于 qstore 测试,明确不得用于生产 Cluster 拆分

数据格式与部署动作

mbmeta.retention_format_version 从 1 升至 2。部署前必须审计已有 mbmeta: 含 V1 或不成对 bucket_max_seq:<day> / bucket_expires_at:<day> 的用户不能安全滚动 混写。对这部分数据执行既有离线迁移或从分发日志重放后,才允许新版本写入和读取。

Redis 服务账户应在最小 ACL 中授予启动期读取 appendfsync 的 CONFIG GET 权限;发布 配置必须明确为 appendfsync everysec 或 appendfsync always。不提供该权限、设置为 no 或返回未知值均应阻断 ready,而不是降级运行。

定向验证

检查 结果
cargo test --locked -p qim-common redis_runtime --lib 6 通过:允许值、no/未知/缺字段、CONFIG 无权限映射、本地 unsafe-dev 边界及 Cluster 解析门禁。
QIM_TEST_REDIS_ADDR=redis://127.0.0.1:<独立端口>/15 cargo test --locked -p qim-store --test redis_store 16 通过:真实 Redis Lua 覆盖旧过期桶 seq=100 + 新存活桶 seq=50、无标量 TTL trim、缺/旧证明拒绝,以及既有邮箱行为。

Cluster 的“unsafe-dev 仍拒绝”真实三节点负向门禁由既有 crates/qim-common/tests/redis_cluster_rejection.rs 与 docs/designs/delta_20260823_redis_cluster_negative_gate.md 持续覆盖;本 Delta 不引入 任何 Cluster 支持。