Repository navigation
fix(relay): 边缘写队列加上限、慢读客户端被断开,空闲判定计入未收齐的帧,节点间写侧溢出立即断开 - #196
Merged
Merged
Conversation
This was referenced Nov 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
变更摘要 / Summary
中文:修 #191 的三处(审计 BA-53、BA-54、BA-55)。
econn.out里无限增长(fix(relay): 控制帧队列加硬上限,待发 PONG 只留最新一条,边缘 PING 限频 #173 只限了速率)。现在netpoll两种实现(Linux 事件循环、每连接协程)都对写队列里不带信用的帧(ack 为 0:PONG、关闭帧)记账,超过netpoll.MaxCtlBytes(64 KiB)就以netpoll.ErrQueueOverflow断开连接并作废队列;带信用的下行数据仍由流窗口(每流 256 KiB)约束,不受这条限制。策略是「断开慢读的对端」,不对其做背压(它本来就不读)。边缘在OnClose里记 warn(reason=write_queue_overflow)并计relay_edge_dropped_total{reason="write_queue_overflow"};这个指标族出现过才输出,Workers 版的黄金输出不变。OnRead在有没收齐的非 PING 帧时也刷新lastSeen,弱网上传近 1 MiB 的帧超过WSIdle不再被当空闲断开;半个 PING 不算,光发 PING 仍不能赖着。sendLocked置overflow时go c.closeTransport()(持着锁,另起协程);closeTransport对空的ws/done容错,裸构造的测试连接不 panic。未做:issue 里「PONG 与隧道侧
queuePong一样合并」。netpoll 的帧队列不可原地替换(部分写出的头帧不能改),而 64 KiB 的上限已把队列界定住(最多约 500 条 PONG),所以没有增加替换槽的复杂度;需要的话单开 issue。English: Fixes the three items of #191 (audit BA-53/54/55).
econn.outwithout limit (fix(relay): 控制帧队列加硬上限,待发 PONG 只留最新一条,边缘 PING 限频 #173 only limited the rate). Bothnetpollimplementations (Linux event loop and per-connection goroutines) now account frames without credit in the write queue (ack 0: PONGs, close frames); beyondnetpoll.MaxCtlBytes(64 KiB) the connection is closed withnetpoll.ErrQueueOverflowand the queue is discarded. Credit-bearing downstream data stays bounded by the stream window (256 KiB per stream) and is not affected. The policy is to drop the slow consumer rather than apply backpressure (it is not reading anyway). The edge logs a warning (reason=write_queue_overflow) inOnCloseand countsrelay_edge_dropped_total{reason="write_queue_overflow"}; the metric family is only rendered once it has occurred, so the Workers golden output is unchanged.OnReadalso refresheslastSeenwhile a non-PING frame is incomplete, so a slow near-1 MiB upload exceedingWSIdleis no longer closed as idle; half a PING does not count, so PING-only clients still cannot linger.sendLockednow doesgo c.closeTransport()when it setsoverflow(it holds the lock, hence the goroutine);closeTransporttolerates a nilws/doneso bare test connections do not panic.Not done: the issue's "coalesce PONG like the tunnel's
queuePong". The netpoll frame queue cannot replace frames in place (the head frame may be partially written), and the 64 KiB cap already bounds the queue (about 500 PONGs); open a follow-up issue if wanted.关联 issue / Related issues
Closes #191(Refs #172、#173)
改动范围 / Scope
go/)契约与协议变化 / Contract and protocol changes
数据库迁移 / Database migrations
无 / None
测试 / Testing
先写了会失败的测试,再修;把修法逐项临时去掉后,对应用例全部失败(无上限、不刷新活性、不关链路),恢复后通过。 / Tests were written to fail first; with each fix removed temporarily the matching cases fail (no cap, no liveness refresh, link left open), and pass once restored.
netpoll:TestCtlQueueOverflow(对端不读、控制帧排 512 MiB 前必须以ErrQueueOverflow断开)、TestCtlQueueBudgetReleased(带信用的帧不计入上限,写完额度归还),两种实现各跑。 / both implementations.edge:TestSlowReaderPingFloodIsDropped(真实 Poller + 慢读客户端,断开、计指标、登记摘干净)、TestFastReaderPingFloodNotDropped、TestPingFlood新增「没收齐的帧刷新活性、半个 PING 不算」。saas:TestInternodeCtlFlood新增「写侧溢出后链路立即关闭」(真实 WebSocket)。obs:TestMetricsEdgeDropped。Linux 事件循环:交叉编译(arm64)的
netpoll与edge全包测试在容器里跑过,通过。 / Cross-compiled arm64netpollandedgefull suites pass in a Linux container.单元测试 / Unit tests:
pnpm go:check(gofmt、vet、staticcheck、go test -race、生成物新鲜度)通过;另带 Redis / PostgreSQL 跑了 netpoll、edge、obs、saas、personal、dataplane、tunnel 全包,通过。集成测试 / Integration tests: 未跑
pnpm test:integration跨仓端到端 / Cross-repo e2e: 未跑
镜像 / Images: 未跑
pnpm check与 / andpnpm repo:test检查清单 / Checklist
msg为简体中文短句、变量进字段,不含正文与令牌 / No apps, protocol package, migrations or dependencies touched; log messages are short Simplified Chinese sentences with variables in fields and carry no bodies or tokensCHANGELOG.md的 Unreleased / Recorded under Unreleased inCHANGELOG.md需要维护者提供的外部条件 / External prerequisites
无 / None
已知限制 / Known limitations
CHANGELOG.md顶部新增## Unreleased,与 armadra-cloud 的另一个修复 PR(主机一直在线时「最近在线」停在连上的时刻 / last_seen of a continuously connected host stays at its connect time #190)会在这里冲突,后合并的需要手动合并两条。 / This PR adds## Unreleasedat the top ofCHANGELOG.md, which will conflict with the other fix PR (主机一直在线时「最近在线」停在连上的时刻 / last_seen of a continuously connected host stays at its connect time #190); whichever merges second needs to combine the two entries.