Target Version
3.0-dev。复核基于 origin/3.0-dev 提交 a75fc8721b99e5c0b4421d0c58b5618bbe71ff31(PR #57 )。PR #57 已消除按 message_id 查询 Message 表的读放大,但 ACK 的可信来源与失败收敛仍未闭环。
Evidence
push/source/push_server.h:384-440 校验 WebSocket 连接身份后,先调用 UnackedPush::ack(user_id, device_id, user_seq) 删除待重传项,再选择 Message Service 并异步上报。
同一路径直接使用客户端 ACK 中的 conversation_id/seq_id 构造 UpdateReadAckReq;没有从服务端持久化的 unacked payload/ledger 恢复并校验可信会话序号。
异步回调只检查 Controller::Failed(),没有检查 UpdateReadAckRsp.header.success;通道不可用、传输失败或业务失败时,已删除的 unacked 不会自动恢复。
message/source/message_server.h:447-463 已直接接收 seq_id,不再查询 Message 表;但仍先 require_member_,再执行 update_last_ack_seq,热路径仍是成员查询加条件更新。
common/dao/data_redis.hpp:1842-1855 的 ACK 删除使用同 slot Lua,Redis 内部删除是原子的,但它与 MySQL 水位线推进之间没有 durable handoff。
PR feat(cache): harden Redis resilience and multilevel caching #57 的 GitHub Actions build/service-artifacts 失败,Reliability 与端到端门禁被跳过,因此当前只能确认源码改造,不能确认故障场景已通过。
Problem or Goal
让送达 ACK 使用服务端可信的 conversation seq_id,并在 Redis unacked 删除与 MySQL last_ack_seq 推进之间建立可重试、幂等、可观察的收敛边界。PR #57 已完成“去 Message 表查询”,本 Issue 聚焦剩余的一致性与接口语义。
Scope
从服务端已持久化的 unacked payload/ACK ledger 解析并校验 message_id/conversation_id/seq_id,不信任客户端自报的水位线。
Message Service 上报成功的定义同时包含 RPC 传输成功和 header.success=true。
仅在水位线推进成功后删除 unacked,或先持久化可恢复 ACK intent,保证任一崩溃点可重试。
将成员有效性与 GREATEST(last_ack_seq, seq_id) 尽量合并为一次条件原子 UPDATE。
明确送达 ACK、已读水位线与用户推送序号三个序号域;必要时将内部 RPC 更名为 UpdateDeliveryAck。
增加重复、乱序、伪造、Message Service 故障和 Push 崩溃测试及容量对比。
Non-goals
Acceptance Criteria
Test-first Plan
先扩展 tests/pkg/contracts 的 ACK 语义契约,并增加全栈 RL-ACK-01:在 ACK intent 已记录、Message RPC 前后分别停止 Push/Message,验证恢复后 last_ack_seq 最终推进且 unacked 最终删除。先运行:
cd tests && go test ./pkg/contracts/... -run TestReadAckUsesConversationSequenceWatermark -v -count=1
再运行目标 Reliability 用例。预期 RED 是客户端伪造 seq_id 可推进水位,或 Message Service 故障后 unacked 已删除且无可重试状态;最小 GREEN 是可信 payload/ledger 与 durable handoff。
Risk and Security
客户端可控 seq_id 属于完整性边界。修复必须绑定认证后的 user/device 与服务端持久 payload,防止跨会话推进;重试状态需要 TTL、容量上限和脱敏日志,不能形成无限 ACK backlog。
Architecture Impact
Yes。Push 与 Message 之间新增明确的 ACK durable handoff/ledger 边界,但 MySQL 仍是送达水位线真相源,Redis 仍负责短期重传状态。
Core-flow Impact
Yes。改变客户端 ACK 校验、unacked 删除时机、Message RPC 失败恢复和 last_ack_seq 推进流程。
Required Skill Updates
更新 .agents/skills/chatnow-orienting/references/core-flows.md,区分 write-ahead delivery 与 durable delivery ACK。
更新 .agents/skills/chatnow-orienting/references/technology-stack.md,记录 ACK 真相源、重试状态与指标。
更新 .agents/skills/chatnow-testing/references/case-catalog.md,登记 RL-ACK-01、伪造/乱序/重复 ACK 用例。
若 RPC 更名或契约变化,更新 repository map 与相关接口文档。
Target Version
3.0-dev。复核基于
origin/3.0-dev提交a75fc8721b99e5c0b4421d0c58b5618bbe71ff31(PR #57)。PR #57 已消除按message_id查询 Message 表的读放大,但 ACK 的可信来源与失败收敛仍未闭环。Evidence
push/source/push_server.h:384-440校验 WebSocket 连接身份后,先调用UnackedPush::ack(user_id, device_id, user_seq)删除待重传项,再选择 Message Service 并异步上报。conversation_id/seq_id构造UpdateReadAckReq;没有从服务端持久化的 unacked payload/ledger 恢复并校验可信会话序号。Controller::Failed(),没有检查UpdateReadAckRsp.header.success;通道不可用、传输失败或业务失败时,已删除的 unacked 不会自动恢复。message/source/message_server.h:447-463已直接接收seq_id,不再查询 Message 表;但仍先require_member_,再执行update_last_ack_seq,热路径仍是成员查询加条件更新。common/dao/data_redis.hpp:1842-1855的 ACK 删除使用同 slot Lua,Redis 内部删除是原子的,但它与 MySQL 水位线推进之间没有 durable handoff。build/service-artifacts失败,Reliability 与端到端门禁被跳过,因此当前只能确认源码改造,不能确认故障场景已通过。Problem or Goal
让送达 ACK 使用服务端可信的 conversation
seq_id,并在 Redis unacked 删除与 MySQLlast_ack_seq推进之间建立可重试、幂等、可观察的收敛边界。PR #57 已完成“去 Message 表查询”,本 Issue 聚焦剩余的一致性与接口语义。Scope
message_id/conversation_id/seq_id,不信任客户端自报的水位线。header.success=true。GREATEST(last_ack_seq, seq_id)尽量合并为一次条件原子 UPDATE。UpdateDeliveryAck。Non-goals
MarkRead(last_read_seq)的已读/未读算法。message_id或用户级user_seq当作会话水位线。Acceptance Criteria
message_id查询 Message 表。last_ack_seq只使用从服务端持久状态恢复并校验的 conversationseq_id。header.success=false时,ACK intent/unacked 保持可重试。message_id/conversation_id/seq_id无法推进水位线。user_seq的契约测试相互独立。Test-first Plan
先扩展
tests/pkg/contracts的 ACK 语义契约,并增加全栈RL-ACK-01:在 ACK intent 已记录、Message RPC 前后分别停止 Push/Message,验证恢复后last_ack_seq最终推进且 unacked 最终删除。先运行:cd tests && go test ./pkg/contracts/... -run TestReadAckUsesConversationSequenceWatermark -v -count=1再运行目标 Reliability 用例。预期 RED 是客户端伪造
seq_id可推进水位,或 Message Service 故障后 unacked 已删除且无可重试状态;最小 GREEN 是可信 payload/ledger 与 durable handoff。Risk and Security
客户端可控
seq_id属于完整性边界。修复必须绑定认证后的 user/device 与服务端持久 payload,防止跨会话推进;重试状态需要 TTL、容量上限和脱敏日志,不能形成无限 ACK backlog。Architecture Impact
Yes。Push 与 Message 之间新增明确的 ACK durable handoff/ledger 边界,但 MySQL 仍是送达水位线真相源,Redis 仍负责短期重传状态。
Core-flow Impact
Yes。改变客户端 ACK 校验、unacked 删除时机、Message RPC 失败恢复和
last_ack_seq推进流程。Required Skill Updates
.agents/skills/chatnow-orienting/references/core-flows.md,区分 write-ahead delivery 与 durable delivery ACK。.agents/skills/chatnow-orienting/references/technology-stack.md,记录 ACK 真相源、重试状态与指标。.agents/skills/chatnow-testing/references/case-catalog.md,登记RL-ACK-01、伪造/乱序/重复 ACK 用例。