aboutsummaryrefslogtreecommitdiffstats
path: root/src/document
diff options
context:
space:
mode:
Diffstat (limited to 'src/document')
-rw-r--r--src/document/changes.rs25
-rw-r--r--src/document/sync.rs36
2 files changed, 52 insertions, 9 deletions
diff --git a/src/document/changes.rs b/src/document/changes.rs
index 0409c50..19d4db6 100644
--- a/src/document/changes.rs
+++ b/src/document/changes.rs
@@ -28,10 +28,13 @@ pub(crate) fn commit_times(doc: &Document, since: &[ChangeHash]) -> Vec<i64> {
}
/// Who wrote the changes after `since`: the authors their writers set (a node's ID,
-/// `replica::authored`), each once; and whether any change has none (a snapshot, or a
-/// change of a node that set none).
-pub(crate) fn authors_since(doc: &Document, since: &[ChangeHash]) -> (Vec<String>, bool) {
- let mut authors: Vec<String> = Vec::new();
+/// `replica::authored`), each once, with the hashes of their changes; and whether any
+/// change has none (a snapshot, or a change of a node that set none).
+pub(crate) fn authors_since(
+ doc: &Document,
+ since: &[ChangeHash],
+) -> (Vec<(String, Vec<ChangeHash>)>, bool) {
+ let mut authors: Vec<(String, Vec<ChangeHash>)> = Vec::new();
let mut unknown = false;
for change in doc.get_changes_meta(since) {
match change
@@ -39,10 +42,10 @@ pub(crate) fn authors_since(doc: &Document, since: &[ChangeHash]) -> (Vec<String
.as_ref()
.and_then(|author| std::str::from_utf8(author.as_bytes()).ok())
{
- Some(author) if !authors.iter().any(|known| known == author) => {
- authors.push(author.to_owned());
- }
- Some(_) => {}
+ Some(author) => match authors.iter_mut().find(|(known, _)| known == author) {
+ Some((_, hashes)) => hashes.push(change.hash),
+ None => authors.push((author.to_owned(), vec![change.hash])),
+ },
None => unknown = true,
}
}
@@ -221,7 +224,11 @@ mod tests {
merged.merge(&mut b).unwrap();
let (mut authors, unknown) = authors_since(&merged, &since);
authors.sort();
- assert_eq!(authors, ["node-a", "node-b"]);
+ let counts: Vec<(&str, usize)> = authors
+ .iter()
+ .map(|(author, hashes)| (author.as_str(), hashes.len()))
+ .collect();
+ assert_eq!(counts, [("node-a", 2), ("node-b", 1)]);
assert!(!unknown);
// A change no node authored (a snapshot's, say).
diff --git a/src/document/sync.rs b/src/document/sync.rs
index 1f61f70..befb8e5 100644
--- a/src/document/sync.rs
+++ b/src/document/sync.rs
@@ -476,6 +476,13 @@ impl SyncSession {
known.len()
}
+ /// The peer has `changes`, or will without us (another parent in their writer's
+ /// tree brings them, `swarm::tree::forward`): counted as sent, they don't go to it,
+ /// unless it asks for them (`resend_if_needed`).
+ pub(crate) fn known_to_peer(&mut self, changes: &[crate::document::ChangeHash]) {
+ self.state.sent_hashes.extend(changes.iter().copied());
+ }
+
/// What the sync state knows of the peer (`PeerView`).
fn peer_view(&self) -> PeerView {
PeerView {
@@ -1238,6 +1245,35 @@ mod tests {
assert_eq!(b.known_beyond(&theirs, &theirs.get_heads()), 0);
}
+ /// Changes the peer gets from elsewhere (another parent in their writer's tree)
+ /// don't go to it, but do if it asks.
+ #[test]
+ fn changes_known_to_the_peer_go_only_when_asked() {
+ use crate::document::testing::change;
+ use automerge::{ROOT, transaction::Transactable};
+ let (mut ours, mut theirs) = (Document::new(), Document::new());
+ change(&mut ours, |doc| doc.put(ROOT, "early", 1).unwrap());
+ let (mut a, mut b) = in_step(&mut ours, &mut theirs);
+ let (now, bytes) = (Instant::now(), SyncBytes::default());
+
+ change(&mut ours, |doc| doc.put(ROOT, "elsewhere", 2).unwrap());
+ a.known_to_peer(&ours.get_heads());
+ let message = a.generate(&ours, now, bytes).expect("our heads moved");
+ a.send_succeeded(a.id());
+ let (carried, _) = decode(&message).unwrap().sizes();
+ assert_eq!(carried, 0, "not sent: the peer gets it from elsewhere");
+
+ // It didn't: it asks, and gets it.
+ b.receive(&mut theirs, decode(&message).unwrap()).unwrap();
+ let asking = b.generate(&theirs, now, bytes).expect("it asks");
+ b.send_succeeded(b.id());
+ a.receive(&mut ours, decode(&asking).unwrap()).unwrap();
+ a.resend_if_needed(&ours, "b");
+ let answer = a.generate(&ours, now, bytes).expect("answered");
+ b.receive(&mut theirs, decode(&answer).unwrap()).unwrap();
+ assert_eq!(theirs.get_heads(), ours.get_heads());
+ }
+
/// Our transport refused a message taken as delivered: the peer's heads are as
/// before, so the next message carries the change again.
#[test]