fix: 测试 WS 客户端按字节流拆包并恢复群事件同步下发

This commit is contained in:
Nixevol
2026-09-30 16:24:31 +08:00
parent 5f91a7758b
commit 09fb544b7d
6 changed files with 265 additions and 67 deletions
+18 -3
View File
@@ -1454,21 +1454,24 @@
- 实际做法:WebSocket 升级成功与 TCP dial 成功后 `SetDeadline(time.Time{})`,避免长会话在 dial timeout 到期后读写全部失败。
- 原因:F10 等短宽限仍需跨数秒保持连接;未清 deadline 时旧 10s dial 会在会话中途使 Recv 失败,表现为 `timeout waiting resp`。
- 备选方案:每次读写刷新 deadline(更繁琐)。
- 影响:跨线改了 harness;行为仅更正测试客户端,不改产品。
- 影响:跨线改了 harness;行为仅更正测试客户端,不改产品。
- **T-01 注**:清 deadline 的做法仍保留。当时把部分 `timeout waiting resp` 归因于服务端死锁的判断已被 issue #3 复审(T-01)取代。
3. **F15 带密建群用独立短生命周期进程**
- 原条款:拉进群须当次带对话密码。
- 实际做法:主会话用 `group.create` 无密断言失败;带密成功在干净进程上立刻建群。
- 原因:与第 4 条同一死锁,补测时先用隔离进程覆盖校验路径。
- 备选方案:仅依赖第 4 条修复后在同一长会话上测 `group.add`。
- 影响:验收覆盖仍成立。
- 影响:验收覆盖仍成立。
- **已被 T-01(issue #3 复审)取代**:不是服务端死锁;带密拉人已改回同一长会话 `group.add`。
4. **群事件 `emit` 改为异步 PublishDown**
- 原条款:群变更向成员推 `group_event`(QoS 0)。
- 实际做法:`internal/app/group/app.go` 的 `emit` 在独立 goroutine 里延迟约 20ms 再 `PublishDown`,让上行 worker 先把 `resp` 推完。
- 原因:同一连接上 `group.create`/`group.add` 同步向本连接注入下行时,与 mochi InlineClient 互相等待,`resp` 回不去(`TestUplinkDMOfflineGroupRecall` 在清掉测试客户端 dial deadline 后稳定复现)。
- 备选方案:broker 层对 Inline 发布做无锁队列。
- 影响:`group_event` 可能略晚于 `resp` 到达;业务结果仍以 `resp` 为准。
- 影响:`group_event` 可能略晚于 `resp` 到达;业务结果仍以 `resp` 为准。
- **已被 T-01(issue #3 复审)取代**:20ms 是误诊绕过;`emit` 已改回同步 QoS 0。`feat/fix-3-downlink-deadlock` 仍不合入。
### fix-issue-1
@@ -1517,6 +1520,8 @@
### 死锁未修(issue #3)
> **已被 T-01 取代(2026-09-30)**:下列「服务端 InlineClient 死锁」结论经实证不成立。真实根因是测试用 WebSocket 客户端 `test/harness/mqtt.go` 不按字节流拆包、并发写不加锁。保留原文供追溯。`feat/fix-3-downlink-deadlock` 与工作树 `E:\code\NixMsg-wt\fix3` 仍不合入。
issue #3 未关闭,`feat/fix-3-downlink-deadlock` 未合入 `main`。下面是核对过的调用链、三次尝试和仍留在 `main` 上的绕过。不改产品行为。
1. **现象**
@@ -1683,3 +1688,13 @@ issue #3 未关闭,`feat/fix-3-downlink-deadlock` 未合入 `main`。下面是
- 原因:未转义时搜 `e_ab` 会命中 `exab…`,搜 `%` 返回全部端。
- 备选方案:在 `internal/protocol.ValidEndpointID` 加保留字,使开通/导入一并拒绝(否决,本波不改 protocol 与 `internal/admin/endpoints.go`)。后台开通与批量导入仍可能使用 `inline`,留给后续波次。
- 影响:目录下划线按字面匹配;自助注册不能占用 `inline`。
### 复审修复 T-01
1. **测试 WS 客户端按字节流拆包并加写锁;群 emit 改回同步 QoS0**
- 日期:2026-09-30
- 原条款:MQTT 5.0 §6 [MQTT-6.0.0-3](不得假设控制包与 WebSocket 帧对齐);PRD F16 群事件尽力推送、没有延迟 20ms;issue #3 复审「结论与统一方案」。
- 实际做法:`test/harness/mqtt.go` 增加接收缓冲与写锁;`Recv` 按 MQTT 剩余长度拆包,opcode `0x2`/`0x0` 载荷都追加,`0x9` 在写锁内回 PONG;`Send` 与 PONG 持锁且整帧一次 `Write`。`internal/app/group/app.go` 的 `emit` 去掉 goroutine 与 `time.Sleep(20ms)`,在调用方上下文同步 `PublishDown`(QoS 0)。F15 去掉独立短进程,在同一长会话用 `group.add` + 当次对话密码。未合入 `feat/fix-3-downlink-deadlock`。
- 原因:`resp` 回不来是测试客户端丢掉合帧里的后续 MQTT 包,以及并发写把发往服务端的流写乱;不是服务端死锁。
- 备选方案:合入 fix3 的 broker `OnPublish`/延后队列(否决,基于错误假设,还会改 PUBACK 语义)。
- 影响:跨线改了总控目录 `test/harness` 与身份线 `emit`;不改 `internal/broker/`。所有经 `DialMQTTWebSocket` 的测试客户端一并受益。