-
Notifications
You must be signed in to change notification settings - Fork 54
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/peerstore impl #505
Feat/peerstore impl #505
Conversation
libp2p/peerstore.nim
Outdated
protoBook*: ProtoBook | ||
keyBook*: KeyBook | ||
metadataBook*: MetadataBook | ||
addrChangeHandlers*: seq[AddrChangeHandler] |
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.
Hmm, wouldn't it make more sense to move the change handlers into the specific *Book
types? For example, I'm not sure what the functionality of KeyBook.keyChange
is and how it relates to keyChangeHandlers
? Wouldn't it make more sense to move keyChangeHandlers
under KeyBook
and get rid of keyChange
, or am I missing something?
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 agree. :) Improvement in ff7d081
The idea was to create some level of indirection between the multiple public handlers that different clients can register on the Peer Store and the single, private handler that the peer store itself registers per *Book when it initialises those books. Each book then only notifies its encompassing Peer Store of changes and the Peer Store in turn notifies its multiple registered clients (perhaps enforcing optional policy). Currently this serves no more than a conceptual purpose, so it will be cleaner to just move the handlers to the individual books. Thanks!
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, I'm not sure if the "proxy" handler makes sense there - but I understand the idea behind it now.
My perspective on the PeerStore
is a wrapper that exposes specific register*Handler
methods to each underlying *Book
. I think it's cleaner and simpler to reason about, but I'm willing to reconsider it when we might require this functionality.
@sinkingsugar would love your input on this. |
libp2p/peerstore.nim
Outdated
changeHandlers: seq[KeyChangeHandler] | ||
|
||
MetadataBook* = object | ||
book*: Table[PeerID, Table[string, seq[byte]]] |
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.
Is this completely arbitrary? What do we imagine storing here? (Generally seems useful, mostly curious)
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.
Yes, it's supposed to be a general purpose key-value metadata store per peer. In my mind we can use it to keep performance related metrics, such as peer age, throughput, bandwidth, etc. In the go- and js implementations it's also used to store peer location, libp2p agent version and other peer-related metadata determined during the identify
procedure (see e.g. libp2p/js-libp2p#800).
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'm not 100% sure about seq[byte]
as it can just hold pure data, no references etc.. but for now it's just fine, it's something that can be fixed when we figure out better.
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 noticed no raises which is good and so we can probably add on top {.push raises: [Defect].}
Ah, great catch, lets get that added right at the top of the file @jm-clius!
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.
In fact, to clarify - there rule of thumb is that we use this style everywhere possible, the only place/reason it can't be used is in the presence of async, non-async apis should default to this style.
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.
Ok, great suggestions and I especially like the consistency of the second suggestion. To be clear, do you then suggest that specialized entry objects be defined for AddressEntry
, ProtoEntry
and KeyEntry
, but that the MetadataBook
maintains the generic PeerBookEntry[T]
so peer book clients can define their own entry types?
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 like the generic entry yes!, maybe codegen will be bloaty but that's not our fault (nim things)
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.
Ok, great suggestions and I especially like the consistency of the second suggestion. To be clear, do you then suggest that specialized entry objects be defined for AddressEntry, ProtoEntry and KeyEntry, but that the MetadataBook maintains the generic PeerBookEntry[T] so peer book clients can define their own entry types?
Yep, precisely. In fact, we can make MetadataBook
be the base for the more specialised book types as well. The only thing I'm not content with is the implicit entry*: T
in PeerBookEntry
, I wish Nim had a more concise syntax for that sort of thing.
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.
Yup, this is a very reasonable suggestion, since addresses, keys and protos can be seen as specialised types of metadata in any case.
My only concern would be that the API should still make explicit the subtle differences between the books. For example, an AddressBook maps a peerId
to a collection of addrs
(and therefore should allow clients to add
individual addrs
to each collection), whereas a KeyBook only allows a single PublicKey
entry per peerId
(and therefore only allows set
ting a singular entry).
I've attempted to do the above in 12e0716. This significantly changes (and simplifies) the PeerStore
by making extensive use of generics. Each PeerStore
still consists of a specialised addressBook
, keyBook
and protoBook
, but now also contains one "any-purpose" metadataBook
of generic type PeerBook[T]
(this can easily be changed to a seq
of such any-purpose books if there is a need for extensibility). Of course, this does not really allow for run-time object variance in the metadataBook
(which I don't think generics are suitable for, in any case?), but does allow clients to specify their own metadata type
when instantiating a new PeerStore
. I'm not exactly happy with the PeerStore
itself being genericised, but since it requires a concrete type when constructing the metadataBook
I don't really see a way around this. I'll be glad for any suggestions to make this even more flexible. cc @sinkingsugar @dryajov
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.
Honestly, the concept of a free form metadata store doesn't make much sense in this context and I would just leave it out, allowing anyone who needs additional information to just extend the PeerBook
and PeerStores
accordingly.
protoBook.book.getOrDefault(peerId, | ||
initHashSet[string]()) | ||
|
||
proc add*(protoBook: var ProtoBook, |
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.
At what point will this be called? Who is responsible for calling it and at what point in the lifecycle?
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.
Since a Peer Store provides a "standalone" API it can theoretically be instantiated and added to (at any time) by any module seeking to provide some level of peer management.
Eventually I envision it to be integrated in such a way that there is a single libp2p instance, with the following integration points (based on js-implementation):
- a
Dialer
(TBD) adds "bootstrap" or statically defined peers when these are dialed. This is most likely the first point of integration forWakuRelay
as well. TheDialer
would also be responsible for maintaining connections and redialing peers when dropped. - the
identify
protocol adds information about identified peers. - Discovery/DHT mechanisms adds information about discovered peers.
libp2p/peerstore.nim
Outdated
keyBook.book.del(peerId) | ||
return true | ||
|
||
proc set*(keyBook: var KeyBook, |
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.
Ditto above, where is this called and who is responsible for it? Upon initial connection in nim-libp2p or somewhere else?
libp2p/peerstore.nim
Outdated
# Listener object with client-defined handlers for peer store events | ||
addrChange*: AddrChangeHandler | ||
# @TODO add handlers for other event types | ||
ProtoChangeHandler* = proc(peerId: PeerID, protos: HashSet[string]) |
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.
Is FooChangeHandler the common name here? Nitpick, but something like FooUpdateHandler might be a tiny bit more descriptive, "Change" to me suggests something could be breaking.
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 agree. change
was selected to stay as close as possible to similar events and naming in the js-implementation, but I am happy to update naming to update
:)
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 think the naming convention makes sense here.
LGTM, Very good style! |
This PR closes #504.
It adds a
KeyBook
,ProtoBook
andMetadataBook
to the PoC Peer Store implementation.Miscellaneous changes
EventListener
object (as per this conversation)Suggested next steps:
WakuRelay
will specifically benefit from something like adialer
interface from where dialed peers can be added to a store, reconnected when connection dropped, etc. Parts of this may overlap with efforts elsewhere innim-libp2p
, so proper coordination is necessary. I will therefore propose any next steps as PoC features in the libp2p repo to gain consensus before proceeding.