Skip to content

[wsutil] Outbox 写协程 panic 时记账与冲刷全部被跳过——「Send(true) − Written ≡ Dropped」恒等式再次破口,帧静默消失且 Dropped 恒为 0 #146

Closed

Description

@zy84338719

现象

wsutil/outbox.go:281-293 的写协程把记账只挂在写返回 error 的分支:

// outbox.go:281
func (o *Outbox) run() {
	defer o.wg.Done()
	defer o.Close()
	for {
		select {
		case msg := <-o.queue:
			if err := o.write(msg); err != nil {
				o.Close()
				o.failInFlight()   // ← 只有 error 分支记账
				return
			}

write 一旦 panic(不是返回 error),GoSafe 收编后 failInFlight/countPending/drain 全部不执行。而文档承诺(outbox.go:218-219):「Send 返回 true 的每一帧都必然闭合进 Written 或 Dropped 之一——『Send(true) 次数 − Written』恒等于 Dropped」。

探针复现(write 对 "boom" panic,3 帧入队):

[async] panic recovered: task=wsutil-outbox: write panic (simulated)
[outbox] Send=true/true/true (3 frames accepted)
[outbox] Written=1 Dropped=0 Rejected=0 Closed=true
[outbox] identity check: Send(true)=3 must equal Written(1)+Dropped(0)=1
[outbox] actually-written frames: ["f1"]

失败场景推演

write 实现 panic(websocket Conn 实现 bug、对 nil 字段写、三方库内部断言)→ 正在写的帧与队列里余下的帧(探针中 "boom" 与 "f3")既不计 Written 也不计 Dropped,"f3" 更是从未写出。调用方按文档 Close → Wait → 关底层连接 收尾,Wait 正常返回(wg.Done 在 unwind 中执行了),以为「都写完了」;监控按恒等式算丢帧差值永远是 0,写坏路径完全失明——这恰是 #105/#117/#123 三轮修复辛苦闭合的口径。

为什么测试没抓到

#105/#117/#123 系列测试只覆盖「写返回 error」与「关闭交错」两个触发面;write panic 路径无用例。且 panic 被 GoSafe 静默收编(只打一行 async 日志),测试断言计数器时很容易恰好不含 panic 场景。

建议修复方向

把 failInFlight()+countPending() 提为 run() 的 defer(panic 展开时同样闭合记账),或在 o.write 调用点包 recover 转 error 走既有失败分支;并在 Dropped 文档补一句「写动作 panic 同样计入 Dropped」。


置信度:高(探针复现:Send(3)=Written(1)+Dropped(0),恒等式差 2;基线 = network 9f3d2c3)

与既有 issue 的差异:#105/#117/#123 都在闭合同一恒等式,触发面分别是「写失败帧不记账」「关闭交错」「拒收混记」;本条是第四种破口——write panic 时整套记账被跳过,机制与修法均不同(delta:#123 后恒等式只对 error 路径成立,对 panic 路径仍假)。

Activity

  1. added a commit that references this issue on Nov 10, 2026
  2. zy84338719 commented on Nov 10, 2026

    @zy84338719
    ContributorAuthor

    已在 v0.9.2 修复并发布 ✅

    变更内容

    本条修复(在 v0.9.2 的版本节里定位到):

    该版本 `v0.9.2` 的其余变更

    关联问题单:#145。

    关联问题单:#146。

    关联问题单:#147。

    Release: https://github.com/oekit/network/releases/tag/v0.9.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions