-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
graph: refactor graph.Builder
update handling
#9476
Conversation
Important Review skippedAuto reviews are limited to specific labels. 🏷️ Labels to auto review (1)
Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Straightforward changes, love the small PR! Just one question, otherwise look good!
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.
LGTM 🥇
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.
Love it! Left some questions and suggestions (also timed myself a bit, it took roughly 25min to review, and most of the time is taken on the decomposition part).
graph/builder.go
Outdated
op ...batch.SchedulerOption) error { | ||
|
||
select { | ||
case <-b.quit: |
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 don't think this select is necessary? If it quits shouldn't we make sure the data are persisted on disk first?
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.
so this just keeps the logic mostly as it stands today today. Since today, the AddEdge/AddChannel/UpdateEdge will abort if the quit channel is closed.
however, this did make me realise that this is not exactly as things stand today since today we will abort at any point if the quit channel is closed (since the handling is spun off in a go routine) and not we only check quickly at the start but then still do the persistence until completion.
So just to keep this PR a pure refactor, im going to revert some of the changes so that this still aborts as soon as the quit channel is closed.
Then in a follow up, we can actually change the logic to: wait for the data to be persisted & then any cancellation of these calls should be determined not by the builder's quit channel but instead by the caller's (of AddChan/Node/Update) passed context. sound good?
graph/builder.go
Outdated
case <-b.quit: | ||
return ErrGraphBuilderShuttingDown | ||
} | ||
return b.handleNetworkUpdate(edge, op...) |
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.
Very nice decomposition! This gets me to think, what's the point of creating a handleNetworkUpdate
, when it takes a msg any
, do a type switch there, and call the corresponding addNode/addEdge/updateEdge
, when we already know what we want here? I think we can directly call addEdge
inside AddEdge
etc. Then handleNetworkUpdate
can just be handleTopologyChange
, wdyt?
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.
yeah mostly agree - the reason for this was just so we dont have to repeat the code that is constant across all 3 calls. namely the error handling (see the if IsError(err, ErrIgnored, ErrOutdated) {
check above) and then the topology notifications.
So can remove this but then we are repeating that code in each handler.
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 the other comment btw)
graph/builder.go
Outdated
} | ||
b.wg.Add(1) | ||
go func() { | ||
defer b.wg.Done() |
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.
Any reason we want this to be async?
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.
just keeping the logic as close to today's as possible here since today this part is async. Perhaps doing the topology notifications takes a while, for example. Callers of AddNode/Channel should only be blocked on the DB writes I think
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.
N/A now as i've reverted to some of the previous logic (but this part still remains async there)
The `netann` package is a more appropriate place for this code to live. Also, once the funding transaction code is moved out of the `graph.Builder`, then no `lnwire` validation will occur in the `graph` package.
In this commit, we remove the `processUpdate` method which handles each announement type (node, channel, channel update) in a separate switch case. Each of these cases currently has a non-trivial amount of code. This commit creates separate methods for each message type we want to handle instead. This removes a level of indentation and will make things easier to review when we start editing the code for each handler.
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 the review ya'll!
I realised that for this to remain a pure refactor PR, some of the initial async code needed to be added back just to keep the behaviour where: if the Builder
's quit
channel is closed, then the public calls (AddChan/Node/Update) will continue to error out.
I can create a follow up PR where we actually change this behaviour to: continue till completion or error if the caller's context is cancelled.
Let me know what you think of this and the fixup commit, and then I'll squash it in if everyone is happy with this
graph/builder.go
Outdated
} | ||
b.wg.Add(1) | ||
go func() { | ||
defer b.wg.Done() |
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.
just keeping the logic as close to today's as possible here since today this part is async. Perhaps doing the topology notifications takes a while, for example. Callers of AddNode/Channel should only be blocked on the DB writes I think
graph/builder.go
Outdated
op ...batch.SchedulerOption) error { | ||
|
||
select { | ||
case <-b.quit: |
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.
so this just keeps the logic mostly as it stands today today. Since today, the AddEdge/AddChannel/UpdateEdge will abort if the quit channel is closed.
however, this did make me realise that this is not exactly as things stand today since today we will abort at any point if the quit channel is closed (since the handling is spun off in a go routine) and not we only check quickly at the start but then still do the persistence until completion.
So just to keep this PR a pure refactor, im going to revert some of the changes so that this still aborts as soon as the quit channel is closed.
Then in a follow up, we can actually change the logic to: wait for the data to be persisted & then any cancellation of these calls should be determined not by the builder's quit channel but instead by the caller's (of AddChan/Node/Update) passed context. sound good?
graph/builder.go
Outdated
case <-b.quit: | ||
return ErrGraphBuilderShuttingDown | ||
} | ||
return b.handleNetworkUpdate(edge, op...) |
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.
yeah mostly agree - the reason for this was just so we dont have to repeat the code that is constant across all 3 calls. namely the error handling (see the if IsError(err, ErrIgnored, ErrOutdated) {
check above) and then the topology notifications.
So can remove this but then we are repeating that code in each handler.
graph/builder.go
Outdated
case <-b.quit: | ||
return ErrGraphBuilderShuttingDown | ||
} | ||
return b.handleNetworkUpdate(edge, op...) |
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 the other comment btw)
graph/builder.go
Outdated
} | ||
b.wg.Add(1) | ||
go func() { | ||
defer b.wg.Done() |
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.
N/A now as i've reverted to some of the previous logic (but this part still remains async there)
@bhandras - re-requesting from you just to get your thoughts on the fixup commit (see comment above) |
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.
LGTM ✨
Pending linter fix and the squash, other good to go. |
The point of the `graph.Builder`'s `networkHandler` goroutine is to ensure that certain requests are handled in a synchronous fashion. However, any requests received on the `networkUpdates` channel, are currently immediately handled in a goroutine which calls `handleNetworkUpdate` which calls `processUpdate` before doing topology notifications. In other words, there is no reason for these `networkUpdates` to be handled in the `networkHandler` since they are always handled asynchronously anyways. This design is most likely due to the fact that originally the gossiper and graph builder code lived in the same system and so the pattern was copied across. So in this commit, we just remove the complexity. The only part we need to spin off in a goroutine is the topology notifications.
This logic used to be handled by the router. Update to reflect new owner.
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.
LGTM 🌹
Part of #9475
Some minor refactoring in the
graph.Builder
. The main purpose of which is to separate out the funding transaction validation logic so that reviewing the removal of that later on will be easier.