Durable Session Projection 实现诊断与运行契约¶
已修复的失效窗口¶
旧路径把 MailboxNode 到 qsession 的 TCP Projection 当成唯一输入。qsession 断开、
进程崩溃或 Redis 写失败时,该易失队列中的事件没有可恢复游标,后续会话列表可永久缺项。
本实现让 qsession 独立静态读取已提交的 qim.dispatch。Redis checkpoint 的
next_offset 只有在每个真实收件人的 ZADD convs:{user} GT created_at conversation_id
成功后才通过 Lua CAS 推进;写入完成但 CAS 前中断会重放,GT 保证活跃时间不倒退。
Mailbox TCP 投影保留为低延迟的无 checkpoint 补充,不能影响 durable checkpoint。
启动门禁¶
- checkpoint meta 与每个 partition checkpoint 固定保存 format version、dispatch topic、
partition count、broker
cluster_id与 immutabletopic_id;任一字段不匹配、checkpoint 超出 broker low/high,或 checkpoint 缺失但low > 0,均以SessionProjectionDirty拒绝服务。 - 启动读取的 high watermark 是有限快照。全部 partition catch-up 到该快照后才绑定 session listener;运行期追加记录由同一 consumer 持续消费。
- consumer 不订阅 consumer group、不提交 Kafka offset,且
auto.offset.reset=error。独立QIM_SESSION_DISPATCH_CLIENT_GROUP只用于 librdkafka client identity。 - qcommit 在 inspect 的 watermarks 前后及 consumer readiness 的 watermarks 前后读取 cluster id 和 topic id;任一变化均拒绝启动。topic id 缺失/零 UUID 同样 fail closed,不能以 topic 名、partition count 或人工 generation 环境变量伪造同一日志。
- 运行期 Redis/Kafka/checkpoint 错误将 qsession 标记 dirty,
PULL_SESSION_LISTfail closed, 需要重启从最后成功 checkpoint 恢复。
必需配置¶
| 配置 | 默认/要求 | 含义 |
|---|---|---|
QIM_REDPANDA_BROKERS |
127.0.0.1:19092 |
dispatch broker |
QIM_DISPATCH_TOPIC |
qim.dispatch |
checkpoint identity 的 topic |
QIM_SESSION_DISPATCH_PARTITION_COUNT |
协议 Mailbox shard 默认值 | 必须是固定合法 2 次幂;topic 分区数必须精确匹配 |
QIM_SESSION_DISPATCH_CLIENT_GROUP |
qim-session-static-assign |
仅 client identity,绝不作为恢复 cursor |
QIM_DISPATCH_RECOVERY_WINDOW_MS |
必填正整数 | mailbox 与 qsession 均要求;broker 实际 retention 必须不小于它 |
QIM_SESSION_DISPATCH_OPERATION_TIMEOUT_MS |
10000 |
broker readiness 超时 |
dispatch topic 的 broker retention 必须覆盖 qsession 与 mailbox 的可恢复停机窗口。两者启动
readiness 都通过 Admin DescribeConfigs 读取实际 retention.ms,拒绝缺失、不可解析、-1
或小于 QIM_DISPATCH_RECOVERY_WINDOW_MS 的 topic(包括缺 DescribeConfigs ACL)。low watermark
checkpoint 校验仍是第二道门:即使保留期后来被错误缩短,启动也不会跳到 earliest 或 high
watermark 伪造健康。
此外,durable projection 的最低 broker 能力是标准 Kafka DescribeTopics 的 KIP-516 非零 topic
UUID,以及可读取的 fetch_cluster_id。Redpanda v24.3.6 不提供 topic UUID,qsession 会在
inspect 时拒绝启动;当前部署基线应为支持该字段的 Redpanda v26.2.2 或等价 Kafka broker。没有
该能力时不得使用人工 generation 配置或 Redpanda 私有 Admin API 伪造身份证明。
Canonical 摘要后端¶
QIM_STORE_BACKEND 默认 redis。设置为 scylla 时,qsession 强制要求
QIM_SCYLLA_HOSTS、QIM_SCYLLA_KEYSPACE、QIM_SCYLLA_DC、QIM_SCYLLA_RF,并按
migrate -> connect -> readiness 顺序启动。Redis 在 Scylla 模式仍保存投影 zset、read
watermark 和 checkpoint;canonical conversation summaries 只通过 Arc<dyn MessageStore>
读取 Scylla,连接或 readiness 失败绝不回退 Redis。
验证¶
- 单元测试锁住 checkpoint identity、low watermark gap、越界 cursor、真实收件人去重以及 “ZADD 成功但 checkpoint 未推进”后的重放语义。
- ignored Redis 测试
redis_checkpoint_cas_拒绝越过失败位点要求scripts/integration/run.sh的隔离 Redis,验证真实 Lua CAS 不能从旧 expected offset 越过已推进 checkpoint。 - ignored Redpanda+Redis 测试
redpanda_同名重建_topic_id_变化拒绝旧_session_checkpoint在 KIP-516 broker 上写入旧 checkpoint、删除重建同名单分区 topic 并将新 high 推过旧 offset;它 验证 UUID 改变而低/high 仍覆盖旧 offset 时,qsession 以SessionProjectionDirtyfail closed。