merge: broker b01-b12
This commit is contained in:
@@ -1207,3 +1207,111 @@ issue #3 未关闭,`feat/fix-3-downlink-deadlock` 未合入 `main`。下面是
|
||||
5. **建议的正确方向**
|
||||
- 在 broker 把对本连接的下行 `InjectPacket` 与上行 worker 解耦:上行读循环先写完 PUBACK,处理 `HandleUplink` 期间不要同步向本连接注入;handler 返回后再发 `resp` 和 `group_event`。不要靠固定 `Sleep`。`InlineClient: true` 保持,`OnPublish` 对 InlineClient 继续放行。
|
||||
- 覆盖 presence 等其他同步 `PublishDown`,而不只包一层 `emit`。
|
||||
|
||||
### 复审修复 B-01
|
||||
|
||||
- 日期:2026-09-30
|
||||
- 原条款:DEVELOPMENT 第 5 节装配 mochi;未写客户端 Receive Maximum。Gitea #8。
|
||||
- 实际做法:`OnConnect` 在心跳校正后调用 `cl.State.Inflight.ResetSendQuota(0)`,不 fork mochi。CONNECT 声明的 Receive Maximum 小于 256 时打 warn,连接仍接受。应用层窗口(推送 32、回执 64、在途 resp 等)约束未确认的 QoS 1。
|
||||
- 原因:mochi v2.7.9 在 `sendQuota>0` 时走 `NextImmediate` 递归读锁,并可因补发后删除 inflight 泄漏配额;已验证置 0 绕开整条路径。
|
||||
- 备选方案:fork 修补 mochi(只修递归读锁仍观察到停滞)。
|
||||
- 影响:服务端不再执行客户端 Receive Maximum;裸设备若带过小的 Receive Maximum,实际在途可能超过该值。
|
||||
|
||||
### 复审修复 B-02
|
||||
|
||||
- 日期:2026-09-30
|
||||
- 原条款:DEVELOPMENT 7.5 / DEVIATIONS N1/N2 第 4 条:大帧名额在 PUBACK、丢弃、断线时归还。Gitea #9。
|
||||
- 实际做法:`OnQosPublish` 按 PacketID 记下超过 64KiB 的出站包;`OnQosComplete`/`OnQosDropped`/断线按 ID 归还。获取名额最多等 5 秒,超时返回 `ErrLargeFrameTimeout`。Publish 未产生 inflight(无订阅者、队列丢弃)时立即归还。不采用「发布完成即归还」。
|
||||
- 原因:mochi 传给 `OnQosComplete` 的是 PUBACK,没有载荷,旧实现从未归还。
|
||||
- 备选方案:发布后立即归还(会把卡死点挪到消息包那份名额)。
|
||||
- 影响:只在 broker 保留一份全局 64 名额;确认超时仍由消息线踢线/清标记触发断线归还。
|
||||
|
||||
### 复审修复 B-05
|
||||
|
||||
- 日期:2026-09-30
|
||||
- 原条款:DEVELOPMENT 第 5 节连接表;Gitea #12。
|
||||
- 实际做法:`OnConnect` 只在认证通过时写入 `byClient`/`byConnID`;拒绝与内部错误不登记。`connState` 增加 `established` 与 `createdAt`,每分钟清扫未建立且已关闭超过 1 分钟的条目。按连接代号查找改为 O(1)。
|
||||
- 原因:mochi 在认证失败路径不调用 `OnDisconnect`,旧实现会永久泄漏。
|
||||
- 备选方案:失败路径也登记再在 Authenticate 返回 false 时删除(仍覆盖不了 CONNACK 失败)。
|
||||
- 影响:失败连接不再占用查找路径;行为对客户端不变(仍回 0x86 或不回 CONNACK)。
|
||||
|
||||
### 复审修复 B-07
|
||||
|
||||
- 日期:2026-09-30
|
||||
- 原条款:PRD §8 日志无正文、无密码、无令牌。Gitea #14。
|
||||
- 实际做法:`broker.New` 给 mochi 包一层 slog.Handler,把 `packets.Packet` / `*packets.Packet` 换成类型、QoS、包号、主题、正文长度。
|
||||
- 原因:默认 info 下第二个 CONNECT、3.1.1 发到错误主题等会把整包写入 JSON 日志。
|
||||
- 备选方案:改 mochi 日志调用点(需 fork)。
|
||||
- 影响:排障时看不到载荷与密码,只见摘要。
|
||||
|
||||
### 复审修复 B-03
|
||||
|
||||
- 日期:2026-09-30
|
||||
- 原条款:DEVELOPMENT 第 5 节每端串行队列;Gitea #10。不改 `PublishDown` 签名。
|
||||
- 实际做法:每连接独立下行队列(256 帧 / 16MiB)和发送 goroutine。`PublishDown` 只入队;发送与上行读循环解耦。队列满返回 `ErrBackpressure`。
|
||||
- 原因:同连接同步 `InjectPacket` 与读循环写 PUBACK 会互相等待。
|
||||
- 备选方案:改 `PublishDown` 签名或继续用 20ms sleep。
|
||||
- 影响:调用方入队即返回;慢客户端只挡住该连接的发送 goroutine。
|
||||
|
||||
### 复审修复 B-06
|
||||
|
||||
- 日期:2026-09-30
|
||||
- 原条款:Gitea #13。`PublishDown` 校验当前连接与下行订阅;导出有效载荷上限。
|
||||
- 实际做法:非空 `connID` 必须仍是当前连接。未订阅 down 返回 `ErrNotSubscribed`。导出 `EffectivePayloadLimit`(Maximum Packet Size 减 128 字节包头预留)。`uplink.publishResp` 改用该函数。新连接建立时把旧连接标为 `superseded`。
|
||||
- 原因:旧连接或未订阅时写入会静默失败或写错连接。
|
||||
- 备选方案:发送时再检查(入队后连接可能已换)。
|
||||
- 影响:无订阅时下行立即失败,不再占用大帧名额。
|
||||
|
||||
### 复审修复 B-04
|
||||
|
||||
- 日期:2026-09-30
|
||||
- 原条款:Gitea #11。写出后再断开,不用固定 sleep。`serve.go` 只改 `identity.New` 的 ConnControl。
|
||||
- 实际做法:`PublishThenDisconnect` 把帧与断开原因一并入队,发送 goroutine 写完再 `Disconnect`。logout / fatalKick 改走该原语。`identity.New` 的 `ConnControl` 置 nil,踢线仍走 Session 钩子。
|
||||
- 原因:固定 20ms/50ms sleep 在慢客户端上会先断开,在快路径上又多余等待。
|
||||
- 备选方案:继续 sleep;或改 identity 生命周期(本线不改)。
|
||||
- 影响:identity 未接 ConnControl 时不再自己 20ms 踢线,生产路径统一由 Session 写出后断开。
|
||||
|
||||
### 复审修复 B-09
|
||||
|
||||
- 日期:2026-09-30
|
||||
- 原条款:Gitea #16。生命周期串行化。不改 presence/app.go。
|
||||
- 实际做法:broker 与 `appUplink` 按端编号加锁串行 `OnSessionEstablished` / `OnDisconnect` / 握手。uplink 另记 hello 握手表,仅已握手连接的断开才按当前连接通知消息线。
|
||||
- 原因:顶号时旧连接 `OnDisconnect` 可能和新连接登记交错。
|
||||
- 备选方案:改 presence 在线表(超出本线允许文件)。
|
||||
- 影响:未 hello 的断开不再把消息连接表当成已握手在线来清推送标记。
|
||||
|
||||
### 复审修复 B-10
|
||||
|
||||
- 日期:2026-09-30
|
||||
- 原条款:Gitea #17。登录写库条件更新;hello 重读令牌。不改 identity/self.go。
|
||||
- 实际做法:密码登录 `UPDATE ... WHERE COALESCE(session_hash,'') = 读到的旧值`,影响行数为 0 则 `ErrSessionWriteConflict`。hello 用 `TokenMatchesDB` 核对明文,库已被换则响应里不带回旧令牌。
|
||||
- 原因:两处同时密码登录会互相覆盖;hello 可能把已作废明文交给客户端。
|
||||
- 备选方案:写库后无条件返回本次签发明文。
|
||||
- 影响:写冲突时 OnConnect 返回 error(不回 0x86),客户端按网络故障重连。
|
||||
|
||||
### 复审修复 B-11
|
||||
|
||||
- 日期:2026-09-30
|
||||
- 原条款:Gitea #18。令牌闲置按在线计。
|
||||
- 实际做法:闲置判断取 `session_used_at` / `online_since` / `offline_since` 的较新者;当前在线(`online_since >= offline_since`)视为未闲置。
|
||||
- 原因:只看 `session_used_at` 会让长期在线却很少写库的令牌过期。
|
||||
- 备选方案:在线时每小时强制刷新 used_at(已有 touch,但仍可能窗口不够)。
|
||||
- 影响:在线设备不会因为闲置天数被踢;离线后从最后一次在线/离线时刻起算。
|
||||
|
||||
### 复审修复 B-12
|
||||
|
||||
- 日期:2026-09-30
|
||||
- 原条款:Gitea #19。认证超时与每端校验并发。不改 auth 池/PHC。
|
||||
- 实际做法:`Authenticate` 套 30 秒超时;argon2 `Verify` 前每端信号量 2。`OnConnect` 同样带 30 秒 ctx。
|
||||
- 原因:慢哈希或卡住的校验会堵住 mochi 读循环;同一编号并发登录会打满全局哈希池。
|
||||
- 备选方案:改全局 Pool 大小(超出允许文件)。
|
||||
- 影响:超时表现为内部错误断开(不回 0x86)。
|
||||
|
||||
### 复审修复 B-08
|
||||
|
||||
- 日期:2026-09-30
|
||||
- 原条款:Gitea #15。Shutdown API。完整 HTTP 停机依赖 L-03。
|
||||
- 实际做法:`Broker.Shutdown` 对现有连接发 MQTT 5 `0x8B`,清空上行队列并 `Close`。`serve` 在 listener Close 之前调用。HTTP `Shutdown` 留给 L-03。
|
||||
- 原因:只关 listener 时 MQTT 客户端看不到规范的停机原因码。
|
||||
- 备选方案:等 L-03 一并做(本线仍提供 broker API,避免监听线无法调用)。
|
||||
- 影响:进程退出时端会收到 server shutting down;监听器 HTTP 优雅停机仍未做。
|
||||
|
||||
Reference in New Issue
Block a user