Skip to content

Instantly share code, notes, and snippets.

@dan-weaver
Last active August 17, 2021 15:18
Show Gist options
  • Save dan-weaver/9d2839e0ed12a03be74724c5c61198a3 to your computer and use it in GitHub Desktop.
Save dan-weaver/9d2839e0ed12a03be74724c5c61198a3 to your computer and use it in GitHub Desktop.
/*
Create two peers, each with one Automerge doc
peer2's doc is a clone of peer1's to ensure common history
Each peer has a syncState representing the estimated state of the other peer
Each peer has a FIFO list "inbox" emulating a network transport
Every 500ms, simulate the network for each peer in turn, via update():
- check for any messages in the peer's inbox
- call Automerge.receiveSyncMessage for each message
- and update doc & sync accordingly
- then clear the inbox
- check whether we think we need to send sync message to peer via generateSyncMessage ***
- update sync accordingly (??)
- if there was a message, push it into the peer's inbox for the next update()
*** Why does it continue to think there are sync updates required,
even though both docs are already in sync?
Notes:
it doesn't matter whether we poll peer1 first or peer2, the same thing happens
*/
import Automerge from "https://cdn.skypack.dev/automerge@1.0.1-preview.4";
let peer1 = {
name: "peer1",
doc: Automerge.init(),
sync: Automerge.initSyncState(), // for syncing to peer2
inbox: [] // FIFO for simulated network transport
};
let peer2 = {
name: "peer2",
// peer2's doc is a duplicate of peer1's doc:
doc: Automerge.clone(peer1.doc),
sync: Automerge.initSyncState(), // for syncing to peer1
inbox: [] // FIFO for simulated network transport
};
// check if a peer has changes to send to the other peer
// and if so, post them in the other's inbox
function send(self, other) {
// do we have any changes (or requests) for the other peer?
let [newsync, msg] = Automerge.generateSyncMessage(self.doc, self.sync);
self.sync = newsync;
// if we have a message to send:
if (msg) {
// always peer2
// console.log('peer sending', self.name)
// send msg to peer by appending to peer's inbox
other.inbox.push(msg);
// log that we sent a message:
document.getElementById("log").innerText +=
"\n" +
"send " +
self.name +
"->" +
other.name +
" msg: " +
msg.toString();
}
}
// check if a peer has any messages in its inbox,
// and apply them if it does
// then clear the inbox
function receive(self, other) {
// for each message received from the other peer:
for (const msg of self.inbox) {
// always peer1
// console.log("peer receiving", self.name)
// log that we received a message
document.getElementById("log").innerText +=
"\n" +
"recv " +
self.name +
"<-" +
other.name +
" msg: " +
msg.toString();
// apply the change & update our doc & syncState:
const [doc, newsync, patch] = Automerge.receiveSyncMessage(
self.doc,
self.sync,
msg
);
self.doc = doc;
self.sync = newsync;
send(self, other)
}
// clear inbox:
self.inbox = [];
}
// both peers initiate a sync
send(peer1, peer2);
send(peer2,peer1);
// simulate network activity
function update(self, other) {
receive(self, other);
}
setInterval(function () {
// simulate network activity for both peers
// doesn't matter which order you do these, same result
update(peer2, peer1);
update(peer1, peer2);
// show current state of docs
document.getElementById("result").innerText =
"peer1.doc = " +
JSON.stringify(peer1.doc) +
"\npeer2.doc = " +
JSON.stringify(peer2.doc);
// show current sync states:
document.getElementById("result").innerText +=
"\npeer1.sync = " +
JSON.stringify(peer1.sync, null, " ") +
"\npeer2.sync = " +
JSON.stringify(peer2.sync, null, " ");
// verify that the two peers' docs have different actorIDs:
document.getElementById("result").innerText +=
"\npeer1-actor: " + Automerge.getActorId(peer1.doc);
document.getElementById("result").innerText +=
"\npeer2-actor: " + Automerge.getActorId(peer2.doc);
}, 500);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment