From a29538d5121142541fb1a27b389ad73e42e075de Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 22 Jan 2024 21:09:42 +0100 Subject: [PATCH 1/4] Allow optimistic mirror of kbuckets with read lock only --- src/discv5.rs | 28 ++++++++++++++++++++++++++++ src/handler/request_call.rs | 2 +- src/kbucket.rs | 2 +- src/kbucket/entry.rs | 4 +--- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/discv5.rs b/src/discv5.rs index 0acf1587a..29d11da8b 100644 --- a/src/discv5.rs +++ b/src/discv5.rs @@ -453,6 +453,17 @@ impl Discv5

{ .collect() } + /// Takes only a read lock on the kbuckets. This means pending entries aren't added. Otherwise + /// same as [`Self::table_entries_id`]. Returns an iterator over all ENR node IDs of nodes + /// currently contained in the routing table. + pub fn table_entries_id_rlock(&self) -> Vec { + self.kbuckets + .read() + .iter_ref() + .map(|entry| *entry.node.key.preimage()) + .collect() + } + /// Returns an iterator over all the ENR's of nodes currently contained in the routing table. pub fn table_entries_enr(&self) -> Vec { self.kbuckets @@ -477,6 +488,23 @@ impl Discv5

{ .collect() } + /// Takes only a read lock on the kbuckets. This means pending entries aren't added. Otherwise + /// same as [`Self::table_entries`]. Returns an iterator over all ENR node IDs of nodes + /// currently contained in the routing table. + pub fn table_entries_rlock(&self) -> Vec<(NodeId, Enr, NodeStatus)> { + self.kbuckets + .read() + .iter_ref() + .map(|entry| { + ( + *entry.node.key.preimage(), + entry.node.value.clone(), + entry.status, + ) + }) + .collect() + } + /// Requests the ENR of a node corresponding to multiaddr or multi-addr string. /// /// Only `ed25519` and `secp256k1` key types are currently supported. diff --git a/src/handler/request_call.rs b/src/handler/request_call.rs index b91c7643e..42506aa76 100644 --- a/src/handler/request_call.rs +++ b/src/handler/request_call.rs @@ -1,4 +1,4 @@ -pub use crate::node_info::{NodeAddress, NodeContact}; +pub use crate::node_info::NodeContact; use crate::{ packet::Packet, rpc::{Request, RequestBody}, diff --git a/src/kbucket.rs b/src/kbucket.rs index 67d137c6a..1489a7bdc 100644 --- a/src/kbucket.rs +++ b/src/kbucket.rs @@ -533,7 +533,7 @@ where /// Returns an iterator over all the entries in the routing table. /// Does not add pending node to kbucket to get an iterator which - /// takes a reference instead of a mutable reference. + /// takes a mutable reference instead of a reference. pub fn iter_ref(&self) -> impl Iterator> { self.buckets.iter().flat_map(move |table| { table.iter().map(move |n| EntryRefView { diff --git a/src/kbucket/entry.rs b/src/kbucket/entry.rs index 1e0304048..97be95de1 100644 --- a/src/kbucket/entry.rs +++ b/src/kbucket/entry.rs @@ -25,9 +25,7 @@ //! representing the nodes participating in the Kademlia DHT. pub use super::{ - bucket::{ - AppliedPending, ConnectionState, InsertResult, Node, NodeStatus, MAX_NODES_PER_BUCKET, - }, + bucket::{AppliedPending, ConnectionState, InsertResult, Node, NodeStatus}, key::*, ConnectionDirection, }; From ccf59793a7e09ec806461c4786a1f66ea554e6e7 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 28 Jan 2024 17:36:00 +0100 Subject: [PATCH 2/4] Publicly expose kbuckets as closure --- src/discv5.rs | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/src/discv5.rs b/src/discv5.rs index 29d11da8b..420801445 100644 --- a/src/discv5.rs +++ b/src/discv5.rs @@ -453,17 +453,6 @@ impl Discv5

{ .collect() } - /// Takes only a read lock on the kbuckets. This means pending entries aren't added. Otherwise - /// same as [`Self::table_entries_id`]. Returns an iterator over all ENR node IDs of nodes - /// currently contained in the routing table. - pub fn table_entries_id_rlock(&self) -> Vec { - self.kbuckets - .read() - .iter_ref() - .map(|entry| *entry.node.key.preimage()) - .collect() - } - /// Returns an iterator over all the ENR's of nodes currently contained in the routing table. pub fn table_entries_enr(&self) -> Vec { self.kbuckets @@ -488,21 +477,13 @@ impl Discv5

{ .collect() } - /// Takes only a read lock on the kbuckets. This means pending entries aren't added. Otherwise - /// same as [`Self::table_entries`]. Returns an iterator over all ENR node IDs of nodes - /// currently contained in the routing table. - pub fn table_entries_rlock(&self) -> Vec<(NodeId, Enr, NodeStatus)> { - self.kbuckets - .read() - .iter_ref() - .map(|entry| { - ( - *entry.node.key.preimage(), - entry.node.value.clone(), - entry.status, - ) - }) - .collect() + /// Takes a closure parameterized by type `Arc>>` as + /// parameter. Caution: caller is responsible of dropping a lock taken on the kbuckets. + pub fn with_kbuckets(&self, f: F) -> T + where + F: FnOnce(&Arc>>) -> T, + { + f(&self.kbuckets) } /// Requests the ENR of a node corresponding to multiaddr or multi-addr string. From 8748e155a3b0669c40945ccf61e1b6ebeb4f0504 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 28 Jan 2024 18:17:12 +0100 Subject: [PATCH 3/4] Add code example to docs --- src/discv5.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/discv5.rs b/src/discv5.rs index 420801445..2e13d884b 100644 --- a/src/discv5.rs +++ b/src/discv5.rs @@ -478,7 +478,24 @@ impl Discv5

{ } /// Takes a closure parameterized by type `Arc>>` as - /// parameter. Caution: caller is responsible of dropping a lock taken on the kbuckets. + /// parameter. Caution: caller is responsible of dropping a lock taken on the kbuckets. We can + /// then for example take a read lock only to optimistically view the current keys in the + /// kbuckets (optimistic since it doesn't apply pending entries, which requires a write lock). + /// ``` + /// use std::str::FromStr; + /// use discv5::{ConfigBuilder, Discv5, ListenConfig, Enr, enr::CombinedKey}; + /// + /// let sk = CombinedKey::generate_secp256k1(); + /// let enr = Enr::builder().build(&sk).unwrap(); + /// let config = ConfigBuilder::new(ListenConfig::default()).build(); + /// let discv5: Discv5 = Discv5::new(enr, sk, config).unwrap(); + /// + /// let entries = discv5.with_kbuckets(|kbuckets| kbuckets + /// .read() + /// .iter_ref() + /// .map(|entry| *entry.node.key.preimage()) + /// .collect::>()); + /// ``` pub fn with_kbuckets(&self, f: F) -> T where F: FnOnce(&Arc>>) -> T, From 38cbfcf0f785683f39514ab482357ba7c5b0c0b9 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 28 Jan 2024 18:33:18 +0100 Subject: [PATCH 4/4] fixup! Add code example to docs --- src/discv5.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/discv5.rs b/src/discv5.rs index 2e13d884b..e33c60f25 100644 --- a/src/discv5.rs +++ b/src/discv5.rs @@ -478,9 +478,10 @@ impl Discv5

{ } /// Takes a closure parameterized by type `Arc>>` as - /// parameter. Caution: caller is responsible of dropping a lock taken on the kbuckets. We can - /// then for example take a read lock only to optimistically view the current keys in the - /// kbuckets (optimistic since it doesn't apply pending entries, which requires a write lock). + /// parameter. Caution: caller is responsible of dropping a lock taken on the kbuckets. For + /// example, a read lock can be taken on the kbuckets to optimistically view the current keys + /// in the kbuckets (optimistic since it doesn't apply pending entries, which requires a write + /// lock). /// ``` /// use std::str::FromStr; /// use discv5::{ConfigBuilder, Discv5, ListenConfig, Enr, enr::CombinedKey};