zebra_network/
peer_cache_updater.rs

1//! An async task that regularly updates the peer cache on disk from the current address book.
2
3use std::{
4    io,
5    sync::{Arc, Mutex},
6};
7
8use chrono::Utc;
9use tokio::time::sleep;
10
11use crate::{
12    constants::{DNS_LOOKUP_TIMEOUT, PEER_DISK_CACHE_UPDATE_INTERVAL},
13    meta_addr::MetaAddr,
14    AddressBook, BoxError, Config,
15};
16
17/// An ongoing task that regularly caches the current `address_book` to disk, based on `config`.
18#[instrument(skip(config, address_book))]
19pub async fn peer_cache_updater(
20    config: Config,
21    address_book: Arc<Mutex<AddressBook>>,
22) -> Result<(), BoxError> {
23    // Wait until we've queried DNS and (hopefully) sent peers to the address book.
24    // Ideally we'd wait for at least one peer crawl, but that makes tests very slow.
25    //
26    // TODO: turn the initial sleep time into a parameter of this function,
27    //       and allow it to be set in tests
28    sleep(DNS_LOOKUP_TIMEOUT * 4).await;
29
30    loop {
31        // Ignore errors because updating the cache is optional.
32        // Errors are already logged by the functions we're calling.
33        let _ = update_peer_cache_once(&config, &address_book).await;
34
35        sleep(PEER_DISK_CACHE_UPDATE_INTERVAL).await;
36    }
37}
38
39/// Caches peers from the current `address_book` to disk, based on `config`.
40pub async fn update_peer_cache_once(
41    config: &Config,
42    address_book: &Arc<Mutex<AddressBook>>,
43) -> io::Result<()> {
44    let peer_list = cacheable_peers(address_book)
45        .iter()
46        .map(|meta_addr| meta_addr.addr)
47        .collect();
48
49    config.update_peer_cache(peer_list).await
50}
51
52/// Returns a list of cacheable peers, blocking for as short a time as possible.
53fn cacheable_peers(address_book: &Arc<Mutex<AddressBook>>) -> Vec<MetaAddr> {
54    // TODO: use spawn_blocking() here, if needed to handle address book mutex load
55    let now = Utc::now();
56
57    // # Concurrency
58    //
59    // We return from this function immediately to make sure the address book is unlocked.
60    address_book
61        .lock()
62        .expect("unexpected panic in previous thread while accessing the address book")
63        .cacheable(now)
64}