cojson 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +16 -0
- package/dist/coValueCore.d.ts +1 -5
- package/dist/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore.js +29 -43
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValueState.d.ts.map +1 -1
- package/dist/coValueState.js +18 -4
- package/dist/coValueState.js.map +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +21 -28
- package/dist/sync.js.map +1 -1
- package/dist/tests/sync.test.js +51 -12
- package/dist/tests/sync.test.js.map +1 -1
- package/package.json +1 -1
- package/src/coValueCore.ts +50 -60
- package/src/coValueState.ts +23 -4
- package/src/sync.ts +21 -38
- package/src/tests/sync.test.ts +64 -21
package/src/sync.ts
CHANGED
|
@@ -23,10 +23,6 @@ export function emptyKnownState(id: RawCoID): CoValueKnownState {
|
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
function getErrorMessage(e: unknown) {
|
|
27
|
-
return e instanceof Error ? e.message : "Unknown error";
|
|
28
|
-
}
|
|
29
|
-
|
|
30
26
|
export type SyncMessage =
|
|
31
27
|
| LoadMessage
|
|
32
28
|
| KnownStateMessage
|
|
@@ -415,7 +411,20 @@ export class SyncManager {
|
|
|
415
411
|
entry.loadFromPeers([peer]).catch((e) => {
|
|
416
412
|
logger.error("Error loading coValue in handleLoad", { err: e });
|
|
417
413
|
});
|
|
414
|
+
} else {
|
|
415
|
+
// We don't have any eligible peers to load the coValue from
|
|
416
|
+
// so we send a known state back to the sender to let it know
|
|
417
|
+
// that the coValue is unavailable
|
|
418
|
+
this.trySendToPeer(peer, {
|
|
419
|
+
action: "known",
|
|
420
|
+
id: msg.id,
|
|
421
|
+
header: false,
|
|
422
|
+
sessions: {},
|
|
423
|
+
}).catch((e) => {
|
|
424
|
+
logger.error("Error sending known state back", { err: e });
|
|
425
|
+
});
|
|
418
426
|
}
|
|
427
|
+
|
|
419
428
|
return;
|
|
420
429
|
} else {
|
|
421
430
|
this.local.loadCoValueCore(msg.id, peer.id).catch((e) => {
|
|
@@ -460,6 +469,13 @@ export class SyncManager {
|
|
|
460
469
|
err: e,
|
|
461
470
|
});
|
|
462
471
|
});
|
|
472
|
+
} else if (entry.state.type === "unavailable") {
|
|
473
|
+
this.trySendToPeer(peer, {
|
|
474
|
+
action: "known",
|
|
475
|
+
id: msg.id,
|
|
476
|
+
header: false,
|
|
477
|
+
sessions: {},
|
|
478
|
+
});
|
|
463
479
|
}
|
|
464
480
|
|
|
465
481
|
if (entry.state.type === "available") {
|
|
@@ -604,39 +620,12 @@ export class SyncManager {
|
|
|
604
620
|
continue;
|
|
605
621
|
}
|
|
606
622
|
|
|
607
|
-
const before = performance.now();
|
|
608
|
-
// eslint-disable-next-line neverthrow/must-use-result
|
|
609
623
|
const result = coValue.tryAddTransactions(
|
|
610
624
|
sessionID,
|
|
611
625
|
newTransactions,
|
|
612
626
|
undefined,
|
|
613
627
|
newContentForSession.lastSignature,
|
|
614
628
|
);
|
|
615
|
-
const after = performance.now();
|
|
616
|
-
if (after - before > 80) {
|
|
617
|
-
const totalTxLength = newTransactions
|
|
618
|
-
.map((t) =>
|
|
619
|
-
t.privacy === "private"
|
|
620
|
-
? t.encryptedChanges.length
|
|
621
|
-
: t.changes.length,
|
|
622
|
-
)
|
|
623
|
-
.reduce((a, b) => a + b, 0);
|
|
624
|
-
logger.debug(
|
|
625
|
-
`Adding incoming transactions took ${(after - before).toFixed(
|
|
626
|
-
2,
|
|
627
|
-
)}ms for ${totalTxLength} bytes = bandwidth: ${(
|
|
628
|
-
(1000 * totalTxLength) / (after - before) / (1024 * 1024)
|
|
629
|
-
).toFixed(2)} MB/s`,
|
|
630
|
-
);
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
// const theirTotalnTxs = Object.values(
|
|
634
|
-
// peer.optimisticKnownStates[msg.id]?.sessions || {},
|
|
635
|
-
// ).reduce((sum, nTxs) => sum + nTxs, 0);
|
|
636
|
-
// const ourTotalnTxs = [...coValue.sessionLogs.values()].reduce(
|
|
637
|
-
// (sum, session) => sum + session.transactions.length,
|
|
638
|
-
// 0,
|
|
639
|
-
// );
|
|
640
629
|
|
|
641
630
|
if (result.isErr()) {
|
|
642
631
|
logger.error("Failed to add transactions", {
|
|
@@ -735,16 +724,10 @@ export class SyncManager {
|
|
|
735
724
|
}
|
|
736
725
|
|
|
737
726
|
async actuallySyncCoValue(coValue: CoValueCore) {
|
|
738
|
-
// let blockingSince = performance.now();
|
|
739
727
|
for (const peer of this.peersInPriorityOrder()) {
|
|
740
728
|
if (peer.closed) continue;
|
|
741
729
|
if (peer.erroredCoValues.has(coValue.id)) continue;
|
|
742
|
-
|
|
743
|
-
// await new Promise<void>((resolve) => {
|
|
744
|
-
// setTimeout(resolve, 0);
|
|
745
|
-
// });
|
|
746
|
-
// blockingSince = performance.now();
|
|
747
|
-
// }
|
|
730
|
+
|
|
748
731
|
if (peer.optimisticKnownStates.has(coValue.id)) {
|
|
749
732
|
await this.tellUntoldKnownStateIncludingDependencies(coValue.id, peer);
|
|
750
733
|
await this.sendNewContentIncludingDependencies(coValue.id, peer);
|
package/src/tests/sync.test.ts
CHANGED
|
@@ -956,27 +956,6 @@ test.skip("When a peer's incoming/readable stream closes, we remove the peer", a
|
|
|
956
956
|
*/
|
|
957
957
|
});
|
|
958
958
|
|
|
959
|
-
test("If we start loading a coValue before connecting to a peer that has it, it will load it once we connect", async () => {
|
|
960
|
-
const { node: node1 } = await createConnectedTestNode();
|
|
961
|
-
|
|
962
|
-
const group = node1.createGroup();
|
|
963
|
-
|
|
964
|
-
const map = group.createMap();
|
|
965
|
-
map.set("hello", "world", "trusting");
|
|
966
|
-
|
|
967
|
-
const node2 = createTestNode();
|
|
968
|
-
|
|
969
|
-
const mapOnNode2Promise = loadCoValueOrFail(node2, map.id);
|
|
970
|
-
|
|
971
|
-
expect(node2.coValuesStore.get(map.core.id).state.type).toEqual("unknown");
|
|
972
|
-
|
|
973
|
-
connectNodeToSyncServer(node2);
|
|
974
|
-
|
|
975
|
-
const mapOnNode2 = await mapOnNode2Promise;
|
|
976
|
-
|
|
977
|
-
expect(mapOnNode2.get("hello")).toEqual("world");
|
|
978
|
-
});
|
|
979
|
-
|
|
980
959
|
test("should keep the peer state when the peer closes", async () => {
|
|
981
960
|
const client = createTestNode();
|
|
982
961
|
|
|
@@ -1726,6 +1705,45 @@ describe("loadCoValueCore with retry", () => {
|
|
|
1726
1705
|
await expect(promise1).resolves.not.toBe("unavailable");
|
|
1727
1706
|
await expect(promise2).resolves.not.toBe("unavailable");
|
|
1728
1707
|
});
|
|
1708
|
+
|
|
1709
|
+
test("should load unavailable coValues after they are synced", async () => {
|
|
1710
|
+
const bob = createTestNode();
|
|
1711
|
+
const alice = createTestNode();
|
|
1712
|
+
|
|
1713
|
+
// Create a group and map on anotherClient
|
|
1714
|
+
const group = alice.createGroup();
|
|
1715
|
+
const map = group.createMap();
|
|
1716
|
+
map.set("key1", "value1", "trusting");
|
|
1717
|
+
|
|
1718
|
+
// Start loading before syncing
|
|
1719
|
+
const result = await bob.loadCoValueCore(map.id);
|
|
1720
|
+
|
|
1721
|
+
expect(result).toBe("unavailable");
|
|
1722
|
+
|
|
1723
|
+
connectTwoPeers(alice, bob, "server", "server");
|
|
1724
|
+
|
|
1725
|
+
const result2 = await bob.loadCoValueCore(map.id);
|
|
1726
|
+
|
|
1727
|
+
expect(result2).not.toBe("unavailable");
|
|
1728
|
+
});
|
|
1729
|
+
|
|
1730
|
+
test("should successfully mark a coValue as unavailable if the server does not have it", async () => {
|
|
1731
|
+
const bob = createTestNode();
|
|
1732
|
+
const alice = createTestNode();
|
|
1733
|
+
const charlie = createTestNode();
|
|
1734
|
+
|
|
1735
|
+
connectTwoPeers(bob, charlie, "client", "server");
|
|
1736
|
+
|
|
1737
|
+
// Create a group and map on anotherClient
|
|
1738
|
+
const group = alice.createGroup();
|
|
1739
|
+
const map = group.createMap();
|
|
1740
|
+
map.set("key1", "value1", "trusting");
|
|
1741
|
+
|
|
1742
|
+
// Start loading before syncing
|
|
1743
|
+
const result = await bob.loadCoValueCore(map.id);
|
|
1744
|
+
|
|
1745
|
+
expect(result).toBe("unavailable");
|
|
1746
|
+
});
|
|
1729
1747
|
});
|
|
1730
1748
|
|
|
1731
1749
|
describe("waitForSyncWithPeer", () => {
|
|
@@ -1894,6 +1912,8 @@ describe("sync protocol", () => {
|
|
|
1894
1912
|
const map = group.createMap();
|
|
1895
1913
|
map.set("hello", "world", "trusting");
|
|
1896
1914
|
|
|
1915
|
+
await map.core.waitForSync();
|
|
1916
|
+
|
|
1897
1917
|
const mapOnJazzCloud = await loadCoValueOrFail(jazzCloud, map.id);
|
|
1898
1918
|
expect(mapOnJazzCloud.get("hello")).toEqual("world");
|
|
1899
1919
|
|
|
@@ -2038,6 +2058,29 @@ describe("sync protocol", () => {
|
|
|
2038
2058
|
},
|
|
2039
2059
|
},
|
|
2040
2060
|
},
|
|
2061
|
+
{
|
|
2062
|
+
from: "server",
|
|
2063
|
+
msg: {
|
|
2064
|
+
action: "known",
|
|
2065
|
+
header: true,
|
|
2066
|
+
id: map.id,
|
|
2067
|
+
sessions: {
|
|
2068
|
+
[client.currentSessionID]: 1,
|
|
2069
|
+
},
|
|
2070
|
+
},
|
|
2071
|
+
},
|
|
2072
|
+
{
|
|
2073
|
+
from: "server",
|
|
2074
|
+
msg: {
|
|
2075
|
+
action: "known",
|
|
2076
|
+
asDependencyOf: undefined,
|
|
2077
|
+
header: true,
|
|
2078
|
+
id: map.id,
|
|
2079
|
+
sessions: {
|
|
2080
|
+
[client.currentSessionID]: 1,
|
|
2081
|
+
},
|
|
2082
|
+
},
|
|
2083
|
+
},
|
|
2041
2084
|
]);
|
|
2042
2085
|
});
|
|
2043
2086
|
});
|