Skip to content

Commit

Permalink
Merge pull request #290 from yarolegovich/yarolegovich/fix-doc-sync-o…
Browse files Browse the repository at this point in the history
…n-connect

fix: peer doesn't receive a document on connecting with another peer
  • Loading branch information
pvh authored Feb 6, 2024
2 parents a19a8e9 + f384720 commit 7a341b1
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 9 deletions.
6 changes: 3 additions & 3 deletions packages/automerge-repo/src/synchronizer/DocSynchronizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,8 @@ export class DocSynchronizer extends Synchronizer {
}

beginSync(peerIds: PeerId[]) {
const newPeers = new Set(
peerIds.filter(peerId => !this.#peers.includes(peerId))
const noPeersWithDocument = peerIds.every(
(peerId) => this.#peerDocumentStatuses[peerId] in ["unavailable", "wants"]
)

// At this point if we don't have anything in our storage, we need to use an empty doc to sync
Expand All @@ -242,7 +242,7 @@ export class DocSynchronizer extends Synchronizer {
this.#checkDocUnavailable()

const wasUnavailable = doc === undefined
if (wasUnavailable && newPeers.size == 0) {
if (wasUnavailable && noPeersWithDocument) {
return
}

Expand Down
33 changes: 32 additions & 1 deletion packages/automerge-repo/test/Repo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { MessageChannelNetworkAdapter } from "../../automerge-repo-network-messa
import assert from "assert"
import * as Uuid from "uuid"
import { describe, expect, it } from "vitest"
import { READY } from "../src/DocHandle.js"
import { HandleState, READY } from "../src/DocHandle.js"
import { parseAutomergeUrl } from "../src/AutomergeUrl.js"
import {
generateAutomergeUrl,
Expand Down Expand Up @@ -1023,6 +1023,37 @@ describe("Repo", () => {
})
})


it('peer receives a document when connection is recovered', async () => {
const alice = "alice" as PeerId;
const bob = "bob" as PeerId;
const [aliceAdapter, bobAdapter] = DummyNetworkAdapter.createConnectedPair();
const aliceRepo = new Repo({
network: [aliceAdapter],
peerId: alice
})
const bobRepo = new Repo({
network: [bobAdapter],
peerId: bob
})

const aliceDoc = aliceRepo.create();
aliceDoc.change((doc: any) => doc.text = 'Hello world');

const bobDoc = bobRepo.find(aliceDoc.url);
bobDoc.unavailable()
await bobDoc.whenReady([HandleState.UNAVAILABLE]);

aliceAdapter.peerCandidate(bob);
// Bob isn't yet connected to Alice and can't respond to her sync message
await pause(100);
bobAdapter.peerCandidate(alice);

await bobDoc.whenReady([HandleState.READY]);

assert.equal(bobDoc.isReady(), true);
});

describe("with peers (mesh network)", () => {
const setup = async () => {
// Set up three repos; connect Alice to Bob, Bob to Charlie, and Alice to Charlie
Expand Down
42 changes: 37 additions & 5 deletions packages/automerge-repo/test/helpers/DummyNetworkAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,53 @@
import { NetworkAdapter } from "../../src/index.js"
import { pause } from "../../src/helpers/pause.js";
import { Message, NetworkAdapter, PeerId } from "../../src/index.js"

export class DummyNetworkAdapter extends NetworkAdapter {
#startReady: boolean
#sendMessage?: SendMessageFn;

constructor({ startReady = true }: Options = {}) {
constructor(opts: Options = {startReady: true}) {
super()
this.#startReady = startReady
this.#startReady = opts.startReady;
this.#sendMessage = opts.sendMessage;
}
send() {}

connect(_: string) {
if (this.#startReady) {
this.emit("ready", { network: this })
}
}

disconnect() {}

peerCandidate(peerId: PeerId) {
this.emit('peer-candidate', { peerId, peerMetadata: {} });
}

override send(message: Message) {
this.#sendMessage?.(message);
}

receive(message: Message) {
this.emit('message', message);
}

static createConnectedPair() {
const adapter1: DummyNetworkAdapter = new DummyNetworkAdapter({
startReady: true,
sendMessage: (message: Message) => pause(10).then(() => adapter2.receive(message)),
});
const adapter2: DummyNetworkAdapter = new DummyNetworkAdapter({
startReady: true,
sendMessage: (message: Message) => pause(10).then(() => adapter1.receive(message)),
});

return [adapter1, adapter2];
}
}

type SendMessageFn = (message: Message) => void;

type Options = {
startReady?: boolean
startReady?: boolean;
sendMessage?: SendMessageFn;
}

0 comments on commit 7a341b1

Please sign in to comment.