-
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
[2/4] - peer: add new abstract message router #8520
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 as PR comments)
Additionally, you can add CodeRabbit Configuration File (
|
Pull reviewers statsStats of the last 30 days for lnd:
|
32998e6
to
0092a7a
Compare
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.
Overall straightforward.
Biggest request is to move the sendQuery_
mechanics to fn so that we can make broad use of it (seems 100% generic).
I also find myself confused by the Either[error, error]
construction as Either[A, A] == A
for all A. I couldn't see any visible semantics difference here so was hoping you could shed some light on the choice.
Other than that there are some nits, and a more controversial nudge towards not having a closure determine routability but rather do explicit message enumeration. But I know we've disagreed on that before and I can yield if you have confidence that this is the better way.
Replying to #8434 (comment) on the original PR.
I'm not a fan of the protofsm change either. See #8337 (comment).
Good point -- we can simplify the routing logic in
The current interface does not enforce a static set of endpoints. To properly do that, we should remove But if we're keeping the static behavior anyway, and the routing logic will still be enumerated within
If we keep the switch statement, we easily and automatically enforce 1:1 mapping since each message will be routed to exactly one case in the switch.
What is the intended use case here? Non-LND usage is not mentioned anywhere in the PR or commits.
If this dynamic behavior is really needed, we could add a
Bloat can be removed while keeping the basic switch statement. The switch would enumerate all messages statically, and route them to one of the 3 subsystems. All secondary routing could be pushed into the subsystems themselves. |
We don't examine feature bits until the peer struct is actually active, the feature bits checks are effectively runtime checks. Based on feature bits, you may want to register some endpoints, but not others.
Let's say I'm making some custom channel factory protocol. I don't necessarily want to put all the code into lnd as it isn't fully baked yet, but I want to re-use all the components to handle the wire message parsing, etc, etc. With this PR, I can make a For an example where
With just the current switch statement, the above example isn't possible.
I think if you follow this suggestion to its logical conclusion, you'd get something that looks very similar to this PR. With just a call back, you force a single sub-system to effectively handle all messages. The current approach lets the endpoints come and go if they wish. This is also already the case, but maybe less obvious, eg: we only make and route to the existing coop state machine once we initiate the closing process. Stepping back, this is a ~450 line diff, if you look at the final PR where this is used, I really don't think the amount of boiler plat added is more than if we added an entirely new sub-system to implement a new custom protocol. |
This is right. I do think that it will improve the flexibility of the LND architecture at the expense of being able to verify its correctness and how the components interact. Similar to the way that LISP macros massively improve expressivity at the cost of understanding (without good discipline). Are we prepared to make this tradeoff? My instinct is usually against this stuff but I have a pretty extreme bias towards verifiability and can't effectively weigh the benefit of being able to hook into LND from outside of it. |
FWIW, this pattern was used with great success to implement some of the |
0092a7a
to
014fe89
Compare
@Crypt-iQ: review reminder |
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've opened up a nice simplification PR #9010. Other than that my only comment is that I don't think we should leak the actual endpoint handlers via the public interface. Seems super sus.
014fe89
to
511c068
Compare
Using master a base for this PR now. |
511c068
to
5e27fd9
Compare
Pulled in @ProofOfKeags's commits, and rebased on top of master. Also pulled in some from #8960 |
In this commit, we add a new abstract message router. Over time, the goal is that this message router replaces the logic we currently have in the readHandler (the giant switch for each message). With this new abstraction, can reduce the responsibilities of the readHandler to *just* reading messages off the wire and handing them off to the msg router. The readHandler no longer needs to know *where* the messages should go, or how they should be dispatched. This will be used in tandem with the new `protofsm` module in an upcoming PR implementing the new rbf-coop close.
Over time with this, we should be able to significantly reduce the size of the peer.Brontide struct as we only need all those deps as the peer needs to recognize and handle each incoming wire message itself.
With this commit, we allow the `MsgRouter` to be available in the `ImplementationCfg`. With this, programs outside of lnd itself are able to now hook into the message processing flow to direct handle custom messages, and even normal wire messages.
In this commit, we fix a bug that would cause a global message router to be stopped anytime a peer disconnected. The global msg router only allows `Start` to be called once, so afterwards, no messages would properly be routed.
5e27fd9
to
f09c517
Compare
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 fresh. So clean.
I think we can (and should) do away with the globalMsgRouter
field and just replace it with a query to the config. You can also call stop on the condition that p.cfg.MsgRouter != p.msgRouter
// Register the message router now as we may need to register some | ||
// endpoints while loading the channels below. | ||
p.msgRouter.WhenSome(func(router msgmux.Router) { | ||
router.Start() | ||
}) |
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 really wish that go would let you just do p.msgRouter.WhenSome(Router.Start)
but alas...
// We'll either use the msg router instance passed in, or create a new | ||
// blank instance. | ||
msgRouter := cfg.MsgRouter.Alt(fn.Some[msgmux.Router]( | ||
msgmux.NewMultiMsgRouter(), | ||
)) |
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.
😘👌🏼
// globalMsgRouter is a flag that indicates whether we have a global | ||
// msg router. If so, then we don't worry about stopping the msg router | ||
// when a peer disconnects. | ||
globalMsgRouter bool |
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 field is totally unnecessary. I think we just inline the call to cfg.MsgRouter.IsSome()
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.
Ah yeah can do that too: we can check against the config version as it has a life cycle independent of the peer itself.
In this commit, we add a new abstract message router. Over time, the goal is that this message router replaces the logic we currently have in the readHandler (the giant switch for each message).
With this new abstraction, can reduce the responsibilities of the readHandler to just reading messages off the wire and handing them off to the msg router. The readHandler no longer needs to know where the messages should go, or how they should be dispatched.
This will be used in tandem with the new
protofsm
module in an upcoming PR implementing the new rbf-coop close.NOTE: This is a remade version of this branch, see this comment: #8434 (comment)