-
Notifications
You must be signed in to change notification settings - Fork 448
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
Add support for configuring priority peers (connection tagging) #369
Comments
@vasco-santos @dirkmc I've written down some thoughts around this. It also made me think more about how service configuration works currently and how it can improve, but I've left that out of these notes. I'll look at writing more about that soon and posting a new issue. Peer ManagementPrioritizing PeersLibp2p needs to be able to identify peers that it deems as priority connections, to enable nodes to maintain a connection to peers that are in a critical path for that node to operate. An example of this would be preload nodes for IPFS browser nodes, or signaling servers for webrtc transports. If the connection to these nodes ends, the node may no longer be able to effectively interact with the network, due to current limitations of distributed technologies. Being able to prioritize peers also enables nodes in the network to create and more easily maintain overlay networks to specific peers. A potential example of this could be a webrtc overlay. Assuming nodes in the network supported a signaling spec, as webrtc nodes became aware of other nodes, they could create an overlay network with a subset of those nodes and signify them as priority peers, similar to how Gossipsub overlays are constructed. This could potentially improve the ability of nodes to query unconnected nodes, without relying on peers being initially connected to the same signaling server. Ideally, both peers would agree to this priority connection and avoid disconnecting from one another. If only one peer marks the other as a priority peer, this can lead to disconnects and immediate redials to that peer, which would be unnecessarily taxing for both nodes. This could be especially aggravating for the receiving node if they are at their high watermark for connections. ConfigurationAs the Peer Store (PeerBook in JS) is the central location of Peer data, it makes sense for it to house the metadata marking the Peer as priority. It may be useful to prioritize specific multiaddrs instead of the peers themselves, but as multiaddrs can change over time, via protocols like AutoNAT and AutoRelay, an initial implementation of just tagging the Peer should be more dependable. Similar to how Bootstrap peers are currently configured today, priority peers would be configured via their multiaddr. Peers that were previously in the Bootstrap list would be removed from there, and added to the priority configuration, as those peers will aslo have connections established. Configuration OptionsHere are some potential configuration options Via the config: new Libp2p({
...,
config: {
peers: {
'/ip4/xxx.xx.xx.x/tcp/4001/QmPreload': {
tags: ['Priority']
}
}
}
}) Via methods const libp2p = new Libp2p({ ... })
libp2p.peerBook.tagPeer('/ip4/xxx.xx.xx.x/tcp/4001/QmPreload', libp2p.peerBook.TAGS.Priority) Updates
Additional ThoughtsIt may be valuable to make this a standalone service module that takes a libp2p instance. This would avoid the need to add specific functionality for this to libp2p itself, and would make it easier for other developers to build similar modules that leverage tagging. const PriorityPeerService = require('libp2p-priority-peer-service')
const libp2p = new Libp2p({
modules: {
services: [ PriorityPeerService ]
}
})
libp2p.peerBook.tagPeer('/ip4/xxx.xx.xx.x/tcp/4001/QmPreload', PriorityPeerService.TAGS.Priority) |
This sounds like a good improvement to connection management 👍 Ideally connections would be prioritized by the service the remote peer provides, and there would be a mechanism to discover which peers provide a particular service, so that the configuration doesn't need to be hard-coded (eg discovery could be through bootstrap nodes or a rendezvous service). A less flexible but simpler approach would be to configure a number of candidate peers that provide a particular service, as a means of providing some redundancy and load balancing. Do we want to maintain permanent connections to bootstrap nodes? It may be more fault tolerant and put less stress on those nodes to prioritize them as "Hi-Lo" - high when there are few connected peers and low once a reliable mesh of connections has formed with other peers. |
Yeah, we really don't want to stay connected to them unless we're below our min peers watermark, as they're primarily just an entry point workaround to join the network. We currently have this behavior for all discovered peers with auto dial. If we're above the min peers watermark we stop auto dialing, but below it we do. I think the progression of a node would ideally look something like:
Different node types may end up needing to have different behavior for this, but I think it accounts for a typical node. I think in general if we fall below the min peers watermark, we should be going through our list of known peers to connect and actively find more peers, so bootstrap nodes wouldn't really need to be tagged. We really don't need the bootstrap module at all, as we should just be pulling from our Peer Store when we have too few connected peers. Instead of configuring the list of them when creating libp2p, we could/should just be adding them to the Peer Store. |
@jacobheun thanks for putting this together! I like your proposal and I think that this is definitely the way to go 🚀 I would introduce a This suggestion also makes me think if that should be an array of tags, or properties. I was more thinking on tags for visualization and debugging purposes, but we can also use them in this use case. Also agree with the "Hi-Lo" reasoning for the bootstrap peers! |
I'm looking at implementing this piece of functionality, If you have two components that mark the same peer as Instead tags could be specific to the tagger, for example So if we have tags with a name and a value, then for connection pruning we might just sum up the value of all tags a peer has, use that to order the connections, and prune the low value connections first. Some tags like libp2p.peerBook.tagPeer(PeerId('QmPreload'), 'keep-alive', {
value: 100,
ttl: 60000 // optional, expire tag in 1m
}) => Promise<void>
libp2p.peerBook.removeTag(PeerId('QmPreload'), 'keep-alive') => Promise<void>
libp2p.peerBook.getTags(PeerId('QmPreload')) => Promise<[{
name: 'keep-alive',
value: 100
}]> These could get configured at startup: new Libp2p({
// ...
peerStore: {
peers: {
'QmPreload': {
tags: {
'keep-alive': { value: 100 },
'preload', { value: 50 }
}
}
}
}
}) We might configure bootstrap nodes as New connections might be protected for a few minutes so they can't get culled before identify has completed and any interested topologies have tagged the peer connections as valuable. |
Allow tagging peers to better prioritise which connections to kill when hitting limits. Also for keeping "priority" connections alive. Refs: libp2p/js-libp2p#369
Allow tagging peers to better prioritise which connections to kill when hitting limits. Also for keeping "priority" connections alive. Refs: libp2p/js-libp2p#369
Allows tagging peers to mark some important or ones we should keep connections open to, etc. Depends on: - [ ] libp2p/js-libp2p-interfaces#255 Refs: libp2p/js-libp2p#369
Allow tagging peers to better prioritise which connections to kill when hitting limits. Also for keeping "priority" connections alive. Refs: libp2p/js-libp2p#369
Allows tagging peers to mark some important or ones we should keep connections open to, etc. Refs: libp2p/js-libp2p#369
@achingbrain : I know there has been work here since your last comment. A few things:
|
2022-09-13 triage conversation: we need to summarize where we got to and discuss what can now be done. We believe we provided the functionality originally outlined and now it's about leveraging it i other areas like gossipsub. That will likely translate to a new issue in gossipsub. |
The final piece here is for interested modules to tag peers they need to keep connections open to.
|
I have some comments to tagging with an integer score:
This tagging system appears to me as a complicated scoring scheme that has not been properly researched and could have unintended practical and security considerations. From reading this post a couple times seems that must goals could be achieved without an integer tag value, and instead just expressing a keep alive status like
Even then, all this decisions are extremely opinionated where you are forcing specific paradigms to libp2p consumers. Currently lodestar is fighting libp2p features more than necessary due to their opinionated nature. For example, the connection manager should never be deciding what peers to disconnect on the first place, but instead just enforce some limits set by the user. i.e. If some consumer like IPFS browser wants the default peer manager strategy, then it should buy into it instead of being there by default. A peer manager can be plugged into libp2p easily using the existing APIs. Then multiple peer manager strategies can be developed and shipped as modular components |
Closing as this is now complete.
It's worth noting that At any rate, the feature has been implemented in a way that consumers such as Lodestar can opt-out and maintain their own peer ranking system separate to the libp2p connection manager. |
This is a component of connection tagging. Libp2p should support configuring/tagging specific peers/multiaddrs as priority connections. The goal here is to have connections that the Connection Manager does not kill, and that we try to maintain connections too. If connections are killed, libp2p should attempt to automatically reconnect to them.
An example of this is an IPFS browser node setting a preload node as an important connection. Since the preload node acts as a proxy for serving all of its content, these connections are vital to maintain. If the connection is lost, the node can become effectively unusable.
This would also be important for private clusters that expose a single relay/proxy node. Maintaining those internal connections to the relay is critical for those peers.
Future iterations of this could involve a spec to have the nodes coordinate and agree on this connection keep alive behavior. This would allow both nodes to agree to maintain the connection and avoid hanging up on one another. It would also allow overtaxed nodes to decline the keep alive, proving the requesting node the opportunity to find other nodes with connection availability.
The text was updated successfully, but these errors were encountered: