[R3-01][high] 定时分发、超限拒绝和撤回后没有唤醒推送 #65

Closed
opened 2026-09-30 17:18:24 +08:00 by nixevol · 2 comments
Owner

现象

基线 origin/main 0c9b459。推送 worker 只在 WakePush 时跑一轮 PushPending,下面三条路径漏了唤醒,在线空闲时消息或回执会停住。

  1. 定时或延迟消息到点后,dispatchDueBatch 只把仍为 pending 的接收方放进唤醒集合。当场变成 dropped / rejected / completed 并写入的回执不会叫醒发送方。发送方若已握手且之后不再自己收发,回执一直留在 receipts.acked=0。立即发送在 Submit 里会 WakePush(senderID),这条到点路径没有。
  2. PushPending 按窗口取出一批。前面若干条因超过 max_receive_bytes 或包上限被 rejectTooLarge 时,只 WakePush 发送方。这一轮结束后 worker 回去等待。排在后面、仍是 pending 且 pushed_conn IS NULL 的正常消息不再推。接收方一直在线、不保留且 expire_at 为空时,到期循环也不会清掉它们。
  3. Recall 把 pending 改成 recalled 并清掉 pushed_conn,窗口空出格子,但只 flushRevokes,没有 WakePush 接收方。确认路径会唤醒,撤回不会。窗口里若只占着被撤回的那几条,后面排队的消息不再下发。

位置

  • internal/app/message/push.go:dispatchDueBatch 只唤醒 pending 接收方;too_large 分支只唤醒发送方
  • internal/app/message/ack.go:Recall 成功后没有 WakePush
  • 对照:internal/app/message/submit.go 的 WakePush(senderID),Ack 里对接收方的 WakePush

方案

  • dispatchDueBatch 在本条已分发时,除 pending 接收方外,始终 WakePush 发送方(有回执或终态都要)。
  • too_large 拒绝后,若本轮没有成功占满窗口,对当前接收方再 WakePush 一次,让 worker 继续取后面的 pending。不要在同一次 PushPending 里递归把整表扫完。
  • Recall 对每个被改成 recalled 的接收方 WakePush。已推送的撤回通知仍走现有 flushRevokes。
  • 补测试:定时到点且接收方离线时,在线发送方能拿到回执;一批超限后面还有小消息时,小消息会被推送;撤回在途消息后,同连接上更晚的 pending 会继续推。

约束

只改 internal/app/message/ 与对应测试。不改 PublishDown 签名。测试监听 :0,数据放临时目录。偏差写入 docs/DEVIATIONS.md 新节 ### 复审修复 R3-01。不要合 feat/fix-3-downlink-deadlock。

## 现象 基线 `origin/main` `0c9b459`。推送 worker 只在 `WakePush` 时跑一轮 `PushPending`,下面三条路径漏了唤醒,在线空闲时消息或回执会停住。 1. 定时或延迟消息到点后,`dispatchDueBatch` 只把仍为 `pending` 的接收方放进唤醒集合。当场变成 `dropped` / `rejected` / `completed` 并写入的回执不会叫醒发送方。发送方若已握手且之后不再自己收发,回执一直留在 `receipts.acked=0`。立即发送在 `Submit` 里会 `WakePush(senderID)`,这条到点路径没有。 2. `PushPending` 按窗口取出一批。前面若干条因超过 `max_receive_bytes` 或包上限被 `rejectTooLarge` 时,只 `WakePush` 发送方。这一轮结束后 worker 回去等待。排在后面、仍是 `pending` 且 `pushed_conn IS NULL` 的正常消息不再推。接收方一直在线、不保留且 `expire_at` 为空时,到期循环也不会清掉它们。 3. `Recall` 把 `pending` 改成 `recalled` 并清掉 `pushed_conn`,窗口空出格子,但只 `flushRevokes`,没有 `WakePush` 接收方。确认路径会唤醒,撤回不会。窗口里若只占着被撤回的那几条,后面排队的消息不再下发。 ## 位置 - `internal/app/message/push.go`:`dispatchDueBatch` 只唤醒 pending 接收方;`too_large` 分支只唤醒发送方 - `internal/app/message/ack.go`:`Recall` 成功后没有 `WakePush` - 对照:`internal/app/message/submit.go` 的 `WakePush(senderID)`,`Ack` 里对接收方的 `WakePush` ## 方案 - `dispatchDueBatch` 在本条已分发时,除 pending 接收方外,始终 `WakePush` 发送方(有回执或终态都要)。 - `too_large` 拒绝后,若本轮没有成功占满窗口,对当前接收方再 `WakePush` 一次,让 worker 继续取后面的 pending。不要在同一次 `PushPending` 里递归把整表扫完。 - `Recall` 对每个被改成 `recalled` 的接收方 `WakePush`。已推送的撤回通知仍走现有 `flushRevokes`。 - 补测试:定时到点且接收方离线时,在线发送方能拿到回执;一批超限后面还有小消息时,小消息会被推送;撤回在途消息后,同连接上更晚的 pending 会继续推。 ## 约束 只改 `internal/app/message/` 与对应测试。不改 `PublishDown` 签名。测试监听 `:0`,数据放临时目录。偏差写入 `docs/DEVIATIONS.md` 新节 `### 复审修复 R3-01`。不要合 `feat/fix-3-downlink-deadlock`。
Author
Owner

????????? (#65)?

  • ??:feat/fix-r3-65-wake
  • commit:b79bba391149877fc4fef9d374eb4bd0c192b5e3
  • ??:dispatchDueBatch ?????? WakePush ???;too_large ??????? WakePush ???;Recall ? recalled ??? WakePush(????? flushRevokes)
  • ??:go test ./internal/app/message/ ??;task check ??
  • ??? origin/feat/fix-r3-65-wake,?? main
????????? (#65)? - ??:`feat/fix-r3-65-wake` - commit:`b79bba391149877fc4fef9d374eb4bd0c192b5e3` - ??:`dispatchDueBatch` ?????? WakePush ???;`too_large` ??????? WakePush ???;`Recall` ? recalled ??? WakePush(????? flushRevokes) - ??:`go test ./internal/app/message/` ??;`task check` ?? - ??? `origin/feat/fix-r3-65-wake`,?? main
Author
Owner

已合入 main:443a6c6 (#65);当前 origin/main HEAD 2d1dfd7。

已合入 main:`443a6c6` (#65);当前 origin/main HEAD `2d1dfd7`。
Sign in to join this conversation.