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

feat(peer-store): introduce libp2p-peer-store #5724

Draft
wants to merge 45 commits into
base: master
Choose a base branch
from

Conversation

drHuangMHT
Copy link
Contributor

Description

Introduce libp2p-peer-store for a peer store implementation.

Related: #4103

Notes & open questions

Change checklist

  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • A changelog entry has been made in the appropriate crates

@drHuangMHT
Copy link
Contributor Author

This is a very basic implementation, would love to hear more ideas on how to implement this!

Copy link
Contributor

@elenaf9 elenaf9 left a comment

Choose a reason for hiding this comment

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

Thanks for starting this effort @drHuangMHT!

If I understand the current implementation correctly, the purpose of this store is just to track connected peers, and explicitly added addresses. However, I think we have to keep in mind that protocols like kademlia or identify very frequently report all addresses that they learn as NewExternalAddrOfPeer. So with the current MemoryStore implementation, the store would grow unbounded with all of these addresses. I think there needs to be some GC strategy.

Also, after reading #4103 (comment), I think a peer store implementation could also do much more than just track explicitly added addresses, e.g.:

  • track how valuable a know address is, by using infos about how the address info was received, if we connected to it already, etc
  • implement a TTL for address records
  • track other meta data for a peer, e.g. public key

I don't think this needs to be implemented in this PR. But I think we could forward more of the already present info to the Store trait, so that a custom implementation can have more sophisticated logic.

@drHuangMHT
Copy link
Contributor Author

Oops that use<'a> syntax was stabilized in rust 1.82, the compiler on 1.80 wasn't happy with it.

Copy link
Member

@jxs jxs left a comment

Choose a reason for hiding this comment

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

Thanks for starting this @drHuangMHT, already looking good! left some notes to allow further customization.
cc @elenaf9

 Store now handles FromSwarm directly; rename some on_* methods; allow removing address; update records on FromSwarm::ConnectionEstablished
@drHuangMHT
Copy link
Contributor Author

drHuangMHT commented Dec 16, 2024

  • track how valuable a know address is, by using infos about how the address info was received, if we connected to it already, etc

This can get very complicated very soon, for example how to record the activitiy on the address(there will be some overhead) and how to make it machine-readable(scoring system). The discussion will be quite lengthy.

  • implement a TTL for address records

I didn't see a good way to implement TTL(garbage collect) for records, any pointer?

  • track other meta data for a peer, e.g. public key

I don't see the remote public key being available through swarm itself, be it in the form of PeerRecord, SignedEnvelope or just PublicKey. But the pubkey is indeed a very important metadata of a peer.

Copy link

@dknopik dknopik left a comment

Choose a reason for hiding this comment

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

Hey @drHuangMHT!
I tried to use the peer store. I like it! I've added some comments with changes I needed to make.

Also, I agree with @jxs's comment that it would be useful to add a generic parameter to the memory store for user data. I know that it is only a reference implementation and users are free to implement Store themselves, but I feel like custom data would be a rather common use case, and it would be unfortunate if users had to copy-paste the memory store in too many cases.

@dknopik
Copy link

dknopik commented Feb 3, 2025

Hi @drHuangMHT,

thanks, the changes look great!
Another thing that I found that would be useful is providing a way to iterate over the peers we have in the store. I am not sure though if this should be offered by the Behaviour, or only by the Store implementation. What do you think?

@drHuangMHT
Copy link
Contributor Author

thanks, the changes look great! Another thing that I found that would be useful is providing a way to iterate over the peers we have in the store. I am not sure though if this should be offered by the Behaviour, or only by the Store implementation. What do you think?

I think it would be better to implement this on the store implementation(not on Behaviour or Store trait itself) so we don't clutter the interfaces, and we already expose the internal store on Behaviour.

Copy link
Member

@jxs jxs left a comment

Choose a reason for hiding this comment

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

Hi @drHuangMHT thanks for driving this.
I had a call Today with @elenaf9 and @dknopik to discuss this PR, as we are using this it to understand how it may help us across two projects where we need a Peer Store.
I would like to avoid adding features that don't have real demand and be as simple as possible in this phase, to then iterate adding them later when needed.
I Left some comments that stemmed from that call, Elena and Daniel feel free to add and correct anything that I missed. DrHuang give your thoughts as well ofc.
Thanks!

addresses: LruCache<Multiaddr, AddressRecord>,
custom: Option<T>,
}
impl<T> PeerRecord<T> {
Copy link
Member

Choose a reason for hiding this comment

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

I don't know if we need all these methods for now (the CRUD seem duplicated on the MemoryStore), can we just have PeerRecord as a struct and have it in the upper module?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We need this when iterating over all records since custom is private.

@drHuangMHT
Copy link
Contributor Author

I forgot this very important reason why I want to support libp2p::core::PeerRecord(signed addresses) here in peer store: gossipsub PeerEXchange! So this is a series of PR preceding it: #5785 makes Identify a source of PeerRecord, this PR allow us to store it and I'm planning on one to enable the use of it in gossipsub.
@jxs thoughts?

Copy link
Member

@jxs jxs left a comment

Choose a reason for hiding this comment

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

I forgot this very important reason why I want to support libp2p::core::PeerRecord(signed addresses) here in peer store: gossipsub PeerEXchange! So this is a series of PR preceding it: #5785 makes Identify a source of PeerRecord, this PR allow us to store it and I'm planning on one to enable the use of it in gossipsub.
@jxs thoughts?

Hi Dr Huang, can we do it on a subsequent PR?

}
}

#[cfg(feature = "serde")]
Copy link
Member

Choose a reason for hiding this comment

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

why do we require the Serde features?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We've discussed it in the last maintainers call. We would like to persist the data in the store in some way and serde is probably the easiest.

Copy link
Member

Choose a reason for hiding this comment

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

AFAIR, we discussed it for kad not for peer-store right?
What are the use cases for persisting the peer-store?
@rishiad as you were also in that conversation, do you have a specific need for serializing the peer-store
I also feel it's counter intuitive to have a MemoryStore that may then be a persistent store

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is called MemoryStore doesn't necessarily suggest it is meant to be temporary and volatile. If you don't want things like a database driver but also would like to persist data before shutdown, flip the switch, just like the generic parameter on it. It is not necessary to duplicate the code right?

Copy link
Member

Choose a reason for hiding this comment

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

but the generic parameter can be retrieved with record_iter and record_iter_mut right? And then serialized after for anyone who needs it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

but the generic parameter can be retrieved with record_iter and record_iter_mut right? And then serialized after for anyone who needs it

I mean we don't really need that generic for PeerRecord but we decided to have it because it makes attaching additional data easier without re-implement MemoryStore, and I think the same can be said for serde support. Not that it is inconvenient when we want to persist the additional data though.

Copy link
Member

Choose a reason for hiding this comment

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

I mean we don't really need that generic for PeerRecord but we decided to have it because it makes attaching additional data easier without re-implement MemoryStore

we need it, you can see it being parametrized here I wouldn't have suggested it if there were no actual use cases for it.
Goes in line with what I wrote here and it's basically applying YAGNI. I.e do not limit hypothetical extensibility but also do not implement it if there are no actual use cases for it.

and I think the same can be said for serde support. Not that it is inconvenient when we want to persist the additional data though.

Yes but if no one requires it why should we maintain that code? What's the advantage?
The disadvantage is that it's more code to maintain, and may also be tampered by users not knowing what they may be doing.
When someone requires a specific feature, they will have the requirements to fit their purpose and will be a much better solution as it's adapted and tailored to their specific usecase, they will also have proper feedback with testing it in real case scenarios etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ping @drHuangMHT

Not waiting for Daniel @dknopik to report back on this?

Copy link
Member

@jxs jxs left a comment

Choose a reason for hiding this comment

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

Ok almost ready to go Dr Huang, only missing removal of the serde code.
Thanks!

}
}

#[cfg(feature = "serde")]
Copy link
Member

Choose a reason for hiding this comment

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

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.

7 participants