fix(p2p): start beacon range sync after the store's head - #645
MegaRedHand wants to merge 1 commit into
Conversation
`beacon_fetched_through` advances only when a range answer is forwarded. Once a follower catches up it imports from gossip, so the watermark stays at its last range batch while the head moves on. Every peer that connects later advertises a head above it, and its Status answer starts a range session from the stale watermark: every block gossip delivered since is fetched again, data columns included, and each one costs the chain actor an import attempt and a head recompute. On the Platåberget follower this re-fetched 748 blocks in 66 bursts in under three hours, each burst as long as the slots since the one before. The largest, 67 blocks, held a gossip block 4.2 s in the actor's queue. `beacon_sync_target` now measures a peer against the higher of the watermark and the store's head. The watermark still covers a batch that was delivered but not yet imported, which is why it replaced the head in the first place. The head covers gossip, and it is a safe floor because a head block's whole ancestry is already imported.
🤖 Kimi Code ReviewI'll review this PR focusing on the beacon sync target logic changes. This is a networking/syncing fix, not consensus-critical state transition code, but correctness still matters for liveness. Overall AssessmentGood fix for a real operational issue. The change is well-documented with clear problem/solution explanation. Two subtle issues to address. Issues Found1. Potential inconsistency:
|
| Priority | Item | Action |
|---|---|---|
| Low | beacon_head() None handling |
Verify against build_status; consider expect or document |
| Low | Missing test for equal floors | Add test |
| Trivial | Comment grammar | Fix "has the chain" → "has in the chain" |
The core logic is correct and the operational data (11,213 blocks → 1.7:1 ratio, 748 blocks in 66 bursts) validates the fix.
Automated review by Kimi (Moonshot AI) · kimi-k2.6 · custom prompt
🤖 Claude Code ReviewReview of PR 645: start beacon range sync after the store's headI read the change and ran no tests. It looks correct and well-scoped. What changed: Why it holds up
Nits (non-blocking)
Approve. Automated review by Claude (Anthropic) · sonnet · custom prompt |
🤖 Codex Code ReviewFindings
Why it looks correct
If you want, I can also do a second pass specifically on denial-of-service angles in the beacon req/resp handlers. Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
🗒️ Description / Motivation
A beacon follower that has caught up keeps downloading blocks it already has.
P2PServer::beacon_fetched_throughis the watermark range sync starts from, and onlyhandle_beacon_blocks_by_range_responseadvances it. Once the follower is at the tip it imports from gossip, so the watermark stays at its last range batch while the head moves on. Every peer that connects later advertises a head above the watermark, and itsStatusanswer starts a range session from there:Each re-fetched block also brings its custody columns (
request_beacon_data_columns_by_rangeruns for the same span), and each one costs the chain actor an import attempt that ends at "already in the store", followed by a head recompute.Observed on the Platåberget follower (build
c184e736) between 14:08Z and 17:02Z on 2026-10-01:cascade_ms~30-47 ms +get_head_ms~20 ms)What Changed
crates/net/p2p/src/req_resp/handlers.rs:beacon_sync_targettakes the store's head slot and measures the peer againstmax(fetched_through, head_slot).handle_status_responsereads the head withStore::beacon_head, the same whole-block decodebuild_statusalready does for every handshake we send, and logs it besidefetched_through.crates/net/p2p/src/lib.rs: thebeacon_fetched_throughdoc says only range answers advance it.docs/beacon_wire.md,CLAUDE.md: describe the two floors.Correctness / Behavior Guarantees
Tests Added / Run
beacon_sync_target_starts_after_a_head_gossip_moved_past_fetched_through(new): a head above the watermark moves the start tohead + 1; a peer at or behind the head getsNone.beacon_sync_target_keys_off_fetched_through_while_the_head_lags(was..._not_store_head): same assertions, now with a head below the watermark passed in explicitly.beacon_sync_target_is_bounded_by_max_sync_range: updated for the new argument.Not deployed to a follower yet.
Related Issues / PRs
✅ Verification Checklist
make fmt: cleanethlambda-p2pwith-D warnings: cleanethlambda-p2punit tests: passing