summaryrefslogtreecommitdiffstats
path: root/src/transport/mod.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-23 22:25:41 +0200
committertv <tv@krebsco.de>2026-10-23 22:25:41 +0200
commitde560ee34ba0297194cae6ac9a1966e3ef614945 (patch)
tree58dbdef0c2be5b747991c712c126271bc8ef692a /src/transport/mod.rs
parentd8f0933b5c88634ee7e83b435241084baa6abf5b (diff)
bump number of heads in keepalive messages
Instead of arbitrarily limiting the number to 16, allow filling keepalive messages as long as they don't cause mycelium to chunk it.
Diffstat (limited to 'src/transport/mod.rs')
-rw-r--r--src/transport/mod.rs49
1 files changed, 38 insertions, 11 deletions
diff --git a/src/transport/mod.rs b/src/transport/mod.rs
index aee3ee3..d7e7135 100644
--- a/src/transport/mod.rs
+++ b/src/transport/mod.rs
@@ -12,14 +12,16 @@ pub use crate::document::replica::Epoch;
pub mod firewall;
pub mod mycelium;
-/// Most heads listed in a keepalive; more are sent as a digest. 16 heads are about
-/// 520 bytes, well within one Mycelium chunk (1300 bytes).
-pub const MAX_LISTED_HEADS: usize = 16;
+/// Most heads listed in a keepalive; more are sent as a digest. A head takes 34 bytes on
+/// the wire, so 32 of them keep a keepalive within one Mycelium chunk (1300 bytes; see
+/// the size test in `mycelium`). There are rarely more heads than nodes that wrote
+/// concurrently since the last merging write.
+pub const MAX_LISTED_HEADS: usize = 32;
/// A document's heads, compactly: the heads themselves while there are few, otherwise a
-/// digest of them. Either way, two summaries are equal exactly when the heads are (the
-/// number of heads decides the form, so equal heads always take the same one).
-#[derive(Debug, Clone, PartialEq, Eq)]
+/// digest of them. Two summaries are equal exactly when the heads are, whatever form
+/// each took (nodes with different limits still agree).
+#[derive(Debug, Clone)]
pub enum HeadsSummary {
/// Sorted.
Heads(Vec<ChangeHash>),
@@ -31,13 +33,18 @@ impl HeadsSummary {
pub fn of(mut heads: Vec<ChangeHash>) -> Self {
heads.sort_unstable();
if heads.len() <= MAX_LISTED_HEADS {
- return Self::Heads(heads);
+ Self::Heads(heads)
+ } else {
+ Self::Digest(digest_of(&heads))
}
- let mut hasher = Sha256::new();
- for head in &heads {
- hasher.update(head.0);
+ }
+
+ /// The digest of the heads, computed if they are listed.
+ pub fn digest(&self) -> [u8; 32] {
+ match self {
+ Self::Heads(heads) => digest_of(heads),
+ Self::Digest(digest) => *digest,
}
- Self::Digest(hasher.finalize().into())
}
/// The heads, if listed.
@@ -49,6 +56,26 @@ impl HeadsSummary {
}
}
+impl PartialEq for HeadsSummary {
+ fn eq(&self, other: &Self) -> bool {
+ match (self, other) {
+ (Self::Heads(a), Self::Heads(b)) => a == b,
+ _ => self.digest() == other.digest(),
+ }
+ }
+}
+
+impl Eq for HeadsSummary {}
+
+/// SHA-256 over sorted heads.
+fn digest_of(sorted_heads: &[ChangeHash]) -> [u8; 32] {
+ let mut hasher = Sha256::new();
+ for head in sorted_heads {
+ hasher.update(head.0);
+ }
+ hasher.finalize().into()
+}
+
/// PING and PONG. Addressed to one peer: `syncs_with_you` is about the recipient.
#[derive(Debug, Clone)]
pub struct Keepalive {