-
Notifications
You must be signed in to change notification settings - Fork 20.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor downloader common ancestor span/binary search fns #21063
Conversation
This breaks the common ancestor search functions into SpanSearch and BinarySearch functions, ensuring that a negotiated common ancestor is never above our local head. It makes switch cases conditional on sync mode use LightSync explicitly, which makes the code more readable (instead of relying on LightSync as tacit default). Signed-off-by: meows <b5c6@protonmail.com>
Eliminates a redundant negotiation if we can already deduce that our only common block is genesis. Signed-off-by: meows <b5c6@protonmail.com>
Signed-off-by: meows <b5c6@protonmail.com>
localHeight = d.lightchain.CurrentHeader().Number.Uint64() | ||
default: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've added these in three places, I can't say I think it's a worthwhile improvement, really. Mind reverting that and make the diff smaller?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I like this is that it removes what I would call "incidental" logic, where a reader/developer has to know and use an implicit context; ie. that there are 3 and only 3 downloader sync modes and what they are. And then read between the lines to understand that the d.lightchain
assignment happens tacitly in the case of the downloader's LightSync
configuration (despite that LightSync
isn't the application default). IMO It's taking advantage of a terse logic pattern at the price of readability, and I would prefer explicit readability at the cost of a few lines.
Hypothetically, with the proposed change, if a developer wants to try adding a 4th downloader sync mode on a given weekend, they won't have to spend any time working to understand a mysterious bug that will turn out to be the switch statement not complaining about an unhandled case.
Of course, you're the owner here, and I'm willing to concede the change with no hard feelings, but those are my two cents.
eth/downloader/downloader.go
Outdated
@@ -741,9 +752,36 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) | |||
} | |||
} | |||
|
|||
// Only attempt span search if local head is "close" to reported remote | |||
if remoteHeight-localHeight <= uint64(MaxHeaderFetch) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we, at this point, certain that the remoteHeight is higher than localHeight? What if it's a lower height but higher TD? Shouldn't this check use abs
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeap, I think you're correct. Fixed with 58dd36b
a, err := d.findAncestorSpanSearch(p, remoteHeight, localHeight, floor) | ||
if err == nil { | ||
if a > localHeight { | ||
a = localHeight |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, what happens here? The found ancestor is above our local height?
Ah, that's when we're aware of a higher block, but we don't consider it canon, for TD reasons...
Co-authored-by: Martin Holst Swende <martin@swende.se> Signed-off-by: meows <b5c6@protonmail.com>
Span search depends on absolute distance between remote and local heights. This fixes the case where remote head is greater than MaxHeaderFetch beneath local height. Signed-off-by: meows <b5c6@protonmail.com>
e017bb4
to
58dd36b
Compare
Force pushed to squash a tiny |
eth/downloader/downloader.go
Outdated
if d.blockchain != nil && | ||
d.blockchain.CurrentHeader() != nil && | ||
d.blockchain.CurrentHeader().Number != nil && | ||
d.blockchain.CurrentHeader().Number.Uint64() > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks extremely defensive. Why did you add all these checks?
It looks pretty moot anyway -- if d.blockchain
is nul, you'll get a panic later on when this falls through to e.g localHeight = d.blockchain.CurrentBlock().NumberU64
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing this out. Not one of my prouder moments 🤒 . The original reasoning was that les
tests failed otherwise, since d.blockchain
would be nil. The middle two conditions I can only explain as results of frustrated debugging, and they are unnecessary.
What is currently unaddressed is that light mode is completely excluded from the condition, so common ancestor negotiation would never be attempted during Light sync. Will push a change to fix this very shortly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See 4122416
In addition to being quite terribly ugly, this condition set erroneously excluded the light sync case entirely. This change handles d._chain cases depending on respective sync modes, and fixes the case that light sync mode would never actually use the findAncestor negotiations. Signed-off-by: meows <b5c6@protonmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is ok, but I also think it needs some actual runtime in production for testing.
Can you give us some explanation why refactor? This PR touches a delicate part of the code, so unless there is a good reason to do so, we would rather not change it now. |
@adamschmideg It's a refactoring intended to help repay some technical debt, which may be contributing, at least in part, to the case of this code's delicateness. The |
We don't really feel comfortable with all these changes at once. Could you rewrite this PR to be only the modularization of the |
This is a simple refactoring, extracting common ancestor negotiation logic to named functions. This commit addresses the concern at the following link: ethereum#21063 (comment) --- This is a combination of 6 commits. This is the 1st commit message: downloader: extract span search to function This is a plain refactoring, extracting span search logic to its own function. An error 'errNoAncestor' is introduced to handle the case when the function does not find a common ancestor. Signed-off-by: meows <b5c6@protonmail.com> downloader: extract binary search to function This is a simple refactoring extracting the binary search for common ancestor logic to its own function. A tweak was made to the 'floor' variable(s) which I'm still not happy with, but logic is functional -- passing tests -- for now. Signed-off-by: meows <b5c6@protonmail.com> downloader: fixup floor type and logic to minimize change This minimizes the diff, keeping the logic as nearly the same as possible, limiting the span and binary search logic extraction to functions as cleanly as possible. Signed-off-by: meows <b5c6@protonmail.com> downloader: remove limiting ancestor to localheight This is logic not existing at ethereum/go-ethereum master, and thus is not pertinent to a just-refactor. Signed-off-by: meows <b5c6@protonmail.com> downloader: tweaks to get the diff as clean as possible Signed-off-by: meows <b5c6@protonmail.com> downloader: rename error noAncestor -> noAncestorFound Signed-off-by: meows <b5c6@protonmail.com> sq
MaxHeaderFetch
range of the peer's reported head