跳转至

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 与 immutable topic_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_LIST fail 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 以 SessionProjectionDirty fail closed。