summaryrefslogtreecommitdiffstats
path: root/src/swarm/stats.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/swarm/stats.rs')
-rw-r--r--src/swarm/stats.rs56
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");