From dc770aabc436a857e9d864e2cfd54613cb504123 Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Sun, 12 Nov 2023 17:55:20 +0200 Subject: [PATCH 01/25] add poll function in kademlia behaviour that polls bootstrap function --- misc/server/src/main.rs | 16 +--------------- protocols/kad/src/behaviour.rs | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/misc/server/src/main.rs b/misc/server/src/main.rs index 2349ebf6485..09e0722a7a2 100644 --- a/misc/server/src/main.rs +++ b/misc/server/src/main.rs @@ -1,7 +1,6 @@ use base64::Engine; use clap::Parser; use futures::stream::StreamExt; -use futures_timer::Delay; use libp2p::identity; use libp2p::identity::PeerId; use libp2p::kad; @@ -14,8 +13,6 @@ use prometheus_client::registry::Registry; use std::error::Error; use std::path::PathBuf; use std::str::FromStr; -use std::task::Poll; -use std::time::Duration; use tracing_subscriber::EnvFilter; use zeroize::Zeroizing; @@ -23,8 +20,6 @@ mod behaviour; mod config; mod http_service; -const BOOTSTRAP_INTERVAL: Duration = Duration::from_secs(5 * 60); - #[derive(Debug, Parser)] #[clap(name = "libp2p server", about = "A rust-libp2p server binary.")] struct Opts { @@ -125,17 +120,8 @@ async fn main() -> Result<(), Box> { } }); - let mut bootstrap_timer = Delay::new(BOOTSTRAP_INTERVAL); - loop { - if let Poll::Ready(()) = futures::poll!(&mut bootstrap_timer) { - bootstrap_timer.reset(BOOTSTRAP_INTERVAL); - let _ = swarm - .behaviour_mut() - .kademlia - .as_mut() - .map(|k| k.bootstrap()); - } + let _ = swarm.behaviour_mut().kademlia.as_mut().map(|k| k.poll()); let event = swarm.next().await.expect("Swarm not to terminate."); metrics.record(&event); diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index fc942cf635a..d79f260e3a4 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -35,6 +35,7 @@ use crate::record::{ }; use crate::K_VALUE; use fnv::{FnvHashMap, FnvHashSet}; +use futures_timer::Delay; use instant::Instant; use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; @@ -116,6 +117,9 @@ pub struct Behaviour { /// The record storage. store: TStore, + + /// The interval used by [`Behaviour::poll`] to call [`Behaviour::bootstrap`]. + refresh_interval: Option, } /// The configurable strategies for the insertion of peers @@ -181,6 +185,7 @@ pub struct Config { provider_publication_interval: Option, kbucket_inserts: BucketInserts, caching: Caching, + refresh_interval: Option, } impl Default for Config { @@ -197,6 +202,7 @@ impl Default for Config { provider_record_ttl: Some(Duration::from_secs(24 * 60 * 60)), kbucket_inserts: BucketInserts::OnConnected, caching: Caching::Enabled { max_peers: 1 }, + refresh_interval: Some(Duration::from_secs(5 * 60)), } } } @@ -391,6 +397,14 @@ impl Config { self.caching = c; self } + + /// Sets the interval on which [`Behaviour::bootstrap`] is called from [`Behaviour::poll`] + /// + /// `None` means that [`Behaviour::bootstrap`] is not called from [`Behaviour::poll`] + pub fn set_refresh_interval(&mut self, interval: Option) -> &mut Self { + self.refresh_interval = interval; + self + } } impl Behaviour @@ -448,6 +462,7 @@ where mode: Mode::Client, auto_mode: true, no_events_waker: None, + refresh_interval: config.refresh_interval, } } @@ -1005,6 +1020,24 @@ where } } + /// Asynchronously polls the Kademlia behavior, triggering [`Behaviour::bootstrap`] if necessary. + /// + /// This function checks the refresh interval and, if ready, resets the timer and + /// triggers the bootstrap operation. It returns a `Result<(), NoKnownPeers>` where + /// Ok(()) indicates success, and Err(NoKnownPeers) is returned if there are no known peers + /// during the bootstrap operation. See [`Behaviour::bootstrap`] for more details. + pub async fn poll(&mut self) -> Result<(), NoKnownPeers> { + if let Some(refresh_interval) = &mut self.refresh_interval { + let mut bootstrap_timer = Delay::new(*refresh_interval); + if let Poll::Ready(()) = futures::poll!(&mut bootstrap_timer) { + bootstrap_timer.reset(*refresh_interval); + self.bootstrap()?; + }; + } + + Ok(()) + } + fn reconfigure_mode(&mut self) { if self.connections.is_empty() { return; From 30559da88c83058744c4dfe1ea6750a431e9f335 Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Sun, 12 Nov 2023 18:15:34 +0200 Subject: [PATCH 02/25] bumb kad version and add changelog --- Cargo.lock | 2 +- Cargo.toml | 2 +- protocols/kad/CHANGELOG.md | 5 +++++ protocols/kad/Cargo.toml | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f54623dca5..3caa8976bbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2724,7 +2724,7 @@ dependencies = [ [[package]] name = "libp2p-kad" -version = "0.45.1" +version = "0.45.2" dependencies = [ "arrayvec", "async-std", diff --git a/Cargo.toml b/Cargo.toml index 27a8214ed6f..8395c970a09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,7 +82,7 @@ libp2p-floodsub = { version = "0.44.0", path = "protocols/floodsub" } libp2p-gossipsub = { version = "0.46.0", path = "protocols/gossipsub" } libp2p-identify = { version = "0.44.0", path = "protocols/identify" } libp2p-identity = { version = "0.2.7" } -libp2p-kad = { version = "0.45.1", path = "protocols/kad" } +libp2p-kad = { version = "0.45.2", path = "protocols/kad" } libp2p-mdns = { version = "0.45.0", path = "protocols/mdns" } libp2p-memory-connection-limits = { version = "0.2.0", path = "misc/memory-connection-limits" } libp2p-metrics = { version = "0.14.1", path = "misc/metrics" } diff --git a/protocols/kad/CHANGELOG.md b/protocols/kad/CHANGELOG.md index 9b57c70a7ed..28348c293b7 100644 --- a/protocols/kad/CHANGELOG.md +++ b/protocols/kad/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.45.2 + +- Add `refresh_interval` config and `Behaviour::poll` function used to poll `Behaviour::boostrap` + See [PR 4838](https://github.com/libp2p/rust-libp2p/pull/4838) + ## 0.45.1 - Fix a bug where calling `Behaviour::remove_address` with an address not in the peer's bucket would remove the peer from the routing table if the bucket has only one address left. diff --git a/protocols/kad/Cargo.toml b/protocols/kad/Cargo.toml index 51e9656441f..affde929b0e 100644 --- a/protocols/kad/Cargo.toml +++ b/protocols/kad/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-kad" edition = "2021" rust-version = { workspace = true } description = "Kademlia protocol for libp2p" -version = "0.45.1" +version = "0.45.2" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" From a7fcd26852d3a9d4723367d22ac74773790d929c Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Sun, 12 Nov 2023 18:20:31 +0200 Subject: [PATCH 03/25] bumb server version and add server changelog --- Cargo.lock | 2 +- Cargo.toml | 2 +- misc/server/CHANGELOG.md | 6 ++++++ misc/server/Cargo.toml | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3caa8976bbb..824135ec0ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3090,7 +3090,7 @@ dependencies = [ [[package]] name = "libp2p-server" -version = "0.12.4" +version = "0.12.5" dependencies = [ "base64 0.21.5", "clap", diff --git a/Cargo.toml b/Cargo.toml index 8395c970a09..9fcf2585300 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,7 +97,7 @@ libp2p-quic = { version = "0.10.1", path = "transports/quic" } libp2p-relay = { version = "0.17.0", path = "protocols/relay" } libp2p-rendezvous = { version = "0.14.0", path = "protocols/rendezvous" } libp2p-request-response = { version = "0.26.0", path = "protocols/request-response" } -libp2p-server = { version = "0.12.4", path = "misc/server" } +libp2p-server = { version = "0.12.5", path = "misc/server" } libp2p-swarm = { version = "0.44.0", path = "swarm" } libp2p-swarm-derive = { version = "=0.34.0", path = "swarm-derive" } # `libp2p-swarm-derive` may not be compatible with different `libp2p-swarm` non-breaking releases. E.g. `libp2p-swarm` might introduce a new enum variant `FromSwarm` (which is `#[non-exhaustive]`) in a non-breaking release. Older versions of `libp2p-swarm-derive` would not forward this enum variant within the `NetworkBehaviour` hierarchy. Thus the version pinning is required. libp2p-swarm-test = { version = "0.3.0", path = "swarm-test" } diff --git a/misc/server/CHANGELOG.md b/misc/server/CHANGELOG.md index d476a8722eb..caca3ddbeff 100644 --- a/misc/server/CHANGELOG.md +++ b/misc/server/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.12.5 +### Changed + +- Use `Behaviour::poll` to call `Behaviour::bootstrap` for Kademlia protocol. + See [PR 4838](https://github.com/libp2p/rust-libp2p/pull/4838). + ## 0.12.4 ### Added diff --git a/misc/server/Cargo.toml b/misc/server/Cargo.toml index 4500ca19c59..813bbe647ac 100644 --- a/misc/server/Cargo.toml +++ b/misc/server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libp2p-server" -version = "0.12.4" +version = "0.12.5" authors = ["Max Inden "] edition = "2021" repository = "https://github.com/libp2p/rust-libp2p" From 695b94a2dd7f35ff8bdd1b395e19193b196f1c31 Mon Sep 17 00:00:00 2001 From: Panagiotis Ganelis <50522617+PanGan21@users.noreply.github.com> Date: Sun, 12 Nov 2023 22:51:25 +0100 Subject: [PATCH 04/25] Fix doc comments Co-authored-by: Thomas Eizinger --- protocols/kad/CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/protocols/kad/CHANGELOG.md b/protocols/kad/CHANGELOG.md index 28348c293b7..96466fcb0a8 100644 --- a/protocols/kad/CHANGELOG.md +++ b/protocols/kad/CHANGELOG.md @@ -1,6 +1,7 @@ -## 0.45.2 +## 0.45.2 - unreleased -- Add `refresh_interval` config and `Behaviour::poll` function used to poll `Behaviour::boostrap` +- Automatically `bootstrap` every 5 minutes. + This can be configured using the `bootstrap_interval` config. See [PR 4838](https://github.com/libp2p/rust-libp2p/pull/4838) ## 0.45.1 From 0b54f9a5263dea50141eff7e6beccd880f1d9b95 Mon Sep 17 00:00:00 2001 From: Panagiotis Ganelis <50522617+PanGan21@users.noreply.github.com> Date: Sun, 12 Nov 2023 22:51:54 +0100 Subject: [PATCH 05/25] Rename to bootstrap_interval Co-authored-by: Thomas Eizinger --- protocols/kad/src/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index d79f260e3a4..43abec330c8 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -119,7 +119,7 @@ pub struct Behaviour { store: TStore, /// The interval used by [`Behaviour::poll`] to call [`Behaviour::bootstrap`]. - refresh_interval: Option, + bootstrap_interval: Option, } /// The configurable strategies for the insertion of peers From d749cbd43d816c70718e07df1dc5ebaa52c633fc Mon Sep 17 00:00:00 2001 From: Panagiotis Ganelis <50522617+PanGan21@users.noreply.github.com> Date: Sun, 12 Nov 2023 23:12:32 +0100 Subject: [PATCH 06/25] Update protocols/kad/src/behaviour.rs Co-authored-by: Thomas Eizinger --- protocols/kad/src/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 43abec330c8..6dd72507f51 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -400,7 +400,7 @@ impl Config { /// Sets the interval on which [`Behaviour::bootstrap`] is called from [`Behaviour::poll`] /// - /// `None` means that [`Behaviour::bootstrap`] is not called from [`Behaviour::poll`] + /// `None` means we don't bootstrap at all. pub fn set_refresh_interval(&mut self, interval: Option) -> &mut Self { self.refresh_interval = interval; self From 7f720ce7ca4b84a935463943bea2de1e8209ebff Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Mon, 13 Nov 2023 00:16:43 +0200 Subject: [PATCH 07/25] fix terminology across behaviour --- protocols/kad/src/behaviour.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 6dd72507f51..4615415bcdd 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -185,7 +185,7 @@ pub struct Config { provider_publication_interval: Option, kbucket_inserts: BucketInserts, caching: Caching, - refresh_interval: Option, + bootstrap_interval: Option, } impl Default for Config { @@ -202,7 +202,7 @@ impl Default for Config { provider_record_ttl: Some(Duration::from_secs(24 * 60 * 60)), kbucket_inserts: BucketInserts::OnConnected, caching: Caching::Enabled { max_peers: 1 }, - refresh_interval: Some(Duration::from_secs(5 * 60)), + bootstrap_interval: Some(Duration::from_secs(5 * 60)), } } } @@ -401,8 +401,8 @@ impl Config { /// Sets the interval on which [`Behaviour::bootstrap`] is called from [`Behaviour::poll`] /// /// `None` means we don't bootstrap at all. - pub fn set_refresh_interval(&mut self, interval: Option) -> &mut Self { - self.refresh_interval = interval; + pub fn set_bootstrap_interval(&mut self, interval: Option) -> &mut Self { + self.bootstrap_interval = interval; self } } @@ -462,7 +462,7 @@ where mode: Mode::Client, auto_mode: true, no_events_waker: None, - refresh_interval: config.refresh_interval, + bootstrap_interval: config.bootstrap_interval, } } @@ -1027,10 +1027,10 @@ where /// Ok(()) indicates success, and Err(NoKnownPeers) is returned if there are no known peers /// during the bootstrap operation. See [`Behaviour::bootstrap`] for more details. pub async fn poll(&mut self) -> Result<(), NoKnownPeers> { - if let Some(refresh_interval) = &mut self.refresh_interval { - let mut bootstrap_timer = Delay::new(*refresh_interval); + if let Some(bootstrap_interval) = &mut self.bootstrap_interval { + let mut bootstrap_timer = Delay::new(*bootstrap_interval); if let Poll::Ready(()) = futures::poll!(&mut bootstrap_timer) { - bootstrap_timer.reset(*refresh_interval); + bootstrap_timer.reset(*bootstrap_interval); self.bootstrap()?; }; } From 3a7737c4ae2748dd057aad8d8841e9f8840fe802 Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Tue, 14 Nov 2023 10:44:58 +0200 Subject: [PATCH 08/25] move polling into the internal poll function --- misc/server/src/main.rs | 2 -- protocols/kad/src/behaviour.rs | 45 ++++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/misc/server/src/main.rs b/misc/server/src/main.rs index 09e0722a7a2..2e4299f4819 100644 --- a/misc/server/src/main.rs +++ b/misc/server/src/main.rs @@ -121,8 +121,6 @@ async fn main() -> Result<(), Box> { }); loop { - let _ = swarm.behaviour_mut().kademlia.as_mut().map(|k| k.poll()); - let event = swarm.next().await.expect("Swarm not to terminate."); metrics.record(&event); match event { diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 4615415bcdd..e777e119af4 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -35,6 +35,7 @@ use crate::record::{ }; use crate::K_VALUE; use fnv::{FnvHashMap, FnvHashSet}; +use futures::Future; use futures_timer::Delay; use instant::Instant; use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr}; @@ -52,6 +53,7 @@ use smallvec::SmallVec; use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; use std::fmt; use std::num::NonZeroUsize; +use std::pin::Pin; use std::task::{Context, Poll, Waker}; use std::time::Duration; use std::vec; @@ -118,8 +120,11 @@ pub struct Behaviour { /// The record storage. store: TStore, - /// The interval used by [`Behaviour::poll`] to call [`Behaviour::bootstrap`]. + /// The interval used to poll [`Behaviour::bootstrap`]. bootstrap_interval: Option, + + /// The timer used to poll [`Behaviour::bootstrap`]. + bootstrap_timer: Option, } /// The configurable strategies for the insertion of peers @@ -441,6 +446,11 @@ where .provider_publication_interval .map(AddProviderJob::new); + let bootstrap_timer = config + .bootstrap_interval + .map(|duration| Some(Delay::new(duration))) + .unwrap_or_else(|| None); + Behaviour { store, caching: config.caching, @@ -463,6 +473,7 @@ where auto_mode: true, no_events_waker: None, bootstrap_interval: config.bootstrap_interval, + bootstrap_timer, } } @@ -877,6 +888,9 @@ where /// /// > **Note**: Bootstrapping requires at least one node of the DHT to be known. /// > See [`Behaviour::add_address`]. + /// > **Note**: The bootstrapping interval is used to call bootstrap periodically + /// to ensure a healthy routing table. + /// > See [`Config::bootstrap_interval`] field. pub fn bootstrap(&mut self) -> Result { let local_key = self.kbuckets.local_key().clone(); let info = QueryInfo::Bootstrap { @@ -1020,24 +1034,6 @@ where } } - /// Asynchronously polls the Kademlia behavior, triggering [`Behaviour::bootstrap`] if necessary. - /// - /// This function checks the refresh interval and, if ready, resets the timer and - /// triggers the bootstrap operation. It returns a `Result<(), NoKnownPeers>` where - /// Ok(()) indicates success, and Err(NoKnownPeers) is returned if there are no known peers - /// during the bootstrap operation. See [`Behaviour::bootstrap`] for more details. - pub async fn poll(&mut self) -> Result<(), NoKnownPeers> { - if let Some(bootstrap_interval) = &mut self.bootstrap_interval { - let mut bootstrap_timer = Delay::new(*bootstrap_interval); - if let Poll::Ready(()) = futures::poll!(&mut bootstrap_timer) { - bootstrap_timer.reset(*bootstrap_interval); - self.bootstrap()?; - }; - } - - Ok(()) - } - fn reconfigure_mode(&mut self) { if self.connections.is_empty() { return; @@ -2489,6 +2485,17 @@ where self.put_record_job = Some(job); } + // Poll bootstrap periodically. + if let Some(mut bootstrap_timer) = self.bootstrap_timer.take() { + if let Poll::Ready(()) = Pin::new(&mut bootstrap_timer).poll(cx) { + if let Some(interval) = self.bootstrap_interval { + bootstrap_timer.reset(interval); + let _ = self.bootstrap(); + } + } + self.bootstrap_timer = Some(bootstrap_timer); + } + loop { // Drain queued events first. if let Some(event) = self.queued_events.pop_front() { From 7343bb2ac884385101ac0591e829747af2403f49 Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Tue, 14 Nov 2023 11:27:30 +0200 Subject: [PATCH 09/25] do not reference private fields in doc --- protocols/kad/src/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 5e178057c6b..71629ebb4aa 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -895,7 +895,7 @@ where /// > See [`Behaviour::add_address`]. /// > **Note**: The bootstrapping interval is used to call bootstrap periodically /// to ensure a healthy routing table. - /// > See [`Config::bootstrap_interval`] field. + /// > See bootstrap_interval field in Config. pub fn bootstrap(&mut self) -> Result { let local_key = self.kbuckets.local_key().clone(); let info = QueryInfo::Bootstrap { From f49210542da2a1d56b4e98072bd6fd271e82a0cc Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Mon, 27 Nov 2023 17:34:41 +0200 Subject: [PATCH 10/25] call bootstrap on new kademlia node connected --- protocols/kad/src/behaviour.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 677ce44541b..d55cb1380bd 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -575,6 +575,7 @@ where }; match entry.insert(addresses.clone(), status) { kbucket::InsertResult::Inserted => { + self.on_new_kademlia_node(); self.queued_events.push_back(ToSwarm::GenerateEvent( Event::RoutingUpdated { peer: *peer, @@ -1302,6 +1303,7 @@ where let addresses = Addresses::new(a); match entry.insert(addresses.clone(), new_status) { kbucket::InsertResult::Inserted => { + self.on_new_kademlia_node(); let event = Event::RoutingUpdated { peer, is_new_peer: true, @@ -2065,6 +2067,10 @@ where } } + fn on_new_kademlia_node(&mut self) { + let _ = self.bootstrap(); + } + /// Preloads a new [`Handler`] with requests that are waiting to be sent to the newly connected peer. fn preload_new_handler( &mut self, From dcf849545d1fa36f3df8920cc7a4c82477a9c6d9 Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Tue, 28 Nov 2023 18:48:21 +0200 Subject: [PATCH 11/25] add enum to track the state of the initial bootstrap --- protocols/kad/src/behaviour.rs | 32 ++++++++++++++++++++++------- protocols/kad/src/behaviour/test.rs | 1 + protocols/kad/src/lib.rs | 6 +++--- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index d55cb1380bd..34872404903 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -125,6 +125,9 @@ pub struct Behaviour { /// The timer used to poll [`Behaviour::bootstrap`]. bootstrap_timer: Option, + + /// Tracks if the latest bootstrap was successfull. + initial_bootstrap: Option, } /// The configurable strategies for the insertion of peers @@ -474,6 +477,7 @@ where no_events_waker: None, bootstrap_interval: config.bootstrap_interval, bootstrap_timer, + initial_bootstrap: None, } } @@ -575,7 +579,7 @@ where }; match entry.insert(addresses.clone(), status) { kbucket::InsertResult::Inserted => { - self.on_new_kademlia_node(); + self.handle_bootstrap(); self.queued_events.push_back(ToSwarm::GenerateEvent( Event::RoutingUpdated { peer: *peer, @@ -894,7 +898,9 @@ where /// /// > **Note**: Bootstrapping requires at least one node of the DHT to be known. /// > See [`Behaviour::add_address`]. - /// > **Note**: The bootstrapping interval is used to call bootstrap periodically + /// > **Note**: Bootstrap does not require to be called manually. It is automatically + /// invoked at regular intervals based on the configured bootstrapping interval. + /// The bootstrapping interval is used to call bootstrap periodically /// to ensure a healthy routing table. /// > See bootstrap_interval field in Config. pub fn bootstrap(&mut self) -> Result { @@ -909,7 +915,8 @@ where Err(NoKnownPeers()) } else { let inner = QueryInner::new(info); - Ok(self.queries.add_iter_closest(local_key, peers, inner)) + let query_id = self.queries.add_iter_closest(local_key, peers, inner); + Ok(query_id) } } @@ -1303,7 +1310,7 @@ where let addresses = Addresses::new(a); match entry.insert(addresses.clone(), new_status) { kbucket::InsertResult::Inserted => { - self.on_new_kademlia_node(); + self.handle_bootstrap(); let event = Event::RoutingUpdated { peer, is_new_peer: true, @@ -2067,8 +2074,13 @@ where } } - fn on_new_kademlia_node(&mut self) { - let _ = self.bootstrap(); + fn handle_bootstrap(&mut self) { + if self.initial_bootstrap.is_none() { + match self.bootstrap() { + Ok(query_id) => self.initial_bootstrap = Some(InitialBootstrap::Active(query_id)), + Err(_) => self.initial_bootstrap = None, + } + } } /// Preloads a new [`Handler`] with requests that are waiting to be sent to the newly connected peer. @@ -2497,7 +2509,7 @@ where if let Poll::Ready(()) = Pin::new(&mut bootstrap_timer).poll(cx) { if let Some(interval) = self.bootstrap_interval { bootstrap_timer.reset(interval); - let _ = self.bootstrap(); + self.handle_bootstrap(); } } self.bootstrap_timer = Some(bootstrap_timer); @@ -3384,3 +3396,9 @@ where .collect::>() .join(", ") } + +#[derive(PartialEq)] +pub enum InitialBootstrap { + Active(QueryId), + Completed, +} diff --git a/protocols/kad/src/behaviour/test.rs b/protocols/kad/src/behaviour/test.rs index 522eebcba92..102221ca401 100644 --- a/protocols/kad/src/behaviour/test.rs +++ b/protocols/kad/src/behaviour/test.rs @@ -186,6 +186,7 @@ fn bootstrap() { let swarm_ids: Vec<_> = swarms.iter().map(Swarm::local_peer_id).cloned().collect(); let qid = swarms[0].behaviour_mut().bootstrap().unwrap(); + assert_eq!(qid.to_string(), 1.to_string()); // Expected known peers let expected_known = swarm_ids.iter().skip(1).cloned().collect::>(); diff --git a/protocols/kad/src/lib.rs b/protocols/kad/src/lib.rs index 519b67f9d7a..ff91710e6c5 100644 --- a/protocols/kad/src/lib.rs +++ b/protocols/kad/src/lib.rs @@ -58,9 +58,9 @@ pub use behaviour::{ AddProviderContext, AddProviderError, AddProviderOk, AddProviderPhase, AddProviderResult, BootstrapError, BootstrapOk, BootstrapResult, GetClosestPeersError, GetClosestPeersOk, GetClosestPeersResult, GetProvidersError, GetProvidersOk, GetProvidersResult, GetRecordError, - GetRecordOk, GetRecordResult, InboundRequest, Mode, NoKnownPeers, PeerRecord, PutRecordContext, - PutRecordError, PutRecordOk, PutRecordPhase, PutRecordResult, QueryInfo, QueryMut, QueryRef, - QueryResult, QueryStats, RoutingUpdate, + GetRecordOk, GetRecordResult, InboundRequest, InitialBootstrap, Mode, NoKnownPeers, PeerRecord, + PutRecordContext, PutRecordError, PutRecordOk, PutRecordPhase, PutRecordResult, QueryInfo, + QueryMut, QueryRef, QueryResult, QueryStats, RoutingUpdate, }; pub use behaviour::{ Behaviour, BucketInserts, Caching, Config, Event, ProgressStep, Quorum, StoreInserts, From f2aaad21d5e88ff1997f7b20ddaf10f95d6b8dde Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Thu, 30 Nov 2023 11:26:41 +0200 Subject: [PATCH 12/25] track if there was a successfull bootstrap and the query id of the automated bootstrap --- protocols/kad/src/behaviour.rs | 42 +++++++++++++++++++--------------- protocols/kad/src/lib.rs | 6 ++--- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 34872404903..d41cadffe72 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -126,8 +126,11 @@ pub struct Behaviour { /// The timer used to poll [`Behaviour::bootstrap`]. bootstrap_timer: Option, - /// Tracks if the latest bootstrap was successfull. - initial_bootstrap: Option, + /// Tracks if the there was a successfull bootstrap. + any_bootstrap_successful: bool, + + /// Tracks the `QueryId` of the automated bootstrap. + current_automated_bootstrap: Option, } /// The configurable strategies for the insertion of peers @@ -477,7 +480,8 @@ where no_events_waker: None, bootstrap_interval: config.bootstrap_interval, bootstrap_timer, - initial_bootstrap: None, + any_bootstrap_successful: false, + current_automated_bootstrap: None, } } @@ -579,7 +583,9 @@ where }; match entry.insert(addresses.clone(), status) { kbucket::InsertResult::Inserted => { - self.handle_bootstrap(); + if !self.any_bootstrap_successful { + self.maybe_bootstrap(); + } self.queued_events.push_back(ToSwarm::GenerateEvent( Event::RoutingUpdated { peer: *peer, @@ -915,8 +921,7 @@ where Err(NoKnownPeers()) } else { let inner = QueryInner::new(info); - let query_id = self.queries.add_iter_closest(local_key, peers, inner); - Ok(query_id) + Ok(self.queries.add_iter_closest(local_key, peers, inner)) } } @@ -1310,7 +1315,9 @@ where let addresses = Addresses::new(a); match entry.insert(addresses.clone(), new_status) { kbucket::InsertResult::Inserted => { - self.handle_bootstrap(); + if !self.any_bootstrap_successful { + self.maybe_bootstrap(); + } let event = Event::RoutingUpdated { peer, is_new_peer: true, @@ -2074,11 +2081,14 @@ where } } - fn handle_bootstrap(&mut self) { - if self.initial_bootstrap.is_none() { + fn maybe_bootstrap(&mut self) { + if self.current_automated_bootstrap.is_none() { match self.bootstrap() { - Ok(query_id) => self.initial_bootstrap = Some(InitialBootstrap::Active(query_id)), - Err(_) => self.initial_bootstrap = None, + Ok(query_id) => { + self.current_automated_bootstrap = Some(query_id); + self.any_bootstrap_successful = true; + } + Err(_) => self.current_automated_bootstrap = None, } } } @@ -2509,7 +2519,9 @@ where if let Poll::Ready(()) = Pin::new(&mut bootstrap_timer).poll(cx) { if let Some(interval) = self.bootstrap_interval { bootstrap_timer.reset(interval); - self.handle_bootstrap(); + if !self.any_bootstrap_successful { + self.maybe_bootstrap(); + } } } self.bootstrap_timer = Some(bootstrap_timer); @@ -3396,9 +3408,3 @@ where .collect::>() .join(", ") } - -#[derive(PartialEq)] -pub enum InitialBootstrap { - Active(QueryId), - Completed, -} diff --git a/protocols/kad/src/lib.rs b/protocols/kad/src/lib.rs index ff91710e6c5..519b67f9d7a 100644 --- a/protocols/kad/src/lib.rs +++ b/protocols/kad/src/lib.rs @@ -58,9 +58,9 @@ pub use behaviour::{ AddProviderContext, AddProviderError, AddProviderOk, AddProviderPhase, AddProviderResult, BootstrapError, BootstrapOk, BootstrapResult, GetClosestPeersError, GetClosestPeersOk, GetClosestPeersResult, GetProvidersError, GetProvidersOk, GetProvidersResult, GetRecordError, - GetRecordOk, GetRecordResult, InboundRequest, InitialBootstrap, Mode, NoKnownPeers, PeerRecord, - PutRecordContext, PutRecordError, PutRecordOk, PutRecordPhase, PutRecordResult, QueryInfo, - QueryMut, QueryRef, QueryResult, QueryStats, RoutingUpdate, + GetRecordOk, GetRecordResult, InboundRequest, Mode, NoKnownPeers, PeerRecord, PutRecordContext, + PutRecordError, PutRecordOk, PutRecordPhase, PutRecordResult, QueryInfo, QueryMut, QueryRef, + QueryResult, QueryStats, RoutingUpdate, }; pub use behaviour::{ Behaviour, BucketInserts, Caching, Config, Event, ProgressStep, Quorum, StoreInserts, From 179db100f544857d2c2b82ac448bbe6966799210 Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Sat, 2 Dec 2023 12:36:41 +0200 Subject: [PATCH 13/25] add link in doc, handle successful bootstrap, reanable polling --- protocols/kad/src/behaviour.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index d41cadffe72..f9d16ab7dab 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -908,7 +908,7 @@ where /// invoked at regular intervals based on the configured bootstrapping interval. /// The bootstrapping interval is used to call bootstrap periodically /// to ensure a healthy routing table. - /// > See bootstrap_interval field in Config. + /// > See [`Config::bootstrap_interval`] field in Config. pub fn bootstrap(&mut self) -> Result { let local_key = self.kbuckets.local_key().clone(); let info = QueryInfo::Bootstrap { @@ -1435,6 +1435,8 @@ where step.last = true; }; + self.any_bootstrap_successful = true; + Some(Event::OutboundQueryProgressed { id: query_id, stats: result.stats, @@ -2084,10 +2086,7 @@ where fn maybe_bootstrap(&mut self) { if self.current_automated_bootstrap.is_none() { match self.bootstrap() { - Ok(query_id) => { - self.current_automated_bootstrap = Some(query_id); - self.any_bootstrap_successful = true; - } + Ok(query_id) => self.current_automated_bootstrap = Some(query_id), Err(_) => self.current_automated_bootstrap = None, } } @@ -2519,9 +2518,7 @@ where if let Poll::Ready(()) = Pin::new(&mut bootstrap_timer).poll(cx) { if let Some(interval) = self.bootstrap_interval { bootstrap_timer.reset(interval); - if !self.any_bootstrap_successful { - self.maybe_bootstrap(); - } + self.maybe_bootstrap(); } } self.bootstrap_timer = Some(bootstrap_timer); From 9c7b8e8e958fb4006e9d4b15a9735338b3eb22fd Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Sat, 2 Dec 2023 15:20:36 +0200 Subject: [PATCH 14/25] fix doc and remove test change --- protocols/kad/src/behaviour.rs | 2 +- protocols/kad/src/behaviour/test.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index f9d16ab7dab..97f02802a82 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -908,7 +908,7 @@ where /// invoked at regular intervals based on the configured bootstrapping interval. /// The bootstrapping interval is used to call bootstrap periodically /// to ensure a healthy routing table. - /// > See [`Config::bootstrap_interval`] field in Config. + /// > See [`Config::set_bootstrap_interval`] for details. pub fn bootstrap(&mut self) -> Result { let local_key = self.kbuckets.local_key().clone(); let info = QueryInfo::Bootstrap { diff --git a/protocols/kad/src/behaviour/test.rs b/protocols/kad/src/behaviour/test.rs index 102221ca401..522eebcba92 100644 --- a/protocols/kad/src/behaviour/test.rs +++ b/protocols/kad/src/behaviour/test.rs @@ -186,7 +186,6 @@ fn bootstrap() { let swarm_ids: Vec<_> = swarms.iter().map(Swarm::local_peer_id).cloned().collect(); let qid = swarms[0].behaviour_mut().bootstrap().unwrap(); - assert_eq!(qid.to_string(), 1.to_string()); // Expected known peers let expected_known = swarm_ids.iter().skip(1).cloned().collect::>(); From 75889e1ecaa3303c26c3d7c14199f0cc963affdd Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 4 Dec 2023 09:55:34 +1100 Subject: [PATCH 15/25] Use defensive programming --- protocols/kad/src/behaviour.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 97f02802a82..316330155b4 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -2084,12 +2084,19 @@ where } fn maybe_bootstrap(&mut self) { - if self.current_automated_bootstrap.is_none() { - match self.bootstrap() { - Ok(query_id) => self.current_automated_bootstrap = Some(query_id), - Err(_) => self.current_automated_bootstrap = None, - } + if self.current_automated_bootstrap.is_some() { + return; } + + let query_id = match self.bootstrap() { + Ok(id) => id, + Err(e) => { + tracing::warn!("skipping automated bootstrap: {e}"); + return; + } + }; + + self.current_automated_bootstrap = Some(query_id); } /// Preloads a new [`Handler`] with requests that are waiting to be sent to the newly connected peer. From f14b9885cde4b87234b87c54a68ac7a3c7cbc648 Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Tue, 5 Dec 2023 12:08:54 +0200 Subject: [PATCH 16/25] Refactor using enum that represends the bootstrap status Co-authored-by: stormshield-frb --- protocols/kad/src/behaviour.rs | 151 ++++++++++++++++++++++----------- 1 file changed, 103 insertions(+), 48 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 316330155b4..cfc0adf593c 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -120,17 +120,8 @@ pub struct Behaviour { /// The record storage. store: TStore, - /// The interval used to poll [`Behaviour::bootstrap`]. - bootstrap_interval: Option, - - /// The timer used to poll [`Behaviour::bootstrap`]. - bootstrap_timer: Option, - - /// Tracks if the there was a successfull bootstrap. - any_bootstrap_successful: bool, - - /// Tracks the `QueryId` of the automated bootstrap. - current_automated_bootstrap: Option, + /// Tracks the status of the current bootstrap. + bootstrap_status: BootstrapStatus, } /// The configurable strategies for the insertion of peers @@ -452,11 +443,6 @@ where .provider_publication_interval .map(AddProviderJob::new); - let bootstrap_timer = config - .bootstrap_interval - .map(|duration| Some(Delay::new(duration))) - .unwrap_or_else(|| None); - Behaviour { store, caching: config.caching, @@ -478,10 +464,7 @@ where mode: Mode::Client, auto_mode: true, no_events_waker: None, - bootstrap_interval: config.bootstrap_interval, - bootstrap_timer, - any_bootstrap_successful: false, - current_automated_bootstrap: None, + bootstrap_status: BootstrapStatus::new(config.bootstrap_interval), } } @@ -583,9 +566,7 @@ where }; match entry.insert(addresses.clone(), status) { kbucket::InsertResult::Inserted => { - if !self.any_bootstrap_successful { - self.maybe_bootstrap(); - } + self.trigger_optional_bootstrap(); self.queued_events.push_back(ToSwarm::GenerateEvent( Event::RoutingUpdated { peer: *peer, @@ -910,6 +891,7 @@ where /// to ensure a healthy routing table. /// > See [`Config::set_bootstrap_interval`] for details. pub fn bootstrap(&mut self) -> Result { + self.bootstrap_status.bootstrap_started(); let local_key = self.kbuckets.local_key().clone(); let info = QueryInfo::Bootstrap { peer: *local_key.preimage(), @@ -1315,9 +1297,7 @@ where let addresses = Addresses::new(a); match entry.insert(addresses.clone(), new_status) { kbucket::InsertResult::Inserted => { - if !self.any_bootstrap_successful { - self.maybe_bootstrap(); - } + self.trigger_optional_bootstrap(); let event = Event::RoutingUpdated { peer, is_new_peer: true, @@ -1433,10 +1413,13 @@ where .continue_iter_closest(query_id, target.clone(), peers, inner); } else { step.last = true; + if result.stats.num_successes() > 0 { + self.bootstrap_status.bootstrap_succeeded(); + } else { + self.bootstrap_status.bootstrap_failed(); + } }; - self.any_bootstrap_successful = true; - Some(Event::OutboundQueryProgressed { id: query_id, stats: result.stats, @@ -1637,6 +1620,11 @@ where .continue_iter_closest(query_id, target.clone(), peers, inner); } else { step.last = true; + if result.stats.num_successes() > 0 { + self.bootstrap_status.bootstrap_succeeded(); + } else { + self.bootstrap_status.bootstrap_failed(); + } } Some(Event::OutboundQueryProgressed { @@ -2083,20 +2071,13 @@ where } } - fn maybe_bootstrap(&mut self) { - if self.current_automated_bootstrap.is_some() { - return; + fn trigger_optional_bootstrap(&mut self) { + if self.bootstrap_status.can_bootstrap() { + if let Err(err) = self.bootstrap() { + tracing::warn!("Failed to trigger bootstrap: {err}"); + self.bootstrap_status.bootstrap_failed(); + }; } - - let query_id = match self.bootstrap() { - Ok(id) => id, - Err(e) => { - tracing::warn!("skipping automated bootstrap: {e}"); - return; - } - }; - - self.current_automated_bootstrap = Some(query_id); } /// Preloads a new [`Handler`] with requests that are waiting to be sent to the newly connected peer. @@ -2521,14 +2502,11 @@ where } // Poll bootstrap periodically. - if let Some(mut bootstrap_timer) = self.bootstrap_timer.take() { - if let Poll::Ready(()) = Pin::new(&mut bootstrap_timer).poll(cx) { - if let Some(interval) = self.bootstrap_interval { - bootstrap_timer.reset(interval); - self.maybe_bootstrap(); - } + if let Poll::Ready(()) = self.bootstrap_status.poll(cx) { + if let Err(err) = self.bootstrap() { + tracing::warn!("Failed to trigger bootstrap: {err}"); + self.bootstrap_status.bootstrap_failed(); } - self.bootstrap_timer = Some(bootstrap_timer); } loop { @@ -3412,3 +3390,80 @@ where .collect::>() .join(", ") } + +#[derive(Debug)] +enum BootstrapStatus { + DoBootstrapOnNewConnection(Option), + InitialBootstrapRunning(Option), + Periodic(Duration, Pin>), + Manual, +} + +impl BootstrapStatus { + fn new(interval: Option) -> Self { + match interval.is_some() { + true => Self::DoBootstrapOnNewConnection(interval), + false => Self::Manual, + } + } + + fn can_bootstrap(&self) -> bool { + match self { + Self::DoBootstrapOnNewConnection(_) => true, + Self::InitialBootstrapRunning(_) | Self::Periodic(_, _) | Self::Manual => false, + } + } + + fn bootstrap_succeeded(&mut self) { + match self { + Self::DoBootstrapOnNewConnection(interval) + | Self::InitialBootstrapRunning(interval) => { + *self = match *interval { + Some(interval) => Self::Periodic(interval, Box::pin(Delay::new(interval))), + None => Self::Manual, + }; + } + Self::Periodic(_, _) => { + // bootstrap is already running periodicaly and everything is fine. + } + Self::Manual => { + // bootstrap was user triggered so the user should handle it. + } + } + } + + fn bootstrap_failed(&mut self) { + match self { + Self::DoBootstrapOnNewConnection(_) => {} + Self::InitialBootstrapRunning(interval) => { + *self = Self::DoBootstrapOnNewConnection(*interval); + } + Self::Periodic(_, _) => { + // in future improvements, we could start an exponential backoff. + } + Self::Manual => { + // bootstrap was user triggered so the user should handle it. + } + } + } + + fn bootstrap_started(&mut self) { + match self { + Self::DoBootstrapOnNewConnection(interval) => { + *self = Self::InitialBootstrapRunning(*interval); + } + Self::InitialBootstrapRunning(_) | Self::Periodic(_, _) | Self::Manual => {} + } + } + + fn poll(&mut self, cx: &mut Context<'_>) -> Poll<()> { + if let Self::Periodic(interval, delay) = self { + if let Poll::Ready(()) = delay.as_mut().poll(cx) { + delay.reset(*interval); + return Poll::Ready(()); + } + } + + Poll::Pending + } +} From a7d2bc0237c5e4ef699932999db88f275a422184 Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Tue, 5 Dec 2023 12:26:05 +0200 Subject: [PATCH 17/25] fix existing unit tests --- protocols/kad/src/behaviour/test.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/protocols/kad/src/behaviour/test.rs b/protocols/kad/src/behaviour/test.rs index 522eebcba92..f251b1535bc 100644 --- a/protocols/kad/src/behaviour/test.rs +++ b/protocols/kad/src/behaviour/test.rs @@ -174,6 +174,7 @@ fn bootstrap() { let num_group = rng.gen_range(1..(num_total % K_VALUE.get()) + 2); let mut cfg = Config::default(); + cfg.bootstrap_interval = None; if rng.gen() { cfg.disjoint_query_paths(true); } @@ -252,7 +253,9 @@ fn query_iter() { fn run(rng: &mut impl Rng) { let num_total = rng.gen_range(2..20); - let mut swarms = build_connected_nodes(num_total, 1) + let mut config = Config::default(); + config.bootstrap_interval = None; + let mut swarms = build_connected_nodes_with_config(num_total, 1, config) .into_iter() .map(|(_a, s)| s) .collect::>(); @@ -500,6 +503,7 @@ fn put_record() { let mut config = Config::default(); config.set_replication_factor(replication_factor); + config.bootstrap_interval = None; if rng.gen() { config.disjoint_query_paths(true); } @@ -869,6 +873,7 @@ fn add_provider() { let mut config = Config::default(); config.set_replication_factor(replication_factor); + config.bootstrap_interval = None; if rng.gen() { config.disjoint_query_paths(true); } @@ -1094,6 +1099,7 @@ fn disjoint_query_does_not_finish_before_all_paths_did() { config.disjoint_query_paths(true); // I.e. setting the amount disjoint paths to be explored to 2. config.set_parallelism(NonZeroUsize::new(2).unwrap()); + config.bootstrap_interval = None; let mut alice = build_node_with_config(config); let mut trudy = build_node(); // Trudy the intrudor, an adversary. From f0e563e74954fcd34135f9446d2a4f4c3b5deeda Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Tue, 5 Dec 2023 12:48:08 +0200 Subject: [PATCH 18/25] satisfy clippy --- protocols/kad/src/behaviour/test.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/protocols/kad/src/behaviour/test.rs b/protocols/kad/src/behaviour/test.rs index f251b1535bc..1863a8d15dd 100644 --- a/protocols/kad/src/behaviour/test.rs +++ b/protocols/kad/src/behaviour/test.rs @@ -174,7 +174,7 @@ fn bootstrap() { let num_group = rng.gen_range(1..(num_total % K_VALUE.get()) + 2); let mut cfg = Config::default(); - cfg.bootstrap_interval = None; + cfg.set_bootstrap_interval(None); if rng.gen() { cfg.disjoint_query_paths(true); } @@ -254,7 +254,7 @@ fn query_iter() { fn run(rng: &mut impl Rng) { let num_total = rng.gen_range(2..20); let mut config = Config::default(); - config.bootstrap_interval = None; + config.set_bootstrap_interval(None); let mut swarms = build_connected_nodes_with_config(num_total, 1, config) .into_iter() .map(|(_a, s)| s) @@ -503,7 +503,7 @@ fn put_record() { let mut config = Config::default(); config.set_replication_factor(replication_factor); - config.bootstrap_interval = None; + config.set_bootstrap_interval(None); if rng.gen() { config.disjoint_query_paths(true); } @@ -873,7 +873,7 @@ fn add_provider() { let mut config = Config::default(); config.set_replication_factor(replication_factor); - config.bootstrap_interval = None; + config.set_bootstrap_interval(None); if rng.gen() { config.disjoint_query_paths(true); } @@ -1099,7 +1099,7 @@ fn disjoint_query_does_not_finish_before_all_paths_did() { config.disjoint_query_paths(true); // I.e. setting the amount disjoint paths to be explored to 2. config.set_parallelism(NonZeroUsize::new(2).unwrap()); - config.bootstrap_interval = None; + config.set_bootstrap_interval(None); let mut alice = build_node_with_config(config); let mut trudy = build_node(); // Trudy the intrudor, an adversary. From 3aa9101fbebec2eb31a1d2b89560a2233d93d307 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 6 Dec 2023 11:55:54 +1100 Subject: [PATCH 19/25] Slightly re-design `bootstrap::Status` --- protocols/kad/src/behaviour.rs | 132 ++++--------------------- protocols/kad/src/bootstrap.rs | 169 +++++++++++++++++++++++++++++++++ protocols/kad/src/lib.rs | 1 + protocols/kad/src/query.rs | 6 ++ 4 files changed, 195 insertions(+), 113 deletions(-) create mode 100644 protocols/kad/src/bootstrap.rs diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index cfc0adf593c..41837fde637 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -23,6 +23,7 @@ mod test; use crate::addresses::Addresses; +use crate::bootstrap::Status; use crate::handler::{Handler, HandlerEvent, HandlerIn, RequestId}; use crate::jobs::*; use crate::kbucket::{self, Distance, KBucketsTable, NodeStatus}; @@ -33,10 +34,8 @@ use crate::record::{ store::{self, RecordStore}, ProviderRecord, Record, }; -use crate::K_VALUE; +use crate::{bootstrap, K_VALUE}; use fnv::{FnvHashMap, FnvHashSet}; -use futures::Future; -use futures_timer::Delay; use instant::Instant; use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; @@ -53,7 +52,6 @@ use smallvec::SmallVec; use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; use std::fmt; use std::num::NonZeroUsize; -use std::pin::Pin; use std::task::{Context, Poll, Waker}; use std::time::Duration; use std::vec; @@ -121,7 +119,7 @@ pub struct Behaviour { store: TStore, /// Tracks the status of the current bootstrap. - bootstrap_status: BootstrapStatus, + bootstrap_status: bootstrap::Status, } /// The configurable strategies for the insertion of peers @@ -464,7 +462,7 @@ where mode: Mode::Client, auto_mode: true, no_events_waker: None, - bootstrap_status: BootstrapStatus::new(config.bootstrap_interval), + bootstrap_status: Status::new(config.bootstrap_interval), } } @@ -566,7 +564,7 @@ where }; match entry.insert(addresses.clone(), status) { kbucket::InsertResult::Inserted => { - self.trigger_optional_bootstrap(); + self.bootstrap_status.on_new_peer_in_routing_table(); self.queued_events.push_back(ToSwarm::GenerateEvent( Event::RoutingUpdated { peer: *peer, @@ -891,7 +889,6 @@ where /// to ensure a healthy routing table. /// > See [`Config::set_bootstrap_interval`] for details. pub fn bootstrap(&mut self) -> Result { - self.bootstrap_status.bootstrap_started(); let local_key = self.kbuckets.local_key().clone(); let info = QueryInfo::Bootstrap { peer: *local_key.preimage(), @@ -900,11 +897,15 @@ where }; let peers = self.kbuckets.closest_keys(&local_key).collect::>(); if peers.is_empty() { - Err(NoKnownPeers()) - } else { - let inner = QueryInner::new(info); - Ok(self.queries.add_iter_closest(local_key, peers, inner)) + return Err(NoKnownPeers()); } + + let inner = QueryInner::new(info); + let id = self.queries.add_iter_closest(local_key, peers, inner); + + self.bootstrap_status.on_started(); + + Ok(id) } /// Establishes the local node as a provider of a value for the given key. @@ -1297,7 +1298,7 @@ where let addresses = Addresses::new(a); match entry.insert(addresses.clone(), new_status) { kbucket::InsertResult::Inserted => { - self.trigger_optional_bootstrap(); + self.bootstrap_status.on_new_peer_in_routing_table(); let event = Event::RoutingUpdated { peer, is_new_peer: true, @@ -1413,11 +1414,7 @@ where .continue_iter_closest(query_id, target.clone(), peers, inner); } else { step.last = true; - if result.stats.num_successes() > 0 { - self.bootstrap_status.bootstrap_succeeded(); - } else { - self.bootstrap_status.bootstrap_failed(); - } + self.bootstrap_status.on_result(&result.stats); }; Some(Event::OutboundQueryProgressed { @@ -1620,11 +1617,7 @@ where .continue_iter_closest(query_id, target.clone(), peers, inner); } else { step.last = true; - if result.stats.num_successes() > 0 { - self.bootstrap_status.bootstrap_succeeded(); - } else { - self.bootstrap_status.bootstrap_failed(); - } + self.bootstrap_status.on_result(&result.stats); } Some(Event::OutboundQueryProgressed { @@ -2071,15 +2064,6 @@ where } } - fn trigger_optional_bootstrap(&mut self) { - if self.bootstrap_status.can_bootstrap() { - if let Err(err) = self.bootstrap() { - tracing::warn!("Failed to trigger bootstrap: {err}"); - self.bootstrap_status.bootstrap_failed(); - }; - } - } - /// Preloads a new [`Handler`] with requests that are waiting to be sent to the newly connected peer. fn preload_new_handler( &mut self, @@ -2502,10 +2486,9 @@ where } // Poll bootstrap periodically. - if let Poll::Ready(()) = self.bootstrap_status.poll(cx) { - if let Err(err) = self.bootstrap() { - tracing::warn!("Failed to trigger bootstrap: {err}"); - self.bootstrap_status.bootstrap_failed(); + if let Poll::Ready(()) = self.bootstrap_status.poll_next_bootstrap(cx) { + if let Err(e) = self.bootstrap() { + tracing::warn!("Failed to trigger bootstrap: {e}"); } } @@ -3390,80 +3373,3 @@ where .collect::>() .join(", ") } - -#[derive(Debug)] -enum BootstrapStatus { - DoBootstrapOnNewConnection(Option), - InitialBootstrapRunning(Option), - Periodic(Duration, Pin>), - Manual, -} - -impl BootstrapStatus { - fn new(interval: Option) -> Self { - match interval.is_some() { - true => Self::DoBootstrapOnNewConnection(interval), - false => Self::Manual, - } - } - - fn can_bootstrap(&self) -> bool { - match self { - Self::DoBootstrapOnNewConnection(_) => true, - Self::InitialBootstrapRunning(_) | Self::Periodic(_, _) | Self::Manual => false, - } - } - - fn bootstrap_succeeded(&mut self) { - match self { - Self::DoBootstrapOnNewConnection(interval) - | Self::InitialBootstrapRunning(interval) => { - *self = match *interval { - Some(interval) => Self::Periodic(interval, Box::pin(Delay::new(interval))), - None => Self::Manual, - }; - } - Self::Periodic(_, _) => { - // bootstrap is already running periodicaly and everything is fine. - } - Self::Manual => { - // bootstrap was user triggered so the user should handle it. - } - } - } - - fn bootstrap_failed(&mut self) { - match self { - Self::DoBootstrapOnNewConnection(_) => {} - Self::InitialBootstrapRunning(interval) => { - *self = Self::DoBootstrapOnNewConnection(*interval); - } - Self::Periodic(_, _) => { - // in future improvements, we could start an exponential backoff. - } - Self::Manual => { - // bootstrap was user triggered so the user should handle it. - } - } - } - - fn bootstrap_started(&mut self) { - match self { - Self::DoBootstrapOnNewConnection(interval) => { - *self = Self::InitialBootstrapRunning(*interval); - } - Self::InitialBootstrapRunning(_) | Self::Periodic(_, _) | Self::Manual => {} - } - } - - fn poll(&mut self, cx: &mut Context<'_>) -> Poll<()> { - if let Self::Periodic(interval, delay) = self { - if let Poll::Ready(()) = delay.as_mut().poll(cx) { - delay.reset(*interval); - return Poll::Ready(()); - } - } - - Poll::Pending - } -} diff --git a/protocols/kad/src/bootstrap.rs b/protocols/kad/src/bootstrap.rs new file mode 100644 index 00000000000..2dd3218c408 --- /dev/null +++ b/protocols/kad/src/bootstrap.rs @@ -0,0 +1,169 @@ +use futures::FutureExt; +use std::task::{Context, Poll, Waker}; +use std::time::Duration; + +use futures_timer::Delay; + +use crate::QueryStats; + +#[derive(Debug)] +pub(crate) struct Status { + interval: Option, + next_periodic_bootstrap: Option, + waker: Option, + + bootstrap_asap: bool, + + is_bootstrapping: bool, + any_bootstrap_succeeded: bool, +} + +impl Status { + pub(crate) fn new(interval: Option) -> Self { + Self { + interval, + next_periodic_bootstrap: interval.map(Delay::new), + waker: None, + bootstrap_asap: false, + is_bootstrapping: false, + any_bootstrap_succeeded: false, + } + } + + pub(crate) fn on_new_peer_in_routing_table(&mut self) { + self.bootstrap_asap = true; + + if let Some(waker) = self.waker.take() { + waker.wake() + } + } + + pub(crate) fn on_started(&mut self) { + self.is_bootstrapping = true; + } + + pub(crate) fn on_result(&mut self, stats: &QueryStats) { + if stats.num_successes() > 0 { + self.any_bootstrap_succeeded = true; + } + + self.is_bootstrapping = false; + } + + pub(crate) fn poll_next_bootstrap(&mut self, cx: &mut Context<'_>) -> Poll<()> { + if self.bootstrap_asap { + self.bootstrap_asap = false; + if let (Some(interval), Some(delay)) = + (self.interval, self.next_periodic_bootstrap.as_mut()) + { + delay.reset(interval); + } + + return Poll::Ready(()); + } + + if let (Some(interval), Some(delay)) = + (self.interval, self.next_periodic_bootstrap.as_mut()) + { + if let Poll::Ready(()) = delay.poll_unpin(cx) { + delay.reset(interval); + return Poll::Ready(()); + } + } + + self.waker = Some(cx.waker().clone()); + Poll::Pending + } + + #[cfg(test)] + async fn next(&mut self) { + std::future::poll_fn(|cx| self.poll_next_bootstrap(cx)).await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use instant::Instant; + + const MS_100: Duration = Duration::from_millis(100); + + #[async_std::test] + async fn given_periodic_bootstrap_when_failed_then_will_try_again_on_next_connection() { + let mut status = Status::new(Some(Duration::from_secs(1))); + + status.next().await; // Await periodic bootstrap + + status.on_started(); + status.on_result(&QueryStats::empty().with_successes(0)); // Boostrap failed + + status.on_new_peer_in_routing_table(); // Connected to a new peer though! + + assert!( + async_std::future::timeout(Duration::from_millis(500), status.next()) + .await + .is_ok(), + "bootstrap to be triggered in less then the configured delay because we connected to a new peer" + ); + } + + #[test] + fn given_no_periodic_bootstrap_when_failed_then_will_try_again_on_next_connection() { + let mut status = Status::new(None); + + // User manually triggered a bootstrap + status.on_started(); + status.on_result(&QueryStats::empty().with_successes(0)); // Boostrap failed + + status.on_new_peer_in_routing_table(); // Connected to a new peer though! + + assert!( + status.next().now_or_never().is_some(), + "bootstrap to be triggered immediately because we connected to a new peer" + ) + } + + #[async_std::test] + async fn given_periodic_bootstrap_when_routing_table_updated_then_wont_bootstrap_until_next_interval( + ) { + let mut status = Status::new(Some(MS_100)); + + status.on_new_peer_in_routing_table(); + + let start = Instant::now(); + status.next().await; + let elapsed = Instant::now().duration_since(start); + + assert!(elapsed < Duration::from_millis(1)); + + let start = Instant::now(); + status.next().await; + let elapsed = Instant::now().duration_since(start); + + assert!(elapsed > MS_100); + } + + #[async_std::test] + async fn given_no_periodic_bootstrap_when_new_entry_then_will_bootstrap() { + let mut status = Status::new(None); + + status.on_new_peer_in_routing_table(); + + status.next().await; + } + + #[async_std::test] + async fn given_periodic_bootstrap_triggers_periodically() { + let mut status = Status::new(Some(MS_100)); + + for _ in 0..5 { + let start = Instant::now(); + + status.next().await; + + let elapsed = Instant::now().duration_since(start); + + assert!(elapsed > (MS_100 - Duration::from_millis(10))); // Subtract 10ms to avoid flakes. + } + } +} diff --git a/protocols/kad/src/lib.rs b/protocols/kad/src/lib.rs index 519b67f9d7a..bc01b9fd3ce 100644 --- a/protocols/kad/src/lib.rs +++ b/protocols/kad/src/lib.rs @@ -37,6 +37,7 @@ mod addresses; mod behaviour; +mod bootstrap; mod handler; mod jobs; mod kbucket; diff --git a/protocols/kad/src/query.rs b/protocols/kad/src/query.rs index bb240d5864a..94bf1175390 100644 --- a/protocols/kad/src/query.rs +++ b/protocols/kad/src/query.rs @@ -511,4 +511,10 @@ impl QueryStats { end: std::cmp::max(self.end, other.end), } } + + #[cfg(test)] + pub(crate) fn with_successes(mut self, successes: u32) -> Self { + self.success = successes; + self + } } From 850bc22c7ccbe8efe8386a364d3452f7b26924aa Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Sun, 12 Nov 2023 17:55:20 +0200 Subject: [PATCH 20/25] feat(kad): Add periodic and automatic bootstrap --- Cargo.lock | 2 +- Cargo.toml | 2 +- misc/server/CHANGELOG.md | 2 +- protocols/kad/CHANGELOG.md | 8 +- protocols/kad/Cargo.toml | 2 +- protocols/kad/src/behaviour.rs | 66 ++++--- protocols/kad/src/behaviour/test.rs | 20 +- protocols/kad/src/bootstrap.rs | 275 +++++++++++++++++++++++----- protocols/kad/src/query.rs | 6 - 9 files changed, 292 insertions(+), 91 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 32514f048bf..56babca4203 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2850,7 +2850,7 @@ dependencies = [ [[package]] name = "libp2p-kad" -version = "0.45.3" +version = "0.45.4" dependencies = [ "arrayvec", "async-std", diff --git a/Cargo.toml b/Cargo.toml index 4155ab1d826..287df70a539 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,7 +85,7 @@ libp2p-floodsub = { version = "0.44.0", path = "protocols/floodsub" } libp2p-gossipsub = { version = "0.46.1", path = "protocols/gossipsub" } libp2p-identify = { version = "0.44.1", path = "protocols/identify" } libp2p-identity = { version = "0.2.8" } -libp2p-kad = { version = "0.45.3", path = "protocols/kad" } +libp2p-kad = { version = "0.45.4", path = "protocols/kad" } libp2p-mdns = { version = "0.45.1", path = "protocols/mdns" } libp2p-memory-connection-limits = { version = "0.2.0", path = "misc/memory-connection-limits" } libp2p-metrics = { version = "0.14.1", path = "misc/metrics" } diff --git a/misc/server/CHANGELOG.md b/misc/server/CHANGELOG.md index 6bf84fd7e76..c1cfee57523 100644 --- a/misc/server/CHANGELOG.md +++ b/misc/server/CHANGELOG.md @@ -2,7 +2,7 @@ ### Changed -- Remove `Behaviour::bootstrap` polling. +- Use periodic and automatic bootstrap of Kademlia. See [PR 4838](https://github.com/libp2p/rust-libp2p/pull/4838). ## 0.12.5 diff --git a/protocols/kad/CHANGELOG.md b/protocols/kad/CHANGELOG.md index 1eed39a3eb8..7552e8541ef 100644 --- a/protocols/kad/CHANGELOG.md +++ b/protocols/kad/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.45.4 + +- Add periodic and automatic bootstrap. + See [PR 4838](https://github.com/libp2p/rust-libp2p/pull/4838). + ## 0.45.3 - The progress of the close query iterator shall be decided by ANY of the new peers. @@ -5,9 +10,6 @@ ## 0.45.2 -- Automatically `bootstrap` every 5 minutes. - This can be configured using the `bootstrap_interval` config. - See [PR 4838](https://github.com/libp2p/rust-libp2p/pull/4838) - Ensure `Multiaddr` handled and returned by `Behaviour` are `/p2p` terminated. See [PR 4596](https://github.com/libp2p/rust-libp2p/pull/4596). diff --git a/protocols/kad/Cargo.toml b/protocols/kad/Cargo.toml index 96f5f10819f..bde0d5f7c84 100644 --- a/protocols/kad/Cargo.toml +++ b/protocols/kad/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-kad" edition = "2021" rust-version = { workspace = true } description = "Kademlia protocol for libp2p" -version = "0.45.3" +version = "0.45.4" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 2d55c6a7d36..844d3d5f8d1 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -23,7 +23,6 @@ mod test; use crate::addresses::Addresses; -use crate::bootstrap::Status; use crate::handler::{Handler, HandlerEvent, HandlerIn, RequestId}; use crate::jobs::*; use crate::kbucket::{self, Distance, KBucketsTable, NodeStatus}; @@ -185,7 +184,8 @@ pub struct Config { provider_publication_interval: Option, kbucket_inserts: BucketInserts, caching: Caching, - bootstrap_interval: Option, + periodic_bootstrap_interval: Option, + automatic_bootstrap_throttle: Option, } impl Default for Config { @@ -202,7 +202,8 @@ impl Default for Config { provider_record_ttl: Some(Duration::from_secs(24 * 60 * 60)), kbucket_inserts: BucketInserts::OnConnected, caching: Caching::Enabled { max_peers: 1 }, - bootstrap_interval: Some(Duration::from_secs(5 * 60)), + periodic_bootstrap_interval: Some(Duration::from_secs(5 * 60)), + automatic_bootstrap_throttle: Some(Duration::from_millis(10)), } } } @@ -398,11 +399,27 @@ impl Config { self } - /// Sets the interval on which [`Behaviour::bootstrap`] is called from [`Behaviour::poll`] + /// Sets the interval on which [`Behaviour::bootstrap`] is called periodically. /// - /// `None` means we don't bootstrap at all. - pub fn set_bootstrap_interval(&mut self, interval: Option) -> &mut Self { - self.bootstrap_interval = interval; + /// * Default to `5` minutes. + /// * Set to `None` to disable periodic bootstrap. + pub fn set_periodic_bootstrap_interval(&mut self, interval: Option) -> &mut Self { + self.periodic_bootstrap_interval = interval; + self + } + + /// Sets the time to wait before calling [`Behaviour::bootstrap`] after a new peer is inserted in the routing table. + /// This prevent cascading bootstrap requests when multiple peers are inserted into the routing table "at the same time". + /// This also allows to wait a little bit for other potential peers to be inserted into the routing table before + /// triggering a bootstrap, giving more context to the future bootstrap request. + /// + /// * Default to `10` ms. + /// * Set to `Some(Duration::ZERO)` to never wait before triggering a bootstrap request when a new peer + /// is inserted in the routing table. + /// * Set to `None` to disable automatic bootstrap (no bootstrap request will be triggered when a new + /// peer is inserted in the routing table). + pub fn set_automatic_bootstrap_throttle(&mut self, duration: Option) -> &mut Self { + self.automatic_bootstrap_throttle = duration; self } } @@ -462,7 +479,10 @@ where mode: Mode::Client, auto_mode: true, no_events_waker: None, - bootstrap_status: Status::new(config.bootstrap_interval), + bootstrap_status: bootstrap::Status::new( + config.periodic_bootstrap_interval, + config.automatic_bootstrap_throttle, + ), } } @@ -883,11 +903,14 @@ where /// /// > **Note**: Bootstrapping requires at least one node of the DHT to be known. /// > See [`Behaviour::add_address`]. - /// > **Note**: Bootstrap does not require to be called manually. It is automatically - /// invoked at regular intervals based on the configured bootstrapping interval. - /// The bootstrapping interval is used to call bootstrap periodically + /// + /// > **Note**: Bootstrap does not require to be called manually. It is periodically + /// invoked at regular intervals based on the configured `periodic_bootstrap_interval` (see + /// [`Config::set_periodic_bootstrap_interval`] for details) and it is also automatically invoked + /// when a new peer is inserted in the routing table based on the `automatic_bootstrap_throttle` + /// (see [`Config::set_automatic_bootstrap_throttle`] for details). + /// These two config parameters are used to call [`Behaviour::bootstrap`] periodically and automatically /// to ensure a healthy routing table. - /// > See [`Config::set_bootstrap_interval`] for details. pub fn bootstrap(&mut self) -> Result { let local_key = self.kbuckets.local_key().clone(); let info = QueryInfo::Bootstrap { @@ -897,15 +920,12 @@ where }; let peers = self.kbuckets.closest_keys(&local_key).collect::>(); if peers.is_empty() { - return Err(NoKnownPeers()); + Err(NoKnownPeers()) + } else { + self.bootstrap_status.on_started(); + let inner = QueryInner::new(info); + Ok(self.queries.add_iter_closest(local_key, peers, inner)) } - - let inner = QueryInner::new(info); - let id = self.queries.add_iter_closest(local_key, peers, inner); - - self.bootstrap_status.on_started(); - - Ok(id) } /// Establishes the local node as a provider of a value for the given key. @@ -1415,7 +1435,7 @@ where .continue_iter_closest(query_id, target.clone(), peers, inner); } else { step.last = true; - self.bootstrap_status.on_result(&result.stats); + self.bootstrap_status.on_finish(); }; Some(Event::OutboundQueryProgressed { @@ -1618,7 +1638,7 @@ where .continue_iter_closest(query_id, target.clone(), peers, inner); } else { step.last = true; - self.bootstrap_status.on_result(&result.stats); + self.bootstrap_status.on_finish(); } Some(Event::OutboundQueryProgressed { @@ -2491,7 +2511,7 @@ where self.put_record_job = Some(job); } - // Poll bootstrap periodically. + // Poll bootstrap periodically and automatically. if let Poll::Ready(()) = self.bootstrap_status.poll_next_bootstrap(cx) { if let Err(e) = self.bootstrap() { tracing::warn!("Failed to trigger bootstrap: {e}"); diff --git a/protocols/kad/src/behaviour/test.rs b/protocols/kad/src/behaviour/test.rs index 1863a8d15dd..eb55e076341 100644 --- a/protocols/kad/src/behaviour/test.rs +++ b/protocols/kad/src/behaviour/test.rs @@ -174,7 +174,9 @@ fn bootstrap() { let num_group = rng.gen_range(1..(num_total % K_VALUE.get()) + 2); let mut cfg = Config::default(); - cfg.set_bootstrap_interval(None); + // Disabling periodic bootstrap and automatic bootstrap to prevent the bootstrap from triggering automatically. + cfg.set_periodic_bootstrap_interval(None); + cfg.set_automatic_bootstrap_throttle(None); if rng.gen() { cfg.disjoint_query_paths(true); } @@ -254,7 +256,9 @@ fn query_iter() { fn run(rng: &mut impl Rng) { let num_total = rng.gen_range(2..20); let mut config = Config::default(); - config.set_bootstrap_interval(None); + // Disabling periodic bootstrap and automatic bootstrap to prevent the bootstrap from triggering automatically. + config.set_periodic_bootstrap_interval(None); + config.set_automatic_bootstrap_throttle(None); let mut swarms = build_connected_nodes_with_config(num_total, 1, config) .into_iter() .map(|(_a, s)| s) @@ -503,7 +507,9 @@ fn put_record() { let mut config = Config::default(); config.set_replication_factor(replication_factor); - config.set_bootstrap_interval(None); + // Disabling periodic bootstrap and automatic bootstrap to prevent the bootstrap from triggering automatically. + config.set_periodic_bootstrap_interval(None); + config.set_automatic_bootstrap_throttle(None); if rng.gen() { config.disjoint_query_paths(true); } @@ -873,7 +879,9 @@ fn add_provider() { let mut config = Config::default(); config.set_replication_factor(replication_factor); - config.set_bootstrap_interval(None); + // Disabling periodic bootstrap and automatic bootstrap to prevent the bootstrap from triggering automatically. + config.set_periodic_bootstrap_interval(None); + config.set_automatic_bootstrap_throttle(None); if rng.gen() { config.disjoint_query_paths(true); } @@ -1099,7 +1107,9 @@ fn disjoint_query_does_not_finish_before_all_paths_did() { config.disjoint_query_paths(true); // I.e. setting the amount disjoint paths to be explored to 2. config.set_parallelism(NonZeroUsize::new(2).unwrap()); - config.set_bootstrap_interval(None); + // Disabling periodic bootstrap and automatic bootstrap to prevent the bootstrap from triggering automatically. + config.set_periodic_bootstrap_interval(None); + config.set_automatic_bootstrap_throttle(None); let mut alice = build_node_with_config(config); let mut trudy = build_node(); // Trudy the intrudor, an adversary. diff --git a/protocols/kad/src/bootstrap.rs b/protocols/kad/src/bootstrap.rs index 2dd3218c408..681f7f2071a 100644 --- a/protocols/kad/src/bootstrap.rs +++ b/protocols/kad/src/bootstrap.rs @@ -4,73 +4,126 @@ use std::time::Duration; use futures_timer::Delay; -use crate::QueryStats; - #[derive(Debug)] pub(crate) struct Status { - interval: Option, - next_periodic_bootstrap: Option, + /// If the user did not disable periodic bootstrap (by providing `None` for `periodic_interval`) + /// this is the periodic interval and the delay of the current period. When `Delay` finishes, + /// a bootstrap will be triggered and the `Delay` will be reset. + interval_and_delay: Option<(Duration, Delay)>, + + /// Configured duration to wait before triggering a bootstrap when a new peer + /// is inserted in the routing table. `None` if automatic bootstrap is disabled. + automatic_throttle: Option, + /// Timer that will be set (if automatic bootstrap is not disabled) when a new peer is inserted + /// in the routing table. When it finishes, it will trigger a bootstrap and will be set to `None` + /// again. If an other new peer is inserted in the routing table before this timer finishes, + /// the timer is reset. + throttle_timer: Option, + + /// Number of bootstrap requests currently in progress. We ensure neither periodic bootstrap + /// or automatic bootstrap trigger new requests when there is still some running. + current_bootstrap_requests: usize, + /// Waker to wake up the `poll` method if progress is ready to be made. waker: Option, - - bootstrap_asap: bool, - - is_bootstrapping: bool, - any_bootstrap_succeeded: bool, } impl Status { - pub(crate) fn new(interval: Option) -> Self { + pub(crate) fn new( + periodic_interval: Option, + automatic_throttle: Option, + ) -> Self { Self { - interval, - next_periodic_bootstrap: interval.map(Delay::new), + interval_and_delay: periodic_interval.map(|interval| (interval, Delay::new(interval))), waker: None, - bootstrap_asap: false, - is_bootstrapping: false, - any_bootstrap_succeeded: false, + automatic_throttle, + throttle_timer: None, + current_bootstrap_requests: 0, } } pub(crate) fn on_new_peer_in_routing_table(&mut self) { - self.bootstrap_asap = true; + // Registering `self.throttle_timer` means scheduling a bootstrap. + // A bootstrap will be triggered when `self.throttle_timer` finishes. + // A `throttle_timer` is useful to not trigger a batch of bootstraps when a + // batch of peers is inserted into the routing table. + if let Some(throttle_duration) = self.automatic_throttle { + self.throttle_timer = Some(throttle_duration.into()); + } else { + // The user disabled bootstrapping on new peer in the routing table. + } + // Waking up the waker that could have been registered. if let Some(waker) = self.waker.take() { waker.wake() } } pub(crate) fn on_started(&mut self) { - self.is_bootstrapping = true; + // No periodic or automatic bootstrap will be triggered as long as + // `self.current_bootstrap_requests > 0` but the user could still manually + // trigger a bootstrap. + self.current_bootstrap_requests += 1; + + // Canceling the `throttle_timer` if any since a bootstrap request is being triggered right now. + self.throttle_timer = None; + + // Resetting the `delay` if any since a bootstrap request is being triggered right now. + if let Some((interval, delay)) = self.interval_and_delay.as_mut() { + delay.reset(*interval); + } } - pub(crate) fn on_result(&mut self, stats: &QueryStats) { - if stats.num_successes() > 0 { - self.any_bootstrap_succeeded = true; + pub(crate) fn on_finish(&mut self) { + if let Some(value) = self.current_bootstrap_requests.checked_sub(1) { + self.current_bootstrap_requests = value; + } else { + debug_assert!( + false, + "Could not decrement current_bootstrap_requests because it's already 0" + ); } - self.is_bootstrapping = false; + // Waking up the waker that could have been registered. + if let Some(waker) = self.waker.take() { + waker.wake(); + } } pub(crate) fn poll_next_bootstrap(&mut self, cx: &mut Context<'_>) -> Poll<()> { - if self.bootstrap_asap { - self.bootstrap_asap = false; - if let (Some(interval), Some(delay)) = - (self.interval, self.next_periodic_bootstrap.as_mut()) - { - delay.reset(interval); + if self.current_bootstrap_requests > 0 { + // Some bootstrap request(s) is(are) currently running. + self.waker = Some(cx.waker().clone()); + return Poll::Pending; + } + + if let Some(throttle_delay) = &mut self.throttle_timer { + // A `throttle_timer` has been registered. It means one or more peers have been + // inserted into the routing table and that a bootstrap request should be triggered. + // However, to not risk cascading bootstrap requests, we wait a little time to ensure + // the user will not add more peers in the routing table in the next "throttle_timer" remaining. + if throttle_delay.poll_unpin(cx).is_ready() { + // The `throttle_timer` is finished, triggering bootstrap right now. + // The call to `on_started` will reset `throttle_delay`. + return Poll::Ready(()); } - return Poll::Ready(()); + // The `throttle_timer` is not finished but the periodic interval for triggering bootstrap might be reached. + } else { + // No new peer has recently been inserted into the routing table or automatic bootstrap is disabled. } - if let (Some(interval), Some(delay)) = - (self.interval, self.next_periodic_bootstrap.as_mut()) - { + // Checking if the user has enabled the periodic bootstrap feature. + if let Some((_, delay)) = self.interval_and_delay.as_mut() { if let Poll::Ready(()) = delay.poll_unpin(cx) { - delay.reset(interval); + // It is time to run the periodic bootstrap. + // The call to `on_started` will reset `delay`. return Poll::Ready(()); } + } else { + // The user disabled periodic bootstrap. } + // Registering the `waker` so that we can wake up when calling `on_new_peer_in_routing_table`. self.waker = Some(cx.waker().clone()); Poll::Pending } @@ -81,23 +134,71 @@ impl Status { } } +/// Simple enum to indicate when the throttle timer resolves. +/// A dedicated `Immediate` variant is necessary because creating +/// `Delay::new(Duration::ZERO)` does not always actually resolve +/// immediately. +#[derive(Debug)] +enum ThrottleTimer { + Immediate, + Delay(Delay), +} + +impl From for ThrottleTimer { + fn from(value: Duration) -> Self { + if value.is_zero() { + Self::Immediate + } else { + Self::Delay(Delay::new(value)) + } + } +} + +impl futures::Future for ThrottleTimer { + type Output = (); + + fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.get_mut() { + Self::Immediate => Poll::Ready(()), + Self::Delay(delay) => delay.poll_unpin(cx), + } + } +} + #[cfg(test)] mod tests { use super::*; use instant::Instant; + const MS_5: Duration = Duration::from_millis(5); const MS_100: Duration = Duration::from_millis(100); + fn do_bootstrap(status: &mut Status) { + status.on_started(); + status.on_finish(); + } + + async fn await_and_do_bootstrap(status: &mut Status) { + status.next().await; + do_bootstrap(status); + } + #[async_std::test] - async fn given_periodic_bootstrap_when_failed_then_will_try_again_on_next_connection() { - let mut status = Status::new(Some(Duration::from_secs(1))); + async fn immediate_automatic_bootstrap_is_triggered_immediately() { + let mut status = Status::new(Some(Duration::from_secs(1)), Some(Duration::ZERO)); - status.next().await; // Await periodic bootstrap + await_and_do_bootstrap(&mut status).await; // Wait for periodic bootstrap - status.on_started(); - status.on_result(&QueryStats::empty().with_successes(0)); // Boostrap failed + assert!( + status.next().now_or_never().is_none(), + "bootstrap to not be triggered immediately because periodic bootstrap is in ~1s" + ); status.on_new_peer_in_routing_table(); // Connected to a new peer though! + assert!( + status.next().now_or_never().is_some(), + "bootstrap to be triggered immediately because we connected to a new peer" + ); assert!( async_std::future::timeout(Duration::from_millis(500), status.next()) @@ -107,13 +208,37 @@ mod tests { ); } + #[async_std::test] + async fn delayed_automatic_bootstrap_is_triggered_before_periodic_bootstrap() { + let mut status = Status::new(Some(Duration::from_secs(1)), Some(MS_5)); + + await_and_do_bootstrap(&mut status).await; // Wait for periodic bootstrap + + assert!( + status.next().now_or_never().is_none(), + "bootstrap to not be triggered immediately because periodic bootstrap is in ~1s" + ); + + status.on_new_peer_in_routing_table(); // Connected to a new peer though! + assert!( + status.next().now_or_never().is_none(), + "bootstrap to not be triggered immediately because throttle is 5ms" + ); + + assert!( + async_std::future::timeout(MS_5 * 2, status.next()) + .await + .is_ok(), + "bootstrap to be triggered in less then the configured periodic delay because we connected to a new peer" + ); + } + #[test] - fn given_no_periodic_bootstrap_when_failed_then_will_try_again_on_next_connection() { - let mut status = Status::new(None); + fn given_no_periodic_bootstrap_and_immediate_automatic_bootstrap_try_on_next_connection() { + let mut status = Status::new(None, Some(Duration::ZERO)); // User manually triggered a bootstrap - status.on_started(); - status.on_result(&QueryStats::empty().with_successes(0)); // Boostrap failed + do_bootstrap(&mut status); status.on_new_peer_in_routing_table(); // Connected to a new peer though! @@ -126,26 +251,27 @@ mod tests { #[async_std::test] async fn given_periodic_bootstrap_when_routing_table_updated_then_wont_bootstrap_until_next_interval( ) { - let mut status = Status::new(Some(MS_100)); + let mut status = Status::new(Some(MS_100), Some(MS_5)); status.on_new_peer_in_routing_table(); let start = Instant::now(); - status.next().await; + await_and_do_bootstrap(&mut status).await; let elapsed = Instant::now().duration_since(start); - assert!(elapsed < Duration::from_millis(1)); + assert!(elapsed < MS_5 * 2); let start = Instant::now(); - status.next().await; + await_and_do_bootstrap(&mut status).await; let elapsed = Instant::now().duration_since(start); assert!(elapsed > MS_100); } #[async_std::test] - async fn given_no_periodic_bootstrap_when_new_entry_then_will_bootstrap() { - let mut status = Status::new(None); + async fn given_no_periodic_bootstrap_and_automatic_bootstrap_when_new_entry_then_will_bootstrap( + ) { + let mut status = Status::new(None, Some(Duration::ZERO)); status.on_new_peer_in_routing_table(); @@ -153,17 +279,66 @@ mod tests { } #[async_std::test] - async fn given_periodic_bootstrap_triggers_periodically() { - let mut status = Status::new(Some(MS_100)); + async fn given_periodic_bootstrap_and_no_automatic_bootstrap_triggers_periodically() { + let mut status = Status::new(Some(MS_100), None); for _ in 0..5 { let start = Instant::now(); - status.next().await; + await_and_do_bootstrap(&mut status).await; let elapsed = Instant::now().duration_since(start); assert!(elapsed > (MS_100 - Duration::from_millis(10))); // Subtract 10ms to avoid flakes. } } + + #[async_std::test] + async fn given_no_periodic_bootstrap_and_automatic_bootstrap_reset_throttle_when_multiple_peers( + ) { + let mut status = Status::new(None, Some(MS_100)); + + status.on_new_peer_in_routing_table(); + for _ in 0..10 { + Delay::new(MS_100 / 2).await; + status.on_new_peer_in_routing_table(); // should reset throttle_timer + } + assert!( + status.next().now_or_never().is_none(), + "bootstrap to not be triggered immediately because throttle has been reset" + ); + + Delay::new(MS_100 - MS_5).await; + + assert!( + async_std::future::timeout(MS_5*2, status.next()) + .await + .is_ok(), + "bootstrap to be triggered in the configured throttle delay because we connected to a new peer" + ); + } + + #[async_std::test] + async fn given_periodic_bootstrap_and_no_automatic_bootstrap_manually_triggering_prevent_periodic( + ) { + let mut status = Status::new(Some(MS_100), None); + + status.on_started(); // first manually triggering + status.on_started(); // second manually triggering + status.on_finish(); // one finishes + + assert!( + async_std::future::timeout(10 * MS_100, status.next()) + .await + .is_err(), + "periodic bootstrap to never be triggered because one is still being run" + ); + + status.on_finish(); // all manual bootstrap finished + + assert!( + status.next().now_or_never().is_some(), + "bootstrap to be triggered immediately because no more bootstrap requests are running" + ) + } } diff --git a/protocols/kad/src/query.rs b/protocols/kad/src/query.rs index 94bf1175390..bb240d5864a 100644 --- a/protocols/kad/src/query.rs +++ b/protocols/kad/src/query.rs @@ -511,10 +511,4 @@ impl QueryStats { end: std::cmp::max(self.end, other.end), } } - - #[cfg(test)] - pub(crate) fn with_successes(mut self, successes: u32) -> Self { - self.success = successes; - self - } } From 99458a7798adbb938b9fd263ddedf3e8112ba47e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20RIBEAU?= Date: Thu, 15 Feb 2024 10:04:21 +0100 Subject: [PATCH 21/25] Preventing automatic_throttle configuration --- protocols/kad/src/behaviour.rs | 13 ++++++++----- protocols/kad/src/bootstrap.rs | 10 ++++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 844d3d5f8d1..6a151bc66d6 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -203,7 +203,7 @@ impl Default for Config { kbucket_inserts: BucketInserts::OnConnected, caching: Caching::Enabled { max_peers: 1 }, periodic_bootstrap_interval: Some(Duration::from_secs(5 * 60)), - automatic_bootstrap_throttle: Some(Duration::from_millis(10)), + automatic_bootstrap_throttle: Some(bootstrap::DEFAULT_AUTOMATIC_THROTTLE), } } } @@ -413,12 +413,16 @@ impl Config { /// This also allows to wait a little bit for other potential peers to be inserted into the routing table before /// triggering a bootstrap, giving more context to the future bootstrap request. /// - /// * Default to `10` ms. + /// * Default to `500` ms. /// * Set to `Some(Duration::ZERO)` to never wait before triggering a bootstrap request when a new peer /// is inserted in the routing table. /// * Set to `None` to disable automatic bootstrap (no bootstrap request will be triggered when a new /// peer is inserted in the routing table). - pub fn set_automatic_bootstrap_throttle(&mut self, duration: Option) -> &mut Self { + #[cfg(test)] + pub(crate) fn set_automatic_bootstrap_throttle( + &mut self, + duration: Option, + ) -> &mut Self { self.automatic_bootstrap_throttle = duration; self } @@ -907,8 +911,7 @@ where /// > **Note**: Bootstrap does not require to be called manually. It is periodically /// invoked at regular intervals based on the configured `periodic_bootstrap_interval` (see /// [`Config::set_periodic_bootstrap_interval`] for details) and it is also automatically invoked - /// when a new peer is inserted in the routing table based on the `automatic_bootstrap_throttle` - /// (see [`Config::set_automatic_bootstrap_throttle`] for details). + /// when a new peer is inserted in the routing table. /// These two config parameters are used to call [`Behaviour::bootstrap`] periodically and automatically /// to ensure a healthy routing table. pub fn bootstrap(&mut self) -> Result { diff --git a/protocols/kad/src/bootstrap.rs b/protocols/kad/src/bootstrap.rs index 681f7f2071a..58fa662d414 100644 --- a/protocols/kad/src/bootstrap.rs +++ b/protocols/kad/src/bootstrap.rs @@ -4,6 +4,9 @@ use std::time::Duration; use futures_timer::Delay; +/// Default value chosen at ``. +pub(crate) const DEFAULT_AUTOMATIC_THROTTLE: Duration = Duration::from_millis(500); + #[derive(Debug)] pub(crate) struct Status { /// If the user did not disable periodic bootstrap (by providing `None` for `periodic_interval`) @@ -282,14 +285,13 @@ mod tests { async fn given_periodic_bootstrap_and_no_automatic_bootstrap_triggers_periodically() { let mut status = Status::new(Some(MS_100), None); - for _ in 0..5 { - let start = Instant::now(); - + let start = Instant::now(); + for i in 1..6 { await_and_do_bootstrap(&mut status).await; let elapsed = Instant::now().duration_since(start); - assert!(elapsed > (MS_100 - Duration::from_millis(10))); // Subtract 10ms to avoid flakes. + assert!(elapsed > (i * MS_100 - Duration::from_millis(10))); // Subtract 10ms to avoid flakes. } } From fcafa6078298c788bd1f9deda566f3bfdfbd6381 Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Tue, 20 Feb 2024 20:28:03 +0100 Subject: [PATCH 22/25] satisfy clippy --- protocols/kad/src/behaviour/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/kad/src/behaviour/test.rs b/protocols/kad/src/behaviour/test.rs index f69b52fa017..b879084c54f 100644 --- a/protocols/kad/src/behaviour/test.rs +++ b/protocols/kad/src/behaviour/test.rs @@ -255,7 +255,7 @@ fn query_iter() { fn run(rng: &mut impl Rng) { let num_total = rng.gen_range(2..20); - let mut config = Config::default(); + let mut config = Config::new(PROTOCOL_NAME); // Disabling periodic bootstrap and automatic bootstrap to prevent the bootstrap from triggering automatically. config.set_periodic_bootstrap_interval(None); config.set_automatic_bootstrap_throttle(None); From 245c2c8e87fb816ed72a9dc2e030179d12da1b2a Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Wed, 6 Mar 2024 12:07:22 +0200 Subject: [PATCH 23/25] fix: use current kad version --- Cargo.lock | 2 +- Cargo.toml | 2 +- protocols/kad/CHANGELOG.md | 5 +---- protocols/kad/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aef78659936..cd91bd4725a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2850,7 +2850,7 @@ dependencies = [ [[package]] name = "libp2p-kad" -version = "0.45.5" +version = "0.45.4" dependencies = [ "arrayvec", "async-std", diff --git a/Cargo.toml b/Cargo.toml index daf4bbbbb08..a5ccae37709 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,7 +85,7 @@ libp2p-floodsub = { version = "0.44.0", path = "protocols/floodsub" } libp2p-gossipsub = { version = "0.46.1", path = "protocols/gossipsub" } libp2p-identify = { version = "0.44.2", path = "protocols/identify" } libp2p-identity = { version = "0.2.8" } -libp2p-kad = { version = "0.45.5", path = "protocols/kad" } +libp2p-kad = { version = "0.45.4", path = "protocols/kad" } libp2p-mdns = { version = "0.45.1", path = "protocols/mdns" } libp2p-memory-connection-limits = { version = "0.2.0", path = "misc/memory-connection-limits" } libp2p-metrics = { version = "0.14.1", path = "misc/metrics" } diff --git a/protocols/kad/CHANGELOG.md b/protocols/kad/CHANGELOG.md index 6456c329ac6..0e781c18916 100644 --- a/protocols/kad/CHANGELOG.md +++ b/protocols/kad/CHANGELOG.md @@ -1,10 +1,7 @@ -## 0.45.5 +## 0.45.4 - Add periodic and automatic bootstrap. See [PR 4838](https://github.com/libp2p/rust-libp2p/pull/4838). - -## 0.45.4 - - Make it mandatory to provide protocol names when creating a `kad::Config`. Deprecate `kad::Config::default()`, replaced by `kad::Config::new(StreamProtocol)`. See [PR 5122](https://github.com/libp2p/rust-libp2p/pull/5122). diff --git a/protocols/kad/Cargo.toml b/protocols/kad/Cargo.toml index 39ece5c53ba..3bf74808f4a 100644 --- a/protocols/kad/Cargo.toml +++ b/protocols/kad/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-kad" edition = "2021" rust-version = { workspace = true } description = "Kademlia protocol for libp2p" -version = "0.45.5" +version = "0.45.4" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" From 5f8ef81f2301643e04c8bd0e17ad64ccd6c66587 Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Thu, 7 Mar 2024 13:34:26 +0200 Subject: [PATCH 24/25] reference automatic_bootstrap_throttle in the docs --- protocols/kad/src/behaviour.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index ad177539a32..153d337e88b 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -927,9 +927,9 @@ where /// > See [`Behaviour::add_address`]. /// /// > **Note**: Bootstrap does not require to be called manually. It is periodically - /// invoked at regular intervals based on the configured `periodic_bootstrap_interval` (see - /// [`Config::set_periodic_bootstrap_interval`] for details) and it is also automatically invoked - /// when a new peer is inserted in the routing table. + /// invoked at regular intervals based on the configured `periodic_bootstrap_interval` and + /// `automatic_bootstrap_throttle` (see [`Config::set_periodic_bootstrap_interval`] for details) + /// and it is also automatically invoked when a new peer is inserted in the routing table. /// These two config parameters are used to call [`Behaviour::bootstrap`] periodically and automatically /// to ensure a healthy routing table. pub fn bootstrap(&mut self) -> Result { From a58ed3705d43d463229c9eb35adbaf053217d60a Mon Sep 17 00:00:00 2001 From: PanGan21 Date: Thu, 7 Mar 2024 16:06:00 +0200 Subject: [PATCH 25/25] fix bootstrap doc to omit private config --- protocols/kad/src/behaviour.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 153d337e88b..31568b0f9a8 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -927,10 +927,10 @@ where /// > See [`Behaviour::add_address`]. /// /// > **Note**: Bootstrap does not require to be called manually. It is periodically - /// invoked at regular intervals based on the configured `periodic_bootstrap_interval` and - /// `automatic_bootstrap_throttle` (see [`Config::set_periodic_bootstrap_interval`] for details) - /// and it is also automatically invoked when a new peer is inserted in the routing table. - /// These two config parameters are used to call [`Behaviour::bootstrap`] periodically and automatically + /// invoked at regular intervals based on the configured `periodic_bootstrap_interval` (see + /// [`Config::set_periodic_bootstrap_interval`] for details) and it is also automatically invoked + /// when a new peer is inserted in the routing table. + /// This parameter is used to call [`Behaviour::bootstrap`] periodically and automatically /// to ensure a healthy routing table. pub fn bootstrap(&mut self) -> Result { let local_key = self.kbuckets.local_key().clone();