Module automerge::sync

source ·
Expand description

Sync Protocol

The sync protocol is based on this paper: https://arxiv.org/abs/2012.00472, it assumes a reliable in-order stream between two peers who are synchronizing a document.

Each peer maintains a State for each peer they are synchronizing with. This state tracks things like what the heads of the other peer are and whether there are in-flight messages. Anything which implements SyncDoc can take part in the sync protocol. The flow goes something like this:

  • The initiating peer creates an empty State and then calls SyncDoc::generate_sync_message to generate new sync message and sends it to the receiving peer.
  • The receiving peer receives a message from the initiator, creates a new State, and calls SyncDoc::receive_sync_message on it’s view of the document
  • The receiving peer then calls SyncDoc::generate_sync_message to generate a new sync message and send it back to the initiator
  • From this point on each peer operates in a loop, receiving a sync message from the other peer and then generating a new message to send back.

Example

use automerge::{transaction::Transactable, sync::{self, SyncDoc}, ReadDoc};
// Create a document on peer1
let mut peer1 = automerge::AutoCommit::new();
peer1.put(automerge::ROOT, "key", "value")?;

// Create a state to track our sync with peer2
let mut peer1_state = sync::State::new();
// Generate the initial message to send to peer2, unwrap for brevity
let message1to2 = peer1.sync().generate_sync_message(&mut peer1_state).unwrap();

// We receive the message on peer2. We don't have a document at all yet
// so we create one
let mut peer2 = automerge::AutoCommit::new();
// We don't have a state for peer1 (it's a new connection), so we create one
let mut peer2_state = sync::State::new();
// Now receive the message from peer 1
peer2.sync().receive_sync_message(&mut peer2_state, message1to2)?;

// Now we loop, sending messages from one to two and two to one until
// neither has anything new to send

loop {
    let two_to_one = peer2.sync().generate_sync_message(&mut peer2_state);
    if let Some(message) = two_to_one.as_ref() {
        println!("two to one");
        peer1.sync().receive_sync_message(&mut peer1_state, message.clone())?;
    }
    let one_to_two = peer1.sync().generate_sync_message(&mut peer1_state);
    if let Some(message) = one_to_two.as_ref() {
        println!("one to two");
        peer2.sync().receive_sync_message(&mut peer2_state, message.clone())?;
    }
    if two_to_one.is_none() && one_to_two.is_none() {
        break;
    }
}

assert_eq!(peer2.get(automerge::ROOT, "key")?.unwrap().0.to_str(), Some("value"));

Structs

A summary of the changes that the sender of the message already has. This is implicitly a request to the recipient to send all changes that the sender does not already have.
The sync message to be sent.
The state of synchronisation with a peer.

Enums

Traits

A document which can take part in the sync protocol