Skip to content
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

les: refactor serverPeerSet #22297

Closed
wants to merge 3 commits into from

Conversation

zsfelfoldi
Copy link
Contributor

This PR removes the old serverPeerSet and uses NodeStateMachine to realize the peer set logic of clients, similarly to what has already been done with clientPeerSet. This change makes the code more consistent by removing multiple custom peer maps used by different mechanisms. It also allows further client side feature improvements (mostly related to incentivization).
The NodeStateMachine is also extended with another ForEach iterator that iterates on nodes having a certain field set.
Replaces #22221

@zsfelfoldi
Copy link
Contributor Author

Now this PR also fixes #22259

Copy link
Member

@rjl493456442 rjl493456442 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the "node-state-machine" based peerset is the good direction. The biggest benefit is that it allows the subscription easily. So that we can simplify the codebase a bit.

But I don't like the idea This change makes the code more consistent by removing multiple custom peer maps used by different mechanisms.

I think the peerset can be the global object shared by all the internal components in the LES codebase. But all the component-specific fields should be defined in the component package internally(for example, the fields of lightFetcher). Mixing all the fields in the single serverPeer struct sounds a bad idea.

Just put the general feedback here.

@@ -136,10 +136,6 @@ type lightFetcher struct {
chain *light.LightChain // The local light chain which maintains the canonical header chain.
fetcher *fetcher.BlockFetcher // The underlying fetcher which takes care block header retrieval.

// Peerset maintained by fetcher
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason to remove this light fetcher peer Infos here?

AFAIK it's cleaner to maintain the component-specific peer fields in the component inside(e.g. fetcher). And we also have the subscription mechanism so it should be super easy and clean.

While in the current implementation, we incline to put these fields in the shared serverPeer object. It's just too much for it.

}

// subscribe adds a service to be notified about added or removed
// peers and also register all active peers into the given service.
func (ps *serverPeerSet) subscribe(sub serverPeerSubscriber) {
ps.lock.Lock()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are using the "node-state-machine" based peerset in the wrong way.
Essentially it's same with the map-based peerset. While the biggest difference is the nsm peerset supports subscription natively.

Originally, in order to support subscription, we need to implement the registerPeer and unregisterPeer for all the external components(downloader, fetcher, etc). It's annoying. So we should just get rid of these complexity.

In the new peerset, we should define these two methods:

func (ps *serverPeerSet) OnRegister(callback func(node *enode.Node, state nodestate.Flags, peer *serverPeer)) {
	ps.ns.SubscribeField(serverPeerField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) {
		if newValue != nil {
			callback(node, state, newValue.(*serverPeer))
		}
	})
}

func (ps *serverPeerSet) OnUnregister(callback func(node *enode.Node, state nodestate.Flags, peer *serverPeer)) {
	ps.ns.SubscribeField(serverPeerField, func(node *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) {
		if oldValue != nil {
			callback(node, state, oldValue.(*serverPeer))
		}
	})
}

So that all the subscribers only need to subscribe the peerset and define the relative actions

peers.OnRegister(func(node *enode.Node, state nodestate.Flags, peer *serverPeer) {})
peers.OnUnregister(func(node *enode.Node, state nodestate.Flags, peer *serverPeer) {})

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the biggest benefit for this refracting I think(an elegant peerset subscription mechanism).

@@ -501,6 +502,13 @@ func (ns *NodeStateMachine) decodeNode(id enode.ID, data []byte) {
if field, err := decode(encField); err == nil {
node.fields[i] = field
node.fieldCount++
if nodes := ns.fields[i].nodes; nodes != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks very weird to me. The nodes is the bitmap for marking all the nodes with this field. It should be initialized in the beginning. However, it's only initialized when used for the first time. Any particular reason for it? If for the memory usage, I don't think it will use a lot.

@rjl493456442
Copy link
Member

Would you mind also do some changes to the clientPeerset? It's already replaced with an NSM-based set, would be nice to also define a clientPeerSet wrapper to make the code cleaner.

@zsfelfoldi
Copy link
Contributor Author

Would you mind also do some changes to the clientPeerset? It's already replaced with an NSM-based set, would be nice to also define a clientPeerSet wrapper to make the code cleaner.

Yes, I'm already working on it.

@fjl fjl removed the status:triage label Feb 23, 2021
@fjl
Copy link
Contributor

fjl commented Feb 23, 2021

We discussed this on PR triage today. The issue with this PR is that it makes the peer set much harder to understand. The previous implementation is a totally standard map+lock construction. This new version requires knowing about NSM. @rjl493456442 does not like that.

The main reason why @zsfelfoldi proposed this change is because the server pool should have a more direct notification of peer connection. We can do this in another way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants