aboutsummaryrefslogtreecommitdiffstats
path: root/src/access
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-08 14:14:27 +0000
committertv <tv@krebsco.de>2026-11-08 15:26:08 +0100
commit075ee0be09be7ebce846043e5230ffb67a7f9a5c (patch)
treebb1ce4c6bdde52f7886d2fabbe535be486692004 /src/access
parent69a08327d24d08ba7a4e879116eeff60ac2a3874 (diff)
screen, signatures: the oldest waiting go, not the newest
Review finding 9: once 4096 signatures waited for their changes, new ones were dropped, and the waiting changes were cut at 10 000 keeping the oldest. A member could send junk once (signatures of changes that never come, changes no signature will vouch for) and fill both for good. Every honest signature arriving before its change was then lost, so under signatures required its changes waited for the actor's next signature: a stall for quiet actors. Both now keep the newest: the oldest go. A share per sender, so a member sending junk without pause can't push out what others sent either, is in future-work.md.
Diffstat (limited to 'src/access')
-rw-r--r--src/access/screen.rs13
1 files changed, 10 insertions, 3 deletions
diff --git a/src/access/screen.rs b/src/access/screen.rs
index a9450a6..4c640b6 100644
--- a/src/access/screen.rs
+++ b/src/access/screen.rs
@@ -39,7 +39,7 @@ use crate::document::replica::{Epoch, user_of};
use crate::document::signatures::Signatures;
use crate::document::{Change, ChangeHash, Document, ReadDoc};
-/// Changes waiting for their dependencies, kept at most.
+/// Changes waiting for their dependencies, kept at most: the oldest go.
const MAX_WAITING: usize = 10_000;
/// Refused changes kept for operators, at most: the oldest go.
pub(crate) const MAX_REFUSED: usize = 1_000;
@@ -187,6 +187,13 @@ impl Waiting {
pub(crate) fn len(&self) -> usize {
self.0.len()
}
+
+ /// The newest `MAX_WAITING`: the oldest go, so changes a peer sent that will never
+ /// be taken (whose dependencies or signatures never come) make way for others.
+ fn keep_newest(&mut self) {
+ let excess = self.0.len().saturating_sub(MAX_WAITING);
+ self.0.drain(..excess);
+ }
}
/// What `apply` did: how many changes it applied, and those it refused.
@@ -269,7 +276,7 @@ pub(crate) fn apply(
let (ready, unready) = in_order(doc, candidates);
waiting.0 = unready;
if ready.is_empty() {
- waiting.0.truncate(MAX_WAITING);
+ waiting.keep_newest();
return Ok(screened);
}
@@ -462,7 +469,7 @@ pub(crate) fn apply(
screened.roots.insert(root);
}
}
- waiting.0.truncate(MAX_WAITING);
+ waiting.keep_newest();
Ok(screened)
}