fix(network): stop block-sync liveness from exiling healthy peers during local commit stalls#192
fix(network): stop block-sync liveness from exiling healthy peers during local commit stalls#192p0mvn wants to merge 3 commits into
Conversation
Analyzed one file, diff |
86c3a07 to
d6bf9f4
Compare
Evidence recordHow these two defects were independently re-identified (before finding the existing Zebra-side fixes), and what a reviewer can verify. Environment: 8-core/31 GiB host, fresh mainnet syncer peered exclusively with one local archive feeder (~825k blocks) — the single-peer limit that turns "peer-set erosion" into a hard, observable wedge. Full harness and run records: 1. SymptomFresh syncs on (another capture wedged at height 4,800 ~90 s in; a third at 2,800; heights vary with when the first look-ahead backpressure stall lands.) 2. Metrics signature at the wedgeBlock-sync lost its connection; the peer re-offered a block-sync stream; the local service refused it ("no demand"); no recovery. 3. The log line and the code pathSyncer log at the wedge moment: Stream kind 6 is block-sync. The refusal traces to 4. Config cannot fix itRaising 5. Provenance of the fixesSearching history for the mechanism ( 6. A/B confirmation
Repro recipe
Posted by Claude Code on Roman's behalf; evidence gathered during the #190 validation work. |
…ing local commit stalls
27cd6c1 to
c9fcb72
Compare
|
@cursor review |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issue.
Reviewed by Cursor Bugbot for commit 5447869. Configure here.
| // Other peer satisfied requests below the floor. | ||
| // Clear this peer's liveness deadline when no requests remain. | ||
| // Unanswered requests expire separately via `expire_due_timeouts`. | ||
| self.window.clear_liveness_if_idle(); |
There was a problem hiding this comment.
Floor GC leaves probe cap zombie
Medium Severity
After floor GC removes a peer’s last outstanding request, clear_liveness_if_idle drops the liveness deadline but leaves requests_without_block_progress at the probe cap. An unproven peer that never served a body can then neither issue another request nor be disconnected for no-progress, leaving a live but useless connection.
Reviewed by Cursor Bugbot for commit 5447869. Configure here.
Zakura PR node runNo summary was produced — see the workflow log. Inspect the node: |
|
I don't think we can merge as is unfortunately. originally we had to add here we're not resetting that which will result in the same bug as before if a peer is at their cap. even if we do reset that, I think we'll have further problems as well. to me, the cleaner solution is to simply not remove a peer's outbound request if we receive the block from a different peer. That seems like the bug here. The request is already inflight, therefore cannot be cancelled. more generally, we are not doing a good job currently of separating active peer state and global shared state. imo Only active entries should be used by:
Retired entries should be used only by:
I'm also down for adding a request id like we did in headersync, I could see that hardening a lot of state management here. |
|
Superseded by #209 |


Motivation
Block-sync's "no accepted block progress" liveness machinery blames peers for stalls that are actually local. Two related defects, both reproduced live.
Bug 1: Peer race resulting in peer disconnect due to garbage collection
Consider:
As this happened repeatedly, healthy peers could be removed one at a time. In my tests, peer count went from five to zero.
The fix was to update the liveness deadline if the apply queue was non-empty, obsolete requests were removed, and the peer had no outstanding work.
Bug 2: local commit stalls looked like peer failures
Note: I do not have the answer for the commit stalls. This is fixing a different layer.
Suppose a peer sends valid blocks, but the node’s verifier or storage layer stalls. Blocks accumulate in the local applying queue, and no commits land. The liveness mechanism sees no progress and blames the peer.
Now, check the applying queue first. If non-empty, do not disconnect the peer.
If the applying queue were to become empty and the peer were truly dysfunctional, it would still be evicted.