From 1c4ac18df2af3f2928488fb55ceda0eae45f8926 Mon Sep 17 00:00:00 2001 From: Marek Date: Tue, 7 Sep 2021 01:33:31 +0200 Subject: [PATCH] Decide if Zebra is at the chain tip (#2722) * Decide if Zebra is at the chain tip * Avoid division by zero * Try increasing EVENT_TIMEOUT * Increase MAX_TEST_EXECUTION * Implement basic tests * Resolve Clippy's erorrs * change doc comments to normal Co-authored-by: Alfredo Garcia --- zebrad/src/components/sync/status.rs | 29 +++++++++++++++-- zebrad/src/components/sync/status/tests.rs | 38 ++++++++++++++++++++-- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/zebrad/src/components/sync/status.rs b/zebrad/src/components/sync/status.rs index 64db08a6..eac751b1 100644 --- a/zebrad/src/components/sync/status.rs +++ b/zebrad/src/components/sync/status.rs @@ -17,6 +17,13 @@ pub struct SyncStatus { } impl SyncStatus { + /// The threshold that determines if the synchronization is at the chain + /// tip. + /// + /// This is based on the fact that sync lengths are around 2-20 blocks long + /// once Zebra reaches the tip. + const MIN_DIST_FROM_TIP: usize = 20; + /// Create an instance of [`SyncStatus`]. /// /// The status is determined based on the latest counts of synchronized blocks, observed @@ -41,9 +48,25 @@ impl SyncStatus { /// Check if the synchronization is likely close to the chain tip. pub fn is_close_to_tip(&self) -> bool { - let _sync_lengths = self.latest_sync_length.borrow(); + let sync_lengths = self.latest_sync_length.borrow(); - // TODO: Determine if the synchronization is actually close to the tip (#2592). - true + // Return early if sync_lengths is empty. + if sync_lengths.is_empty() { + return false; + } + + // Compute the sum of the `sync_lengths`. + // The sum is computed by saturating addition in order to avoid overflowing. + let sum = sync_lengths + .iter() + .fold(0usize, |sum, rhs| sum.saturating_add(*rhs)); + + // Compute the average sync length. + // This value effectively represents a simple moving average. + let avg = sum / sync_lengths.len(); + + // The synchronization process is close to the chain tip once the + // average sync length falls below the threshold. + avg < Self::MIN_DIST_FROM_TIP } } diff --git a/zebrad/src/components/sync/status/tests.rs b/zebrad/src/components/sync/status/tests.rs index 203b6798..978043ea 100644 --- a/zebrad/src/components/sync/status/tests.rs +++ b/zebrad/src/components/sync/status/tests.rs @@ -12,7 +12,7 @@ const DEFAULT_ASYNC_SYNCHRONIZED_TASKS_PROPTEST_CASES: u32 = 32; /// The maximum time one test instance should run. /// /// If the test exceeds this time it is considered to have failed. -const MAX_TEST_EXECUTION: Duration = Duration::from_secs(1); +const MAX_TEST_EXECUTION: Duration = Duration::from_secs(10); /// The maximum time to wait for an event to be received. /// @@ -120,7 +120,7 @@ proptest! { /// looping repeatedly while [`SyncStatus`] reports that it is close to the chain tip. /// 2. Waits until [`SyncStatus`] reports that it is close to the chain tip. /// 3. Notifies the main task that it awoke, i.e., that the [`SyncStatus`] has finished - /// wating until it was close to the chain tip. + /// waiting until it was close to the chain tip. async fn wait_task( mut status: SyncStatus, update_events: Arc, @@ -138,3 +138,37 @@ proptest! { } } } + +/// Test if totally empty sync lengths array is not near tip. +#[test] +fn empty_sync_lengths() { + let (status, _recent_sync_lengths) = SyncStatus::new(); + + assert!(!status.is_close_to_tip()); +} + +/// Test if sync lengths array with all zeroes is near tip. +#[test] +fn zero_sync_lengths() { + let (status, mut recent_sync_lengths) = SyncStatus::new(); + + for _ in 0..RecentSyncLengths::MAX_RECENT_LENGTHS { + recent_sync_lengths.push_extend_tips_length(0); + } + + assert!(status.is_close_to_tip()); +} + +/// Test if sync lengths array with high values is not near tip. +#[test] +fn high_sync_lengths() { + let (status, mut recent_sync_lengths) = SyncStatus::new(); + + // The value 500 is based on the fact that sync lengths are around 500 + // blocks long when Zebra is syncing. + for _ in 0..RecentSyncLengths::MAX_RECENT_LENGTHS { + recent_sync_lengths.push_extend_tips_length(500); + } + + assert!(!status.is_close_to_tip()); +}