cojson 0.20.0 → 0.20.2

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.
Files changed (91) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +21 -0
  3. package/dist/GarbageCollector.d.ts +3 -3
  4. package/dist/GarbageCollector.d.ts.map +1 -1
  5. package/dist/GarbageCollector.js +4 -4
  6. package/dist/GarbageCollector.js.map +1 -1
  7. package/dist/PeerState.d.ts +6 -1
  8. package/dist/PeerState.d.ts.map +1 -1
  9. package/dist/PeerState.js +18 -3
  10. package/dist/PeerState.js.map +1 -1
  11. package/dist/coValueCore/coValueCore.d.ts +26 -5
  12. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  13. package/dist/coValueCore/coValueCore.js +115 -50
  14. package/dist/coValueCore/coValueCore.js.map +1 -1
  15. package/dist/coValues/coList.d.ts +1 -0
  16. package/dist/coValues/coList.d.ts.map +1 -1
  17. package/dist/coValues/coList.js +3 -0
  18. package/dist/coValues/coList.js.map +1 -1
  19. package/dist/config.d.ts +2 -2
  20. package/dist/config.d.ts.map +1 -1
  21. package/dist/config.js +4 -4
  22. package/dist/config.js.map +1 -1
  23. package/dist/exports.d.ts +3 -3
  24. package/dist/exports.d.ts.map +1 -1
  25. package/dist/exports.js +2 -2
  26. package/dist/exports.js.map +1 -1
  27. package/dist/localNode.d.ts +12 -0
  28. package/dist/localNode.d.ts.map +1 -1
  29. package/dist/localNode.js +51 -3
  30. package/dist/localNode.js.map +1 -1
  31. package/dist/queue/LinkedList.d.ts +9 -3
  32. package/dist/queue/LinkedList.d.ts.map +1 -1
  33. package/dist/queue/LinkedList.js +30 -1
  34. package/dist/queue/LinkedList.js.map +1 -1
  35. package/dist/queue/OutgoingLoadQueue.d.ts +95 -0
  36. package/dist/queue/OutgoingLoadQueue.d.ts.map +1 -0
  37. package/dist/queue/OutgoingLoadQueue.js +240 -0
  38. package/dist/queue/OutgoingLoadQueue.js.map +1 -0
  39. package/dist/sync.d.ts.map +1 -1
  40. package/dist/sync.js +34 -41
  41. package/dist/sync.js.map +1 -1
  42. package/dist/tests/LinkedList.test.js +90 -0
  43. package/dist/tests/LinkedList.test.js.map +1 -1
  44. package/dist/tests/OutgoingLoadQueue.test.d.ts +2 -0
  45. package/dist/tests/OutgoingLoadQueue.test.d.ts.map +1 -0
  46. package/dist/tests/OutgoingLoadQueue.test.js +814 -0
  47. package/dist/tests/OutgoingLoadQueue.test.js.map +1 -0
  48. package/dist/tests/coValueCore.loadFromStorage.test.js +87 -0
  49. package/dist/tests/coValueCore.loadFromStorage.test.js.map +1 -1
  50. package/dist/tests/knownState.lazyLoading.test.js +44 -0
  51. package/dist/tests/knownState.lazyLoading.test.js.map +1 -1
  52. package/dist/tests/sync.concurrentLoad.test.d.ts +2 -0
  53. package/dist/tests/sync.concurrentLoad.test.d.ts.map +1 -0
  54. package/dist/tests/sync.concurrentLoad.test.js +481 -0
  55. package/dist/tests/sync.concurrentLoad.test.js.map +1 -0
  56. package/dist/tests/sync.garbageCollection.test.js +87 -3
  57. package/dist/tests/sync.garbageCollection.test.js.map +1 -1
  58. package/dist/tests/sync.multipleServers.test.js +0 -62
  59. package/dist/tests/sync.multipleServers.test.js.map +1 -1
  60. package/dist/tests/sync.peerReconciliation.test.js +156 -0
  61. package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
  62. package/dist/tests/sync.storage.test.js +1 -1
  63. package/dist/tests/testStorage.d.ts.map +1 -1
  64. package/dist/tests/testStorage.js +3 -1
  65. package/dist/tests/testStorage.js.map +1 -1
  66. package/dist/tests/testUtils.d.ts +1 -0
  67. package/dist/tests/testUtils.d.ts.map +1 -1
  68. package/dist/tests/testUtils.js +2 -1
  69. package/dist/tests/testUtils.js.map +1 -1
  70. package/package.json +4 -4
  71. package/src/GarbageCollector.ts +4 -3
  72. package/src/PeerState.ts +26 -3
  73. package/src/coValueCore/coValueCore.ts +129 -53
  74. package/src/coValues/coList.ts +4 -0
  75. package/src/config.ts +4 -4
  76. package/src/exports.ts +2 -2
  77. package/src/localNode.ts +65 -4
  78. package/src/queue/LinkedList.ts +34 -4
  79. package/src/queue/OutgoingLoadQueue.ts +307 -0
  80. package/src/sync.ts +37 -43
  81. package/src/tests/LinkedList.test.ts +111 -0
  82. package/src/tests/OutgoingLoadQueue.test.ts +1129 -0
  83. package/src/tests/coValueCore.loadFromStorage.test.ts +108 -0
  84. package/src/tests/knownState.lazyLoading.test.ts +52 -0
  85. package/src/tests/sync.concurrentLoad.test.ts +650 -0
  86. package/src/tests/sync.garbageCollection.test.ts +115 -3
  87. package/src/tests/sync.multipleServers.test.ts +0 -65
  88. package/src/tests/sync.peerReconciliation.test.ts +199 -0
  89. package/src/tests/sync.storage.test.ts +1 -1
  90. package/src/tests/testStorage.ts +3 -1
  91. package/src/tests/testUtils.ts +3 -1
@@ -0,0 +1,481 @@
1
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
2
+ import { CO_VALUE_LOADING_CONFIG, setMaxInFlightLoadsPerPeer, } from "../config.js";
3
+ import { blockMessageTypeOnOutgoingPeer, fillCoMapWithLargeData, loadCoValueOrFail, importContentIntoNode, setupTestNode, SyncMessagesLog, TEST_NODE_CONFIG, waitFor, } from "./testUtils.js";
4
+ let jazzCloud;
5
+ // Store original config values
6
+ let originalMaxInFlightLoads;
7
+ let originalTimeout;
8
+ beforeEach(async () => {
9
+ // We want to simulate a real world communication that happens asynchronously
10
+ TEST_NODE_CONFIG.withAsyncPeers = true;
11
+ originalMaxInFlightLoads =
12
+ CO_VALUE_LOADING_CONFIG.MAX_IN_FLIGHT_LOADS_PER_PEER;
13
+ originalTimeout = CO_VALUE_LOADING_CONFIG.TIMEOUT;
14
+ SyncMessagesLog.clear();
15
+ jazzCloud = setupTestNode({ isSyncServer: true });
16
+ });
17
+ afterEach(() => {
18
+ // Restore original config
19
+ setMaxInFlightLoadsPerPeer(originalMaxInFlightLoads);
20
+ CO_VALUE_LOADING_CONFIG.TIMEOUT = originalTimeout;
21
+ vi.useRealTimers();
22
+ });
23
+ describe("concurrent load", () => {
24
+ test("should throttle load requests when at capacity", async () => {
25
+ setMaxInFlightLoadsPerPeer(2);
26
+ const client = setupTestNode({
27
+ connected: false,
28
+ });
29
+ const { peerOnServer } = client.connectToSyncServer();
30
+ // Create multiple CoValues on the server
31
+ const group = jazzCloud.node.createGroup();
32
+ const map1 = group.createMap();
33
+ const map2 = group.createMap();
34
+ const map3 = group.createMap();
35
+ map1.set("key", "value1");
36
+ map2.set("key", "value2");
37
+ map3.set("key", "value3");
38
+ // Block content responses to see the throttling effect
39
+ const blocker = blockMessageTypeOnOutgoingPeer(peerOnServer, "content", {});
40
+ // Start loading all three
41
+ const promise1 = client.node.loadCoValueCore(map1.id);
42
+ const promise2 = client.node.loadCoValueCore(map2.id);
43
+ const promise3 = client.node.loadCoValueCore(map3.id);
44
+ // Wait for messages to be sent
45
+ await new Promise((resolve) => setTimeout(resolve, 10));
46
+ // Get the LOAD messages sent
47
+ const loadMessages = SyncMessagesLog.messages.filter((m) => m.msg.action === "load");
48
+ // Only 2 LOAD messages should have been sent (throttled)
49
+ expect(loadMessages.length).toBe(2);
50
+ // Unblock and let it complete
51
+ blocker.unblock();
52
+ blocker.sendBlockedMessages();
53
+ await Promise.all([promise1, promise2, promise3]);
54
+ // After completion, all 3 should have been loaded
55
+ const allLoadMessages = SyncMessagesLog.messages.filter((m) => m.msg.action === "load");
56
+ expect(allLoadMessages.length).toBe(3);
57
+ // Verify all were loaded successfully despite throttling
58
+ expect(SyncMessagesLog.getMessages({
59
+ Group: group.core,
60
+ Map1: map1.core,
61
+ Map2: map2.core,
62
+ Map3: map3.core,
63
+ })).toMatchInlineSnapshot(`
64
+ [
65
+ "client -> server | LOAD Map1 sessions: empty",
66
+ "client -> server | LOAD Map2 sessions: empty",
67
+ "server -> client | CONTENT Group header: true new: After: 0 New: 3",
68
+ "server -> client | CONTENT Map1 header: true new: After: 0 New: 1",
69
+ "server -> client | CONTENT Map2 header: true new: After: 0 New: 1",
70
+ "client -> server | KNOWN Group sessions: header/3",
71
+ "client -> server | KNOWN Map1 sessions: header/1",
72
+ "client -> server | LOAD Map3 sessions: empty",
73
+ "client -> server | KNOWN Map2 sessions: header/1",
74
+ "server -> client | CONTENT Map3 header: true new: After: 0 New: 1",
75
+ "client -> server | KNOWN Map3 sessions: header/1",
76
+ ]
77
+ `);
78
+ });
79
+ test("should process pending loads when capacity becomes available", async () => {
80
+ setMaxInFlightLoadsPerPeer(1);
81
+ const client = setupTestNode({
82
+ connected: true,
83
+ });
84
+ // Create multiple CoValues on the server
85
+ const group = jazzCloud.node.createGroup();
86
+ const map1 = group.createMap();
87
+ const map2 = group.createMap();
88
+ map1.set("key", "value1", "trusting");
89
+ map2.set("key", "value2", "trusting");
90
+ // Load both sequentially due to throttling
91
+ const [result1, result2] = await Promise.all([
92
+ loadCoValueOrFail(client.node, map1.id),
93
+ loadCoValueOrFail(client.node, map2.id),
94
+ ]);
95
+ expect(result1.get("key")).toBe("value1");
96
+ expect(result2.get("key")).toBe("value2");
97
+ // Verify both were loaded successfully despite throttling
98
+ expect(SyncMessagesLog.getMessages({
99
+ Group: group.core,
100
+ Map1: map1.core,
101
+ Map2: map2.core,
102
+ })).toMatchInlineSnapshot(`
103
+ [
104
+ "client -> server | LOAD Map1 sessions: empty",
105
+ "server -> client | CONTENT Group header: true new: After: 0 New: 3",
106
+ "server -> client | CONTENT Map1 header: true new: After: 0 New: 1",
107
+ "client -> server | KNOWN Group sessions: header/3",
108
+ "client -> server | KNOWN Map1 sessions: header/1",
109
+ "client -> server | LOAD Map2 sessions: empty",
110
+ "server -> client | CONTENT Map2 header: true new: After: 0 New: 1",
111
+ "client -> server | KNOWN Map2 sessions: header/1",
112
+ ]
113
+ `);
114
+ });
115
+ test("should prioritize unavailable CoValues over available ones", async () => {
116
+ setMaxInFlightLoadsPerPeer(1);
117
+ const client = setupTestNode({
118
+ connected: true,
119
+ });
120
+ // Create CoValues on the server
121
+ const group = jazzCloud.node.createGroup();
122
+ const map1 = group.createMap();
123
+ const map2 = group.createMap();
124
+ const map3 = group.createMap();
125
+ map1.set("key", "value1", "trusting");
126
+ map2.set("key", "value2", "trusting");
127
+ map3.set("key", "value3", "trusting");
128
+ // First, load map1 to make it "available" locally
129
+ await loadCoValueOrFail(client.node, map1.id);
130
+ SyncMessagesLog.clear();
131
+ // Update map1 on server (so client has stale version)
132
+ map1.set("key", "updated1", "trusting");
133
+ // Now load map2 (unavailable) and reload map1 (available but outdated)
134
+ // map2 should be prioritized
135
+ const [result1, result2] = await Promise.all([
136
+ loadCoValueOrFail(client.node, map1.id), // Available, lower priority
137
+ loadCoValueOrFail(client.node, map2.id), // Unavailable, higher priority
138
+ ]);
139
+ // Both should succeed
140
+ expect(result2.get("key")).toBe("value2");
141
+ // map2 (unavailable) should have been loaded first
142
+ const loadMessages = SyncMessagesLog.messages.filter((m) => m.msg.action === "load");
143
+ expect(loadMessages.length).toBeGreaterThanOrEqual(1);
144
+ // The first load should be for map2 (unavailable, high priority)
145
+ expect(loadMessages[0]?.msg).toMatchObject({
146
+ action: "load",
147
+ id: map2.id,
148
+ });
149
+ });
150
+ test("should handle high load with many concurrent requests", async () => {
151
+ setMaxInFlightLoadsPerPeer(5);
152
+ const client = setupTestNode({
153
+ connected: true,
154
+ });
155
+ // Create many CoValues on the server
156
+ const group = jazzCloud.node.createGroup();
157
+ const maps = Array.from({ length: 20 }, (_, i) => {
158
+ const map = group.createMap();
159
+ map.set("index", i, "trusting");
160
+ return map;
161
+ });
162
+ // Load all of them concurrently
163
+ const results = await Promise.all(maps.map((map) => loadCoValueOrFail(client.node, map.id)));
164
+ // All should have been loaded successfully
165
+ results.forEach((result, i) => {
166
+ expect(result.get("index")).toBe(i);
167
+ });
168
+ });
169
+ test("should timeout load requests that take too long", async () => {
170
+ vi.useFakeTimers();
171
+ setMaxInFlightLoadsPerPeer(1);
172
+ CO_VALUE_LOADING_CONFIG.TIMEOUT = 1000;
173
+ const client = setupTestNode({
174
+ connected: false,
175
+ });
176
+ const { peerOnServer } = client.connectToSyncServer();
177
+ // Create a CoValue on the server
178
+ const group = jazzCloud.node.createGroup();
179
+ const map = group.createMap();
180
+ map.set("key", "value");
181
+ // Block content to simulate a slow/unresponsive server
182
+ const blocker = blockMessageTypeOnOutgoingPeer(peerOnServer, "content", {
183
+ id: map.id,
184
+ });
185
+ const loadPromise = client.node.loadCoValueCore(map.id);
186
+ // Advance past the timeout
187
+ await vi.advanceTimersByTimeAsync(CO_VALUE_LOADING_CONFIG.TIMEOUT + 100);
188
+ // The queue slot should be freed
189
+ // The second retry attempt should happen after RETRY_DELAY
190
+ await vi.advanceTimersByTimeAsync(CO_VALUE_LOADING_CONFIG.RETRY_DELAY + 100);
191
+ // Unblock to let retries succeed
192
+ blocker.sendBlockedMessages();
193
+ blocker.unblock();
194
+ // Wait for the retry to complete
195
+ await vi.advanceTimersByTimeAsync(100);
196
+ const result = await loadPromise;
197
+ // The retry should have succeeded (since we unblocked)
198
+ expect(result.isAvailable()).toBe(true);
199
+ });
200
+ test("should free queue slots on disconnect", async () => {
201
+ setMaxInFlightLoadsPerPeer(2);
202
+ const client = setupTestNode({
203
+ connected: false,
204
+ });
205
+ const { peerState, peerOnServer } = client.connectToSyncServer();
206
+ // Create CoValues on the server
207
+ const group = jazzCloud.node.createGroup();
208
+ const map1 = group.createMap();
209
+ const map2 = group.createMap();
210
+ const map3 = group.createMap();
211
+ // Block content to keep requests in-flight
212
+ const blocker = blockMessageTypeOnOutgoingPeer(peerOnServer, "content", {});
213
+ // Start loading (will be in-flight)
214
+ client.node.loadCoValueCore(map1.id);
215
+ client.node.loadCoValueCore(map2.id);
216
+ client.node.loadCoValueCore(map3.id);
217
+ await new Promise((resolve) => setTimeout(resolve, 10));
218
+ // Disconnect
219
+ peerState.gracefulShutdown();
220
+ // Queue should be cleared
221
+ // Reconnect and verify new requests can be sent
222
+ client.connectToSyncServer();
223
+ const result = await loadCoValueOrFail(client.node, map1.id);
224
+ expect(result.get("key")).toBeUndefined(); // map1 was created without a key
225
+ blocker.unblock();
226
+ });
227
+ test("should handle reconnection with pending loads", async () => {
228
+ setMaxInFlightLoadsPerPeer(1);
229
+ const client = setupTestNode({
230
+ connected: false,
231
+ });
232
+ const { peerState, peerOnServer } = client.connectToSyncServer({
233
+ persistent: true,
234
+ });
235
+ // Create a CoValue on the server
236
+ const group = jazzCloud.node.createGroup();
237
+ const map = group.createMap();
238
+ map.set("key", "value");
239
+ // Block content to keep request in-flight
240
+ blockMessageTypeOnOutgoingPeer(peerOnServer, "content", {
241
+ id: map.id,
242
+ });
243
+ // Start loading
244
+ const loadPromise = client.node.loadCoValueCore(map.id);
245
+ await new Promise((resolve) => setTimeout(resolve, 10));
246
+ // Disconnect
247
+ peerState.gracefulShutdown();
248
+ // Reconnect
249
+ client.connectToSyncServer({
250
+ persistent: true,
251
+ });
252
+ // The load should complete after reconnection
253
+ const result = await loadPromise;
254
+ expect(result.isAvailable()).toBe(true);
255
+ });
256
+ test("should maintain FIFO order for queued requests", async () => {
257
+ setMaxInFlightLoadsPerPeer(1);
258
+ const client = setupTestNode({
259
+ connected: false,
260
+ });
261
+ const { peerOnServer } = client.connectToSyncServer();
262
+ // Create CoValues on the server
263
+ const group = jazzCloud.node.createGroup();
264
+ const maps = Array.from({ length: 5 }, () => group.createMap());
265
+ // Block content to build up the queue
266
+ const blocker = blockMessageTypeOnOutgoingPeer(peerOnServer, "content", {});
267
+ // Start loading all maps (first one goes in-flight, rest queued)
268
+ const loadPromises = maps.map((map) => client.node.loadCoValueCore(map.id));
269
+ await new Promise((resolve) => setTimeout(resolve, 10));
270
+ // Get the LOAD messages before unblocking
271
+ const loadMessagesBefore = SyncMessagesLog.messages.filter((m) => m.msg.action === "load");
272
+ // Only 1 should be sent (at capacity)
273
+ expect(loadMessagesBefore.length).toBe(1);
274
+ expect(loadMessagesBefore[0]?.msg).toMatchObject({
275
+ action: "load",
276
+ id: maps[0]?.id,
277
+ });
278
+ // Unblock to process the queue
279
+ blocker.sendBlockedMessages();
280
+ blocker.unblock();
281
+ await Promise.all(loadPromises);
282
+ // Verify all LOAD messages were sent
283
+ const allLoadMessages = SyncMessagesLog.messages.filter((m) => m.msg.action === "load");
284
+ // All 5 should eventually be sent
285
+ expect(allLoadMessages.length).toBe(5);
286
+ // They should be in order (maps[0], maps[1], maps[2], maps[3], maps[4])
287
+ for (let i = 0; i < allLoadMessages.length; i++) {
288
+ expect(allLoadMessages[i]?.msg).toMatchObject({
289
+ action: "load",
290
+ id: maps[i]?.id,
291
+ });
292
+ }
293
+ });
294
+ test("should allow dependency loads to overflow the concurrency limit", async () => {
295
+ setMaxInFlightLoadsPerPeer(1);
296
+ const server = setupTestNode();
297
+ const client = setupTestNode({ connected: false });
298
+ client.connectToSyncServer({
299
+ ourName: "client",
300
+ syncServerName: "server",
301
+ syncServer: server.node,
302
+ });
303
+ // Create a CoValue on the server - the Map depends on the Group
304
+ const group = server.node.createGroup();
305
+ group.addMember("everyone", "writer");
306
+ const map = group.createMap();
307
+ map.set("key", "value");
308
+ // Delete the Group from server so it won't be pushed with the Map content
309
+ // skipVerify prevents the server from checking dependencies before sending
310
+ server.node.syncManager.disableTransactionVerification();
311
+ server.node.internalDeleteCoValue(group.id);
312
+ // Load the map from the client
313
+ // The flow is:
314
+ // 1. Client sends LOAD Map to server (takes the slot, limit=1)
315
+ // 2. Server responds with Map content (no deps pushed because skipVerify + Group deleted)
316
+ // 3. Client sees missing dependency (Group), sends LOAD Group to server
317
+ // This would be blocked by limit=1 without allowOverflow since Map load slot is taken
318
+ // 4. With allowOverflow, Group load bypasses the queue
319
+ // 5. Server responds with KNOWN Group (doesn't have it - was deleted)
320
+ // 6. Group content is moved back to server (simulating it becoming available)
321
+ // 7. Server responds with Group content
322
+ // 8. Client can now process Map content
323
+ const promise = loadCoValueOrFail(client.node, map.id);
324
+ // Wait for the Map content to be sent
325
+ await waitFor(() => SyncMessagesLog.messages.length >= 2);
326
+ importContentIntoNode(group.core, server.node, 1);
327
+ const result = await promise;
328
+ expect(result.get("key")).toBe("value");
329
+ // Verify both were loaded successfully despite throttling
330
+ expect(SyncMessagesLog.getMessages({
331
+ Group: group.core,
332
+ Map: map.core,
333
+ })).toMatchInlineSnapshot(`
334
+ [
335
+ "client -> server | LOAD Map sessions: empty",
336
+ "server -> client | CONTENT Map header: true new: After: 0 New: 1",
337
+ "client -> server | LOAD Group sessions: empty",
338
+ "server -> client | KNOWN Group sessions: empty",
339
+ "server -> client | CONTENT Group header: true new: After: 0 New: 5",
340
+ "client -> server | KNOWN Group sessions: header/5",
341
+ "client -> server | KNOWN Map sessions: header/1",
342
+ ]
343
+ `);
344
+ });
345
+ test("should keep load slot occupied while streaming large CoValues", async () => {
346
+ setMaxInFlightLoadsPerPeer(1);
347
+ const client = setupTestNode({
348
+ connected: false,
349
+ });
350
+ const { peerState, peerOnServer } = client.connectToSyncServer();
351
+ // Create a large CoValue that requires multiple chunks to stream
352
+ const group = jazzCloud.node.createGroup();
353
+ const largeMap = group.createMap();
354
+ fillCoMapWithLargeData(largeMap);
355
+ // Create a small CoValue that will be queued
356
+ const smallMap = group.createMap();
357
+ smallMap.set("key", "value", "trusting");
358
+ // Block all the streaming chunks, except the first content message
359
+ const blocker = blockMessageTypeOnOutgoingPeer(peerOnServer, "content", {
360
+ id: largeMap.id,
361
+ matcher: (msg) => msg.action === "content" && !msg.expectContentUntil,
362
+ });
363
+ // Start loading both maps concurrently
364
+ const largeMapOnClient = await client.node.loadCoValueCore(largeMap.id);
365
+ const smallMapPromise = client.node.loadCoValueCore(smallMap.id);
366
+ expect(client.node.getCoValue(largeMap.id).isStreaming()).toBe(true);
367
+ await new Promise((resolve) => setTimeout(resolve, 10));
368
+ // The SmallMap load should still be waiting in the queue
369
+ expect(SyncMessagesLog.getMessages({
370
+ Group: group.core,
371
+ LargeMap: largeMapOnClient,
372
+ SmallMap: smallMap.core,
373
+ })).toMatchInlineSnapshot(`
374
+ [
375
+ "client -> server | LOAD LargeMap sessions: empty",
376
+ "server -> client | CONTENT Group header: true new: After: 0 New: 3",
377
+ "server -> client | CONTENT LargeMap header: true new: After: 0 New: 73 expectContentUntil: header/200",
378
+ "client -> server | KNOWN Group sessions: header/3",
379
+ "client -> server | KNOWN LargeMap sessions: header/73",
380
+ ]
381
+ `);
382
+ // Now unblock and send all remaining chunks to complete streaming
383
+ blocker.unblock();
384
+ blocker.sendBlockedMessages();
385
+ await client.node.getCoValue(largeMap.id).waitForFullStreaming();
386
+ const loadedSmallMap = await smallMapPromise;
387
+ expect(loadedSmallMap.isAvailable()).toBe(true);
388
+ });
389
+ test("should prioritize user-initiated loads over peer reconciliation loads", async () => {
390
+ setMaxInFlightLoadsPerPeer(1);
391
+ // Create CoValues on the server before the client connects
392
+ const group = jazzCloud.node.createGroup();
393
+ const [a, b, c] = [
394
+ group.createMap({ test: "a" }),
395
+ group.createMap({ test: "b" }),
396
+ group.createMap({ test: "c" }),
397
+ ];
398
+ const client = setupTestNode({
399
+ connected: false,
400
+ });
401
+ const { peerState } = client.connectToSyncServer();
402
+ // Load a CoValue to make it available locally
403
+ await loadCoValueOrFail(client.node, a.id);
404
+ await loadCoValueOrFail(client.node, b.id);
405
+ // Close the peer connection
406
+ peerState.gracefulShutdown();
407
+ SyncMessagesLog.clear();
408
+ // Reconnect to the server to trigger the reconciliation load
409
+ client.connectToSyncServer();
410
+ // The reconciliation load should be in the low-priority queue
411
+ // Now make a user-initiated load for a different CoValue
412
+ await loadCoValueOrFail(client.node, c.id);
413
+ // Wait for the reconciliation loads to be sent
414
+ await waitFor(() => SyncMessagesLog.messages.length >= 8);
415
+ // Expect Group, C, A, B
416
+ expect(SyncMessagesLog.getMessages({
417
+ Group: group.core,
418
+ A: a.core,
419
+ B: b.core,
420
+ C: c.core,
421
+ })).toMatchInlineSnapshot(`
422
+ [
423
+ "client -> server | LOAD Group sessions: header/3",
424
+ "server -> client | KNOWN Group sessions: header/3",
425
+ "client -> server | LOAD C sessions: empty",
426
+ "server -> client | CONTENT C header: true new: After: 0 New: 1",
427
+ "client -> server | KNOWN C sessions: header/1",
428
+ "client -> server | LOAD A sessions: header/1",
429
+ "server -> client | KNOWN A sessions: header/1",
430
+ "client -> server | LOAD B sessions: header/1",
431
+ "server -> client | KNOWN B sessions: header/1",
432
+ ]
433
+ `);
434
+ });
435
+ test("should upgrade low-priority reconciliation load to high-priority when user requests it", async () => {
436
+ setMaxInFlightLoadsPerPeer(1);
437
+ // Create CoValues on the server before the client connects
438
+ const group = jazzCloud.node.createGroup();
439
+ const [a, b, c] = [
440
+ group.createMap({ test: "a" }),
441
+ group.createMap({ test: "b" }),
442
+ group.createMap({ test: "c" }),
443
+ ];
444
+ const client = setupTestNode({
445
+ connected: false,
446
+ });
447
+ // Load both CoValues to make them marked as unavailable
448
+ await client.node.loadCoValueCore(a.id);
449
+ await client.node.loadCoValueCore(b.id);
450
+ await client.node.loadCoValueCore(c.id);
451
+ // Reconnect to the server to trigger the reconciliation load
452
+ client.connectToSyncServer();
453
+ // The reconciliation load should be in the low-priority queue
454
+ // Now try to bump-up the priority of the load for c
455
+ client.node.loadCoValueCore(c.id);
456
+ // Wait for the reconciliation loads to be sent
457
+ await waitFor(() => SyncMessagesLog.messages.length >= 6);
458
+ // Expect A, C, B
459
+ expect(SyncMessagesLog.getMessages({
460
+ Group: group.core,
461
+ A: a.core,
462
+ B: b.core,
463
+ C: c.core,
464
+ })).toMatchInlineSnapshot(`
465
+ [
466
+ "client -> server | LOAD A sessions: empty",
467
+ "server -> client | CONTENT Group header: true new: After: 0 New: 3",
468
+ "server -> client | CONTENT A header: true new: After: 0 New: 1",
469
+ "client -> server | KNOWN Group sessions: header/3",
470
+ "client -> server | KNOWN A sessions: header/1",
471
+ "client -> server | LOAD C sessions: empty",
472
+ "server -> client | CONTENT C header: true new: After: 0 New: 1",
473
+ "client -> server | KNOWN C sessions: header/1",
474
+ "client -> server | LOAD B sessions: empty",
475
+ "server -> client | CONTENT B header: true new: After: 0 New: 1",
476
+ "client -> server | KNOWN B sessions: header/1",
477
+ ]
478
+ `);
479
+ });
480
+ });
481
+ //# sourceMappingURL=sync.concurrentLoad.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.concurrentLoad.test.js","sourceRoot":"","sources":["../../src/tests/sync.concurrentLoad.test.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { beforeEach, describe, expect, test } from "vitest";
2
+ import { expectMap } from "../coValue";
2
3
  import { setGarbageCollectorMaxAge } from "../config";
3
4
  import { SyncMessagesLog, TEST_NODE_CONFIG, loadCoValueOrFail, setupTestNode, } from "./testUtils";
4
5
  // We want to simulate a real world communication that happens asynchronously
@@ -126,19 +127,21 @@ describe("sync after the garbage collector has run", () => {
126
127
  // The storage should work even after the coValue is unmounted, so the load should be successful
127
128
  const mapOnServer = await loadCoValueOrFail(jazzCloud.node, map.id);
128
129
  expect(mapOnServer.get("hello")).toEqual("updated");
130
+ // With garbageCollected shells, client uses cached knownState (header/1)
131
+ // which is more accurate than asking storage (which returns empty)
129
132
  expect(SyncMessagesLog.getMessages({
130
133
  Group: group.core,
131
134
  Map: map.core,
132
135
  })).toMatchInlineSnapshot(`
133
136
  [
134
- "client -> server | LOAD Map sessions: empty",
137
+ "client -> server | LOAD Map sessions: header/1",
135
138
  "client -> server | LOAD Group sessions: header/3",
136
139
  "client -> storage | CONTENT Group header: true new: After: 0 New: 3",
137
140
  "client -> server | CONTENT Group header: true new: After: 0 New: 3",
138
141
  "client -> storage | CONTENT Map header: true new: After: 0 New: 1",
139
142
  "client -> server | CONTENT Map header: true new: After: 0 New: 1",
140
- "server -> storage | LOAD Map sessions: empty",
141
- "storage -> server | KNOWN Map sessions: empty",
143
+ "server -> storage | GET_KNOWN_STATE Map",
144
+ "storage -> server | GET_KNOWN_STATE_RESULT Map sessions: empty",
142
145
  "server -> client | KNOWN Map sessions: empty",
143
146
  "server -> storage | GET_KNOWN_STATE Group",
144
147
  "storage -> server | GET_KNOWN_STATE_RESULT Group sessions: empty",
@@ -150,5 +153,86 @@ describe("sync after the garbage collector has run", () => {
150
153
  ]
151
154
  `);
152
155
  });
156
+ test("knownStateWithStreaming returns lastKnownState for garbageCollected CoValues", async () => {
157
+ // This test verifies that knownStateWithStreaming() returns the cached lastKnownState
158
+ // for garbage-collected CoValues, not an empty state. This is important for peer
159
+ // reconciliation where we want to send the last known state to minimize data transfer.
160
+ const client = setupTestNode();
161
+ client.addStorage({ ourName: "client" });
162
+ client.node.enableGarbageCollector();
163
+ const group = client.node.createGroup();
164
+ const map = group.createMap();
165
+ map.set("hello", "world", "trusting");
166
+ // Sync to server
167
+ client.connectToSyncServer();
168
+ await client.node.syncManager.waitForAllCoValuesSync();
169
+ // Capture known state before GC
170
+ const originalKnownState = map.core.knownState();
171
+ const originalKnownStateWithStreaming = map.core.knownStateWithStreaming();
172
+ // For available CoValues, both should be equal (no streaming in progress)
173
+ expect(originalKnownState).toEqual(originalKnownStateWithStreaming);
174
+ expect(originalKnownState.header).toBe(true);
175
+ expect(Object.values(originalKnownState.sessions)[0]).toBe(1);
176
+ // Disconnect before GC
177
+ client.disconnect();
178
+ // Run GC to create garbageCollected shell
179
+ client.node.garbageCollector?.collect();
180
+ client.node.garbageCollector?.collect();
181
+ const gcCoValue = client.node.getCoValue(map.id);
182
+ expect(gcCoValue.loadingState).toBe("garbageCollected");
183
+ // Key assertion: knownStateWithStreaming() should return lastKnownState, not empty state
184
+ const gcKnownState = gcCoValue.knownState();
185
+ const gcKnownStateWithStreaming = gcCoValue.knownStateWithStreaming();
186
+ // Both should equal the original known state (the cached lastKnownState)
187
+ expect(gcKnownState).toEqual(originalKnownState);
188
+ expect(gcKnownStateWithStreaming).toEqual(originalKnownState);
189
+ // Specifically verify it's NOT an empty state
190
+ expect(gcKnownStateWithStreaming.header).toBe(true);
191
+ expect(Object.keys(gcKnownStateWithStreaming.sessions).length).toBeGreaterThan(0);
192
+ });
193
+ test("garbageCollected CoValues read from verified content after reload", async () => {
194
+ // This test verifies that after reloading a GC'd CoValue:
195
+ // 1. lastKnownState is cleared
196
+ // 2. knownState() returns data from verified content (not cached)
197
+ // We prove this by adding a transaction after reload and verifying knownState() updates
198
+ const client = setupTestNode();
199
+ client.addStorage({ ourName: "client" });
200
+ client.node.enableGarbageCollector();
201
+ const group = client.node.createGroup();
202
+ const map = group.createMap();
203
+ map.set("hello", "world", "trusting");
204
+ // Sync to server
205
+ client.connectToSyncServer();
206
+ await client.node.syncManager.waitForAllCoValuesSync();
207
+ // Capture known state before GC (has 1 transaction)
208
+ const originalKnownState = map.core.knownState();
209
+ const originalSessionCount = Object.values(originalKnownState.sessions)[0];
210
+ expect(originalSessionCount).toBe(1);
211
+ // Disconnect before GC
212
+ client.disconnect();
213
+ // Run GC to create garbageCollected shell
214
+ client.node.garbageCollector?.collect();
215
+ client.node.garbageCollector?.collect();
216
+ const gcMap = client.node.getCoValue(map.id);
217
+ expect(gcMap.loadingState).toBe("garbageCollected");
218
+ // Verify knownState() returns lastKnownState (still shows 1 transaction)
219
+ expect(gcMap.knownState()).toEqual(originalKnownState);
220
+ // Reconnect and reload
221
+ client.connectToSyncServer();
222
+ const reloadedCore = await client.node.loadCoValueCore(map.id);
223
+ // Verify CoValue is now available
224
+ expect(reloadedCore.loadingState).toBe("available");
225
+ expect(reloadedCore.isAvailable()).toBe(true);
226
+ // At this point, knownState() should be reading from verified content
227
+ // To prove this, we add a new transaction and verify knownState() updates
228
+ const reloadedContent = expectMap(reloadedCore.getCurrentContent());
229
+ reloadedContent.set("hello", "updated locally", "trusting");
230
+ // Verify knownState() now shows 2 transactions
231
+ // This proves we're reading from verified content, not cached lastKnownState
232
+ const newKnownState = reloadedCore.knownState();
233
+ const newSessionCount = Object.values(newKnownState.sessions)[0];
234
+ expect(newSessionCount).toBe(2);
235
+ expect(newKnownState).not.toEqual(originalKnownState);
236
+ });
153
237
  });
154
238
  //# sourceMappingURL=sync.garbageCollection.test.js.map