2021-08-19 16:16:16 -07:00
|
|
|
//! A channel which holds a list of recent syncer response lengths.
|
|
|
|
|
|
|
|
use tokio::sync::watch;
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
|
|
|
|
|
|
|
/// A helper type which holds a list of recent syncer response lengths.
|
|
|
|
/// These sync lengths can be used to work out if Zebra has reached the end of the chain.
|
|
|
|
///
|
|
|
|
/// New lengths are added to the front of the list.
|
|
|
|
/// Old lengths are dropped if the list is longer than `MAX_RECENT_LENGTHS`.
|
|
|
|
//
|
|
|
|
// TODO: disable the mempool if obtain or extend tips return errors?
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct RecentSyncLengths {
|
|
|
|
/// A sender for an array of recent sync lengths.
|
|
|
|
sender: watch::Sender<Vec<usize>>,
|
|
|
|
|
|
|
|
/// A local copy of the contents of `sender`.
|
|
|
|
// TODO: Replace with calls to `watch::Sender::borrow` once Tokio is updated to 1.0.0 (#2573)
|
|
|
|
recent_lengths: Vec<usize>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RecentSyncLengths {
|
|
|
|
/// The maximum number of lengths sent by `RecentSyncLengths`.
|
|
|
|
///
|
|
|
|
/// Older lengths are dropped.
|
|
|
|
///
|
|
|
|
/// This length was chosen as a tradeoff between:
|
|
|
|
/// * clearing temporary errors and temporary syncs quickly
|
|
|
|
/// * distinguishing between temporary and sustained syncs/errors
|
|
|
|
/// * activating the syncer shortly after reaching the chain tip
|
2022-12-12 15:20:03 -08:00
|
|
|
pub const MAX_RECENT_LENGTHS: usize = 3;
|
2021-08-19 16:16:16 -07:00
|
|
|
|
|
|
|
/// Create a new instance of [`RecentSyncLengths`]
|
|
|
|
/// and a [`watch::Receiver`] endpoint for receiving recent sync lengths.
|
|
|
|
pub fn new() -> (Self, watch::Receiver<Vec<usize>>) {
|
|
|
|
let (sender, receiver) = watch::channel(Vec::new());
|
|
|
|
|
|
|
|
(
|
|
|
|
RecentSyncLengths {
|
|
|
|
sender,
|
|
|
|
recent_lengths: Vec::with_capacity(Self::MAX_RECENT_LENGTHS),
|
|
|
|
},
|
|
|
|
receiver,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We skip the genesis block download, because it just uses the genesis hash directly,
|
|
|
|
// rather than asking peers for the next blocks in the chain.
|
|
|
|
// (And if genesis downloads kept failing, we could accidentally activate the mempool.)
|
|
|
|
|
2022-06-14 20:57:19 -07:00
|
|
|
/// Insert a sync length from [`ChainSync::obtain_tips`] at the front of the
|
|
|
|
/// list.
|
|
|
|
///
|
|
|
|
/// [`ChainSync::obtain_tips`]: super::ChainSync::obtain_tips
|
2021-08-19 16:16:16 -07:00
|
|
|
#[instrument(skip(self), fields(self.recent_lengths))]
|
|
|
|
pub fn push_obtain_tips_length(&mut self, sync_length: usize) {
|
|
|
|
// currently, we treat lengths from obtain and extend tips exactly the same,
|
|
|
|
// but we might want to ignore some obtain tips lengths
|
|
|
|
//
|
|
|
|
// See "Response Lengths During Sync -> Details" in:
|
|
|
|
// https://github.com/ZcashFoundation/zebra/issues/2592#issuecomment-897304684
|
|
|
|
self.update(sync_length)
|
|
|
|
}
|
|
|
|
|
2022-06-14 20:57:19 -07:00
|
|
|
/// Insert a sync length from [`ChainSync::extend_tips`] at the front of the
|
|
|
|
/// list.
|
|
|
|
///
|
|
|
|
/// [`ChainSync::extend_tips`]: super::ChainSync::extend_tips
|
2021-08-19 16:16:16 -07:00
|
|
|
#[instrument(skip(self), fields(self.recent_lengths))]
|
|
|
|
pub fn push_extend_tips_length(&mut self, sync_length: usize) {
|
|
|
|
self.update(sync_length)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sends a list update to listeners.
|
|
|
|
///
|
|
|
|
/// Prunes recent lengths, if the list is longer than `MAX_RECENT_LENGTHS`.
|
|
|
|
fn update(&mut self, sync_length: usize) {
|
|
|
|
self.recent_lengths.insert(0, sync_length);
|
|
|
|
|
|
|
|
self.recent_lengths.truncate(Self::MAX_RECENT_LENGTHS);
|
|
|
|
|
2022-01-28 09:24:53 -08:00
|
|
|
debug!(
|
2021-08-19 16:16:16 -07:00
|
|
|
recent_lengths = ?self.recent_lengths,
|
|
|
|
"sending recent sync lengths"
|
|
|
|
);
|
|
|
|
|
|
|
|
// ignore dropped receivers
|
|
|
|
let _ = self.sender.send(self.recent_lengths.clone());
|
|
|
|
}
|
|
|
|
}
|