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 <oxarbitrage@gmail.com>
This commit is contained in:
Marek 2021-09-07 01:33:31 +02:00 committed by GitHub
parent 65e308d2e1
commit 1c4ac18df2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 62 additions and 5 deletions

View File

@ -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
}
}

View File

@ -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<Semaphore>,
@ -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());
}