Skip to content

Commit 36c2f94

Browse files
feat(kad): improve automatic bootstrap
As discussed in the last maintainer call, some improvements are probably necessary for the automatic bootstrap feature (introduced by #4838). Indeed, like @drHuangMHT mentioned in #5341 and like @guillaumemichel has agreed, triggering a bootstrap every time an update happens inside the routing table consumes a lot more resources. The idea behind the automatic bootstrap feature it that, when a peer is starting, if a routing table update happens we probably don't want to wait for the periodic bootstrap to trigger and we want to trigger it right now. However, like @guillaumemichel said, this is something we want to do at startup or when a network connectivity problem happens, we don't want to do that all the time. This PR is a proposal to trigger automatically a bootstrap on routing table update but only when we have less that `K_VALUE` peers in it (meaning that we are starting up or something went wrong and the fact that a new peer is inserted is probably a sign that the network connectivity issue is resolved). I have also added a new triggering condition like mentioned in the maintainer call. When discovering a new listen address and if we have no connected peers, we trigger a bootstrap. This condition is based on our own experience at Stormshield : some peers were starting before the network interfaces were up, doing so, the automatic and periodic bootstrap failed, but when the network interfaces were finally up, we were waiting X minutes for the periodic bootstrap to actually trigger a bootstrap and join the p2p network. Pull-Request: #5474.
1 parent 23eb4f8 commit 36c2f94

File tree

3 files changed

+38
-11
lines changed

3 files changed

+38
-11
lines changed

protocols/kad/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
See [PR 5347](https://github.com/libp2p/rust-libp2p/pull/5347).
2424
- Correctly handle the `NoKnownPeers` error on automatic bootstrap.
2525
See [PR 5349](https://github.com/libp2p/rust-libp2p/pull/5349).
26+
- Improve automatic bootstrap triggering conditions:
27+
trigger when the routing table is updated and we have less that `K_VALUE` peers in it,
28+
trigger when a new listen address is discovered and we have no connected peers.
29+
See [PR 5474](https://github.com/libp2p/rust-libp2p/pull/5474).
2630

2731
## 0.45.3
2832

protocols/kad/src/behaviour.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
mod test;
2424

2525
use crate::addresses::Addresses;
26-
use crate::bootstrap;
2726
use crate::handler::{Handler, HandlerEvent, HandlerIn, RequestId};
2827
use crate::kbucket::{self, Distance, KBucketsTable, NodeStatus};
2928
use crate::protocol::{ConnectionType, KadPeer, ProtocolConfig};
@@ -33,6 +32,7 @@ use crate::record::{
3332
store::{self, RecordStore},
3433
ProviderRecord, Record,
3534
};
35+
use crate::{bootstrap, K_VALUE};
3636
use crate::{jobs::*, protocol};
3737
use fnv::FnvHashSet;
3838
use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr};
@@ -604,7 +604,8 @@ where
604604
};
605605
match entry.insert(addresses.clone(), status) {
606606
kbucket::InsertResult::Inserted => {
607-
self.bootstrap_status.on_new_peer_in_routing_table();
607+
self.bootstrap_on_low_peers();
608+
608609
self.queued_events.push_back(ToSwarm::GenerateEvent(
609610
Event::RoutingUpdated {
610611
peer: *peer,
@@ -1324,7 +1325,8 @@ where
13241325
let addresses = Addresses::new(a);
13251326
match entry.insert(addresses.clone(), new_status) {
13261327
kbucket::InsertResult::Inserted => {
1327-
self.bootstrap_status.on_new_peer_in_routing_table();
1328+
self.bootstrap_on_low_peers();
1329+
13281330
let event = Event::RoutingUpdated {
13291331
peer,
13301332
is_new_peer: true,
@@ -1375,6 +1377,20 @@ where
13751377
}
13761378
}
13771379

1380+
/// A new peer has been inserted in the routing table but we check if the routing
1381+
/// table is currently small (less that `K_VALUE` peers are present) and only
1382+
/// trigger a bootstrap in that case
1383+
fn bootstrap_on_low_peers(&mut self) {
1384+
if self
1385+
.kbuckets()
1386+
.map(|kbucket| kbucket.num_entries())
1387+
.sum::<usize>()
1388+
< K_VALUE.get()
1389+
{
1390+
self.bootstrap_status.trigger();
1391+
}
1392+
}
1393+
13781394
/// Handles a finished (i.e. successful) query.
13791395
fn query_finished(&mut self, q: Query) -> Option<Event> {
13801396
let query_id = q.id();
@@ -2613,6 +2629,12 @@ where
26132629
}
26142630
FromSwarm::DialFailure(dial_failure) => self.on_dial_failure(dial_failure),
26152631
FromSwarm::AddressChange(address_change) => self.on_address_change(address_change),
2632+
FromSwarm::NewListenAddr(_) if self.connected_peers.is_empty() => {
2633+
// A new listen addr was just discovered and we have no connected peers,
2634+
// it can mean that our network interfaces were not up but they are now
2635+
// so it might be a good idea to trigger a bootstrap.
2636+
self.bootstrap_status.trigger();
2637+
}
26162638
_ => {}
26172639
}
26182640
}

protocols/kad/src/bootstrap.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ impl Status {
4444
}
4545
}
4646

47-
pub(crate) fn on_new_peer_in_routing_table(&mut self) {
47+
/// Trigger a bootstrap now or after the configured `automatic_throttle` if configured.
48+
pub(crate) fn trigger(&mut self) {
4849
// Registering `self.throttle_timer` means scheduling a bootstrap.
4950
// A bootstrap will be triggered when `self.throttle_timer` finishes.
5051
// A `throttle_timer` is useful to not trigger a batch of bootstraps when a
@@ -201,7 +202,7 @@ mod tests {
201202
"bootstrap to not be triggered immediately because periodic bootstrap is in ~1s"
202203
);
203204

204-
status.on_new_peer_in_routing_table(); // Connected to a new peer though!
205+
status.trigger(); // Connected to a new peer though!
205206
assert!(
206207
status.next().now_or_never().is_some(),
207208
"bootstrap to be triggered immediately because we connected to a new peer"
@@ -226,7 +227,7 @@ mod tests {
226227
"bootstrap to not be triggered immediately because periodic bootstrap is in ~1s"
227228
);
228229

229-
status.on_new_peer_in_routing_table(); // Connected to a new peer though!
230+
status.trigger(); // Connected to a new peer though!
230231
assert!(
231232
status.next().now_or_never().is_none(),
232233
"bootstrap to not be triggered immediately because throttle is 5ms"
@@ -247,7 +248,7 @@ mod tests {
247248
// User manually triggered a bootstrap
248249
do_bootstrap(&mut status);
249250

250-
status.on_new_peer_in_routing_table(); // Connected to a new peer though!
251+
status.trigger(); // Connected to a new peer though!
251252

252253
assert!(
253254
status.next().now_or_never().is_some(),
@@ -260,7 +261,7 @@ mod tests {
260261
) {
261262
let mut status = Status::new(Some(MS_100), Some(MS_5));
262263

263-
status.on_new_peer_in_routing_table();
264+
status.trigger();
264265

265266
let start = Instant::now();
266267
await_and_do_bootstrap(&mut status).await;
@@ -280,7 +281,7 @@ mod tests {
280281
) {
281282
let mut status = Status::new(None, Some(Duration::ZERO));
282283

283-
status.on_new_peer_in_routing_table();
284+
status.trigger();
284285

285286
status.next().await;
286287
}
@@ -304,10 +305,10 @@ mod tests {
304305
) {
305306
let mut status = Status::new(None, Some(MS_100));
306307

307-
status.on_new_peer_in_routing_table();
308+
status.trigger();
308309
for _ in 0..10 {
309310
Delay::new(MS_100 / 2).await;
310-
status.on_new_peer_in_routing_table(); // should reset throttle_timer
311+
status.trigger(); // should reset throttle_timer
311312
}
312313
assert!(
313314
status.next().now_or_never().is_none(),

0 commit comments

Comments
 (0)