Storage radius oscillates every wakeup when the reserve is full of out-of-radius chunks, leaking pullsync streams on peers
维护者通常 2 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 新手友好度
- 45/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 活跃
- 技术栈
- go
调研方向
Start with reserveWorker in pkg/storer/reserve.go and the pullsync handler in pkg/pullsync/pullsync.go; trace how radius changes restart the puller and how a reset stream affects the handler waiting in collectAddrs. Reproduce with a full reserve whose within-radius count is below the threshold, then verify the radius stops oscillating and cancelled requests no longer leave handlers blocked. The issue links #5657 and #5658 for related context.
由索引模型根据 Issue 内容生成。
描述
Context
bee v2.8.2 (master has the same code). Mainnet full nodes.
Summary
A node whose reserve is full but holds mostly chunks outside its storage radius flips its radius down and back up on every reserve-worker wakeup (~30 min). Each flip restarts the puller. The puller cancels its sync requests, and every cancelled request leaves an inbound pullsync stream stuck on the peer that served it. The affected peers climb steadily towards the libp2p 5000 inbound-stream limit, after which they refuse all new inbound streams.
1. Radius flip (pkg/storer/reserve.go, reserveWorker)
On each thresholdTicker tick:
if count < threshold(db.reserve.Capacity()) && db.syncer.SyncRate() == 0 && radius > minimumRadius {
radius--
count is the number of chunks within radius. If a reserve is at capacity but most of its chunks have a lower proximity order, count stays below 50% of capacity while IsWithinCapacity() is false. The radius drops (e.g. 9 → 8), the reserve is now over capacity, unreserve raises it back to 9, and the next tick repeats.
Observed on 3 nodes (capacity 4,194,304):
| reserveSize | reserveSizeWithinRadius |
|---|---|
| 4,192,842 | 618,428 |
| 4,193,523 | 1,600,989 |
| 4,193,023 | 2,065,927 |
Healthy nodes in the same neighbourhoods hold ~3.82M within radius 9. These 3 nodes recorded 803 radius changes in 7 days; all other nodes recorded 0. One of them started flipping when a batch expiry pushed its within-radius count from 2,141,002 to 2,065,638, just under the 2,097,152 threshold.
Suggestion: do not decrease the radius while the reserve is at or above capacity, or evict out-of-radius chunks before evaluating the threshold.
2. Each flip leaks streams on peers (pkg/pullsync/pullsync.go)
Each radius change triggers puller.manage/onChange, which cancels sync workers and resets intervals. That produces ~600–970 context canceled sync errors per flip on the flipping node.
On the serving peer, the handler for each cancelled request stays blocked in makeOffer → collectAddrs → SubscribeBin waiting for the first chunk of the interval. The remote stream reset is not observed, and the handler's context is only cancelled on peer disconnect. So each flip leaves ~25–30 stuck streams per serving peer.
Evidence from one affected peer (/debug/pprof/goroutine?debug=2):
- 3783 pullsync handlers blocked in
collectAddrs, 3590 of them on a single connection to the flipping node. The other peers hold a normal 23–27. - The stream count rises by +25–30 within the same minute as each of the flipping node's puller error bursts (6/6 matches over 3 hours).
- The ~2-minute radius-8 window means peers at PO 8 (the sibling half of the neighbourhood) also serve the flipping node, so they are affected too, not only same-neighbourhood peers.
The same server-side behaviour was triggered by a third-party client between September and October, and several nodes hit the 5000 cap. #5657 described the handler not observing resets but was closed. #5658 (a per-peer stream limit with disconnect) would contain the damage, but does not fix the cause.
Suggestion: cancel the handler's context when the stream is reset (or bound the first-chunk wait), plus #5658 as defence in depth, and backoff in the puller.
Reproduce
Any full node whose reserve fills with chunks below its eventual radius (e.g. it synced initially at a lower radius) and whose within-radius count falls under 50% of capacity.
- 主要语言
- Go
- 星标
- 1.5k
- 派生
- 390
- 平均合并
- 2 天 11 小时
- 30 天内合并 PR
- 23
环境准备
- 提供 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
ethersphere/bee 的其他 Issue
-
Chequebook deployment uses a fixed 175,000 gas limit; it needs about 630,000 under Glamsterdam (live on Sepolia since 2026-10-06)可能已有人在做 @sbackend123 于 9 天前认领。 未关闭
难度 2/5 1-3 小时 新手友好度 85/100
ethersphere/bee#5650 · 已指派 1 人 ·
维护者通常 2 天内回复
-
Add a transaction inbox for outbound chain transactions可能已有人在做 @sbackend123 于 3 天前认领。 未关闭seen
难度 5/5 一周以上 新手友好度 35/100
ethersphere/bee#5660 · 已指派 1 人 ·
维护者通常 2 天内回复
-
accepted seen
难度 4/5 3-5 天 新手友好度 40/100
ethersphere/bee#5648 · 1 条评论 ·
维护者通常 2 天内回复
-
pusher: chunksWorker deadlocks on a deferred chunk that fails IdentityAddress (send on nil op.Err), halting all push-sync可能已有人在做 @sbackend123 于 11 天前认领。 未关闭seen
难度 3/5 1-2 天 新手友好度 75/100
ethersphere/bee#5641 · 已指派 1 人 ·
维护者通常 2 天内回复
-
GET /bzz/<ref>/ can 404 off-node, and the 404 is indistinguishable from a missing index document未关闭seen
难度 3/5 1-2 天 新手友好度 52/100
ethersphere/bee#5625 · 1 条评论 ·
维护者通常 2 天内回复
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 82/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 65/100
BurntSushi/toml#530 ·
-
难度 2/5 1-3 小时 新手友好度 77/100
维护者通常 1 天内回复
-
clawsweeper:fix-shape-clear clawsweeper:queueable-fix clawsweeper:source-repro impact:session-state issue-rating: 🦞 diamond lobster no-stale P2
难度 2/5 1-3 小时 新手友好度 74/100
openclaw/wacli#503 · 1 条评论 · 1 个 reaction ·
维护者通常 1 天内回复
-
[Bug] rmqctl error diagnostics emit terminal control characters可能已有人在做 关联的 PR 仍在进行中或已合并。 未关闭
难度 2/5 1-3 小时 新手友好度 81/100
apache/rocketmq-dashboard#6895 ·
维护者通常 3 天内回复