diff options
Diffstat (limited to 'src/swarm/stats.rs')
| -rw-r--r-- | src/swarm/stats.rs | 56 |
1 files changed, 39 insertions, 17 deletions
diff --git a/src/swarm/stats.rs b/src/swarm/stats.rs index 786734a..eb34276 100644 --- a/src/swarm/stats.rs +++ b/src/swarm/stats.rs @@ -8,10 +8,11 @@ use std::time::Duration; use tokio::time::Instant; use tracing::{debug, warn}; +use crate::document::replica::Epoch; use crate::state::AppState; /// Saving takes time proportional to the document; we spend at most about 1% of it on -/// measuring, and measure at least this long apart. +/// measuring, and measure at least this long apart, and only after a change. const SAVED_SIZE_MIN_INTERVAL: Duration = Duration::from_secs(60); const SAVED_SIZE_DUTY: u32 = 100; @@ -29,6 +30,8 @@ struct Inner { stale_hello_replies: u64, saved_size: Option<SavedSize>, measuring: bool, + /// The document version (`AppState::change_tx`) and epoch the saved size is of. + measured_version: Option<(u64, Epoch)>, propagation: Propagation, } @@ -157,10 +160,12 @@ impl NodeStats { self.lock().saved_size } - /// Whether a measurement is due; if so, it counts as started. - fn start_measuring(&self, now: Instant) -> bool { + /// Whether a measurement of the document at `version` is due; if so, it counts as + /// started. + fn start_measuring(&self, now: Instant, version: (u64, Epoch)) -> bool { let mut inner = self.lock(); let due = !inner.measuring + && inner.measured_version != Some(version) && inner.saved_size.is_none_or(|last| { now >= last.at + SAVED_SIZE_MIN_INTERVAL.max(last.took * SAVED_SIZE_DUTY) }); @@ -170,20 +175,24 @@ impl NodeStats { due } - /// The document was saved for another reason (an export): a measurement for free. - pub(crate) fn saved_size_measured(&self, bytes: u64, took: Duration) { - self.lock().saved_size = Some(SavedSize { + /// The document at `version` was saved for another reason (an export): a + /// measurement for free. + pub(crate) fn saved_size_measured(&self, bytes: u64, took: Duration, version: (u64, Epoch)) { + let mut inner = self.lock(); + inner.saved_size = Some(SavedSize { bytes, at: Instant::now(), took, }); + inner.measured_version = Some(version); } - fn finish_measuring(&self, measured: Option<SavedSize>) { + fn finish_measuring(&self, measured: Option<(SavedSize, (u64, Epoch))>) { let mut inner = self.lock(); inner.measuring = false; - if let Some(measured) = measured { + if let Some((measured, version)) = measured { inner.saved_size = Some(measured); + inner.measured_version = Some(version); } } @@ -200,14 +209,24 @@ impl NodeStats { } } +/// The document's version for measuring: bumped by every write and applied sync +/// message (`AppState::notify_store_changed`), together with the epoch (a switch +/// replaces the document). +pub(crate) fn document_version(state: &AppState) -> (u64, Epoch) { + (*state.change_tx.borrow(), state.local_epoch()) +} + /// Measures the document's saved size in the background when due: the first time, then -/// at least a minute apart and spending no more than about 1% of the time on it (a -/// document that takes 2 s to copy and save is measured every 200 s). Called by the -/// peer worker every few seconds. +/// only after the document changed, at least a minute apart and spending no more than +/// about 1% of the time on it (a document that takes 2 s to copy and save is measured +/// at most every 200 s). An idle swarm measures once. Called by the peer worker every +/// few seconds. /// /// The copy holds the read lock (writes wait for it); the save runs without any lock. pub(crate) fn measure_saved_size(state: &AppState) { - if !state.stats.start_measuring(Instant::now()) { + // Taken before the copy: a change during it makes another measurement due. + let version = document_version(state); + if !state.stats.start_measuring(Instant::now(), version) { return; } let state = state.clone(); @@ -228,11 +247,14 @@ pub(crate) fn measure_saved_size(state: &AppState) { took_ms = took.as_millis() as u64, "Measured the saved document size" ); - Some(SavedSize { - bytes, - at: Instant::now(), - took, - }) + Some(( + SavedSize { + bytes, + at: Instant::now(), + took, + }, + version, + )) } Err(error) => { warn!(error = %error, "Measuring the saved document size failed"); |
