Skip to content

Commit

Permalink
feat(net): Cache a list of useful peers on disk (#6739)
Browse files Browse the repository at this point in the history
* Rewrite some state cache docs to clarify

* Add a zebra_network::Config.cache_dir for peer address caches

* Add new config test files and fix config test failure message

* Create some zebra-chain and zebra-network convenience functions

* Add methods for reading and writing the peer address cache

* Add cached disk peers to the initial peers list

* Add metrics and logging for loading and storing the peer cache

* Replace log of useless redacted peer IP addresses

* Limit the peer cache minimum and maximum size, don't write empty caches

* Add a cacheable_peers() method to the address book

* Add a peer disk cache updater task to the peer set tasks

* Document that the peer cache is shared by multiple instances unless configured otherwise

* Disable peer cache read/write in disconnected tests

* Make initial peer cache updater sleep shorter for tests

* Add unit tests for reading and writing the peer cache

* Update the task list in the start command docs

* Modify the existing persistent acceptance test to check for peer caches

* Update the peer cache directory when writing test configs

* Add a CacheDir type so the default config can be enabled, but tests can disable it

* Update tests to use the CacheDir config type

* Rename some CacheDir internals

* Add config file test cases for each kind of CacheDir config

* Panic if the config contains invalid socket addresses, rather than continuing

* Add a network directory to state cache directory contents tests

* Add new network.cache_dir config to the config parsing tests
  • Loading branch information
teor2345 authored Jun 6, 2023
1 parent f197dfb commit 04e96c2
Show file tree
Hide file tree
Showing 22 changed files with 868 additions and 48 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5847,6 +5847,7 @@ dependencies = [
"byteorder",
"bytes",
"chrono",
"dirs",
"futures",
"hex",
"howudoin",
Expand All @@ -5863,6 +5864,7 @@ dependencies = [
"regex",
"serde",
"static_assertions",
"tempfile",
"thiserror",
"tokio",
"tokio-stream",
Expand Down
5 changes: 5 additions & 0 deletions zebra-chain/src/parameters/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ impl Network {
Network::Testnet => "test".to_string(),
}
}

/// Return the lowercase network name.
pub fn lowercase_name(&self) -> String {
self.to_string().to_ascii_lowercase()
}
}

impl FromStr for Network {
Expand Down
4 changes: 3 additions & 1 deletion zebra-network/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ bitflags = "2.2.1"
byteorder = "1.4.3"
bytes = "1.4.0"
chrono = { version = "0.4.26", default-features = false, features = ["clock", "std"] }
dirs = "5.0.1"
hex = "0.4.3"
humantime-serde = "1.1.1"
indexmap = { version = "1.9.3", features = ["serde"] }
Expand All @@ -37,10 +38,11 @@ rand = { version = "0.8.5", package = "rand" }
rayon = "1.7.0"
regex = "1.8.4"
serde = { version = "1.0.163", features = ["serde_derive"] }
tempfile = "3.5.0"
thiserror = "1.0.40"

futures = "0.3.28"
tokio = { version = "1.28.2", features = ["net", "time", "tracing", "macros", "rt-multi-thread"] }
tokio = { version = "1.28.2", features = ["fs", "net", "time", "tracing", "macros", "rt-multi-thread"] }
tokio-stream = { version = "0.1.14", features = ["sync", "time"] }
tokio-util = { version = "0.7.8", features = ["codec"] }
tower = { version = "0.4.13", features = ["retry", "discover", "load", "load-shed", "timeout", "util", "buffer"] }
Expand Down
36 changes: 32 additions & 4 deletions zebra-network/src/address_book.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ impl AddressBook {
self.local_listener
}

/// Get the contents of `self` in random order with sanitized timestamps.
/// Get the active addresses in `self` in random order with sanitized timestamps,
/// including our local listener address.
pub fn sanitized(&self, now: chrono::DateTime<Utc>) -> Vec<MetaAddr> {
use rand::seq::SliceRandom;
let _guard = self.span.enter();
Expand All @@ -254,20 +255,47 @@ impl AddressBook {
peers.insert(local_listener.addr, local_listener);

// Then sanitize and shuffle
let mut peers = peers
let mut peers: Vec<MetaAddr> = peers
.descending_values()
.filter_map(|meta_addr| meta_addr.sanitize(self.network))
// Security: remove peers that:
// # Security
//
// Remove peers that:
// - last responded more than three hours ago, or
// - haven't responded yet but were reported last seen more than three hours ago
//
// This prevents Zebra from gossiping nodes that are likely unreachable. Gossiping such
// nodes impacts the network health, because connection attempts end up being wasted on
// peers that are less likely to respond.
.filter(|addr| addr.is_active_for_gossip(now))
.collect::<Vec<_>>();
.collect();

peers.shuffle(&mut rand::thread_rng());

peers
}

/// Get the active addresses in `self`, in preferred caching order,
/// excluding our local listener address.
pub fn cacheable(&self, now: chrono::DateTime<Utc>) -> Vec<MetaAddr> {
let _guard = self.span.enter();

let peers = self.by_addr.clone();

// Get peers in preferred order, then keep the recently active ones
peers
.descending_values()
// # Security
//
// Remove peers that:
// - last responded more than three hours ago, or
// - haven't responded yet but were reported last seen more than three hours ago
//
// This prevents Zebra from caching nodes that are likely unreachable,
// which improves startup time and reliability.
.filter(|addr| addr.is_active_for_gossip(now))
.cloned()
.collect()
}

/// Look up `addr` in the address book, and return its [`MetaAddr`].
Expand Down
Loading

0 comments on commit 04e96c2

Please sign in to comment.