cojson 0.20.9 → 0.20.10
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 +20 -0
- package/dist/PeerState.d.ts +2 -2
- package/dist/PeerState.d.ts.map +1 -1
- package/dist/PeerState.js +3 -3
- package/dist/PeerState.js.map +1 -1
- package/dist/StorageReconciliationAckTracker.d.ts +14 -0
- package/dist/StorageReconciliationAckTracker.d.ts.map +1 -0
- package/dist/StorageReconciliationAckTracker.js +72 -0
- package/dist/StorageReconciliationAckTracker.js.map +1 -0
- package/dist/SyncStateManager.js +2 -2
- package/dist/SyncStateManager.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +2 -1
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +43 -10
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValues/coList.d.ts +2 -0
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coList.js +28 -0
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/group.d.ts +4 -1
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +15 -1
- package/dist/coValues/group.js.map +1 -1
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +14 -0
- package/dist/config.js.map +1 -1
- package/dist/exports.d.ts +9 -1
- package/dist/exports.d.ts.map +1 -1
- package/dist/exports.js +5 -1
- package/dist/exports.js.map +1 -1
- package/dist/localNode.d.ts +7 -3
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +13 -5
- package/dist/localNode.js.map +1 -1
- package/dist/permissions.d.ts +1 -0
- package/dist/permissions.d.ts.map +1 -1
- package/dist/queue/LinkedList.d.ts +2 -0
- package/dist/queue/LinkedList.d.ts.map +1 -1
- package/dist/queue/LinkedList.js +7 -0
- package/dist/queue/LinkedList.js.map +1 -1
- package/dist/queue/OutgoingLoadQueue.d.ts +4 -1
- package/dist/queue/OutgoingLoadQueue.d.ts.map +1 -1
- package/dist/queue/OutgoingLoadQueue.js +41 -13
- package/dist/queue/OutgoingLoadQueue.js.map +1 -1
- package/dist/queue/PriorityBasedMessageQueue.d.ts +1 -0
- package/dist/queue/PriorityBasedMessageQueue.d.ts.map +1 -1
- package/dist/queue/PriorityBasedMessageQueue.js +11 -1
- package/dist/queue/PriorityBasedMessageQueue.js.map +1 -1
- package/dist/storage/knownState.d.ts +2 -0
- package/dist/storage/knownState.d.ts.map +1 -1
- package/dist/storage/knownState.js +11 -0
- package/dist/storage/knownState.js.map +1 -1
- package/dist/storage/sqlite/client.d.ts +10 -1
- package/dist/storage/sqlite/client.d.ts.map +1 -1
- package/dist/storage/sqlite/client.js +84 -0
- package/dist/storage/sqlite/client.js.map +1 -1
- package/dist/storage/sqlite/sqliteMigrations.d.ts.map +1 -1
- package/dist/storage/sqlite/sqliteMigrations.js +11 -0
- package/dist/storage/sqlite/sqliteMigrations.js.map +1 -1
- package/dist/storage/sqliteAsync/client.d.ts +10 -1
- package/dist/storage/sqliteAsync/client.d.ts.map +1 -1
- package/dist/storage/sqliteAsync/client.js +86 -0
- package/dist/storage/sqliteAsync/client.js.map +1 -1
- package/dist/storage/storageAsync.d.ts +9 -2
- package/dist/storage/storageAsync.d.ts.map +1 -1
- package/dist/storage/storageAsync.js +19 -0
- package/dist/storage/storageAsync.js.map +1 -1
- package/dist/storage/storageSync.d.ts +9 -2
- package/dist/storage/storageSync.d.ts.map +1 -1
- package/dist/storage/storageSync.js +20 -13
- package/dist/storage/storageSync.js.map +1 -1
- package/dist/storage/types.d.ts +64 -0
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js.map +1 -1
- package/dist/sync.d.ts +44 -2
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +268 -44
- package/dist/sync.js.map +1 -1
- package/dist/tests/OutgoingLoadQueue.test.js +137 -39
- package/dist/tests/OutgoingLoadQueue.test.js.map +1 -1
- package/dist/tests/SQLiteClientAsync.test.js +1 -1
- package/dist/tests/SQLiteClientAsync.test.js.map +1 -1
- package/dist/tests/StorageApiAsync.test.js +138 -0
- package/dist/tests/StorageApiAsync.test.js.map +1 -1
- package/dist/tests/StorageApiSync.test.js +154 -0
- package/dist/tests/StorageApiSync.test.js.map +1 -1
- package/dist/tests/StorageReconciliationAckTracker.test.d.ts +2 -0
- package/dist/tests/StorageReconciliationAckTracker.test.d.ts.map +1 -0
- package/dist/tests/StorageReconciliationAckTracker.test.js +74 -0
- package/dist/tests/StorageReconciliationAckTracker.test.js.map +1 -0
- package/dist/tests/SyncStateManager.test.js +18 -0
- package/dist/tests/SyncStateManager.test.js.map +1 -1
- package/dist/tests/coList.test.js +112 -1
- package/dist/tests/coList.test.js.map +1 -1
- package/dist/tests/coValueCore.loadFromStorage.test.js +36 -0
- package/dist/tests/coValueCore.loadFromStorage.test.js.map +1 -1
- package/dist/tests/group.test.js +44 -0
- package/dist/tests/group.test.js.map +1 -1
- package/dist/tests/knownState.lazyLoading.test.js +6 -0
- package/dist/tests/knownState.lazyLoading.test.js.map +1 -1
- package/dist/tests/messagesTestUtils.d.ts.map +1 -1
- package/dist/tests/messagesTestUtils.js +4 -0
- package/dist/tests/messagesTestUtils.js.map +1 -1
- package/dist/tests/sync.concurrentLoad.test.js +333 -1
- package/dist/tests/sync.concurrentLoad.test.js.map +1 -1
- package/dist/tests/sync.garbageCollection.test.js +4 -0
- package/dist/tests/sync.garbageCollection.test.js.map +1 -1
- package/dist/tests/sync.load.test.js +19 -0
- package/dist/tests/sync.load.test.js.map +1 -1
- package/dist/tests/sync.mesh.test.js +1 -0
- package/dist/tests/sync.mesh.test.js.map +1 -1
- package/dist/tests/sync.multipleServers.test.js +41 -3
- package/dist/tests/sync.multipleServers.test.js.map +1 -1
- package/dist/tests/sync.storage.test.js +2 -0
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.storageAsync.test.js +1 -0
- package/dist/tests/sync.storageAsync.test.js.map +1 -1
- package/dist/tests/sync.storageReconciliation.test.d.ts +2 -0
- package/dist/tests/sync.storageReconciliation.test.d.ts.map +1 -0
- package/dist/tests/sync.storageReconciliation.test.js +501 -0
- package/dist/tests/sync.storageReconciliation.test.js.map +1 -0
- package/dist/tests/testUtils.d.ts +1 -0
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +3 -2
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +4 -4
- package/src/PeerState.ts +10 -3
- package/src/StorageReconciliationAckTracker.ts +83 -0
- package/src/SyncStateManager.ts +3 -3
- package/src/coValueCore/coValueCore.ts +47 -16
- package/src/coValues/coList.ts +23 -0
- package/src/coValues/group.ts +18 -0
- package/src/config.ts +18 -0
- package/src/exports.ts +8 -0
- package/src/localNode.ts +18 -0
- package/src/permissions.ts +1 -1
- package/src/queue/LinkedList.ts +10 -0
- package/src/queue/OutgoingLoadQueue.ts +57 -15
- package/src/queue/PriorityBasedMessageQueue.ts +15 -1
- package/src/storage/knownState.ts +14 -0
- package/src/storage/sqlite/client.ts +128 -0
- package/src/storage/sqlite/sqliteMigrations.ts +11 -0
- package/src/storage/sqliteAsync/client.ts +139 -0
- package/src/storage/storageAsync.ts +37 -0
- package/src/storage/storageSync.ts +41 -16
- package/src/storage/types.ts +110 -0
- package/src/sync.ts +311 -14
- package/src/tests/OutgoingLoadQueue.test.ts +226 -59
- package/src/tests/SQLiteClientAsync.test.ts +1 -1
- package/src/tests/StorageApiAsync.test.ts +161 -1
- package/src/tests/StorageApiSync.test.ts +176 -0
- package/src/tests/StorageReconciliationAckTracker.test.ts +99 -0
- package/src/tests/SyncStateManager.test.ts +25 -0
- package/src/tests/coList.test.ts +138 -0
- package/src/tests/coValueCore.loadFromStorage.test.ts +72 -1
- package/src/tests/group.test.ts +87 -0
- package/src/tests/knownState.lazyLoading.test.ts +36 -1
- package/src/tests/messagesTestUtils.ts +4 -0
- package/src/tests/sync.concurrentLoad.test.ts +491 -0
- package/src/tests/sync.garbageCollection.test.ts +4 -0
- package/src/tests/sync.load.test.ts +26 -0
- package/src/tests/sync.mesh.test.ts +1 -0
- package/src/tests/sync.multipleServers.test.ts +60 -2
- package/src/tests/sync.storage.test.ts +2 -0
- package/src/tests/sync.storageAsync.test.ts +1 -0
- package/src/tests/sync.storageReconciliation.test.ts +697 -0
- package/src/tests/testUtils.ts +10 -1
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
afterEach,
|
|
3
|
+
beforeEach,
|
|
4
|
+
describe,
|
|
5
|
+
expect,
|
|
6
|
+
onTestFinished,
|
|
7
|
+
test,
|
|
8
|
+
vi,
|
|
9
|
+
} from "vitest";
|
|
2
10
|
import {
|
|
3
11
|
CO_VALUE_LOADING_CONFIG,
|
|
4
12
|
setMaxInFlightLoadsPerPeer,
|
|
@@ -195,6 +203,26 @@ describe("OutgoingLoadQueue", () => {
|
|
|
195
203
|
expect(duplicateCallbackCount).toBe(0);
|
|
196
204
|
});
|
|
197
205
|
|
|
206
|
+
test("should skip duplicate enqueue for same CoValue ID while in-flight", () => {
|
|
207
|
+
const queue = new OutgoingLoadQueue(TEST_PEER_ID);
|
|
208
|
+
const node = createTestNode();
|
|
209
|
+
const otherNode = createTestNode();
|
|
210
|
+
const group = node.createGroup();
|
|
211
|
+
|
|
212
|
+
const map = group.createMap();
|
|
213
|
+
const sameIdCoValue = otherNode.getCoValue(map.id);
|
|
214
|
+
|
|
215
|
+
let duplicateCallbackCount = 0;
|
|
216
|
+
|
|
217
|
+
queue.enqueue(map.core, () => {});
|
|
218
|
+
queue.enqueue(sameIdCoValue, () => {
|
|
219
|
+
duplicateCallbackCount += 1;
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
expect(queue.inFlightCount).toBe(1);
|
|
223
|
+
expect(duplicateCallbackCount).toBe(0);
|
|
224
|
+
});
|
|
225
|
+
|
|
198
226
|
test("should skip duplicate enqueue for same CoValue ID", () => {
|
|
199
227
|
setMaxInFlightLoadsPerPeer(1);
|
|
200
228
|
const queue = new OutgoingLoadQueue(TEST_PEER_ID);
|
|
@@ -417,6 +445,34 @@ describe("OutgoingLoadQueue", () => {
|
|
|
417
445
|
expect(queue.inFlightCount).toBe(1);
|
|
418
446
|
});
|
|
419
447
|
|
|
448
|
+
test("should complete in-flight request by CoValue ID even with different instance", () => {
|
|
449
|
+
setMaxInFlightLoadsPerPeer(1);
|
|
450
|
+
const queue = new OutgoingLoadQueue(TEST_PEER_ID);
|
|
451
|
+
const node = createTestNode();
|
|
452
|
+
const otherNode = createTestNode();
|
|
453
|
+
const group = node.createGroup();
|
|
454
|
+
|
|
455
|
+
const map = group.createMap();
|
|
456
|
+
const nextMap = group.createMap();
|
|
457
|
+
const sameIdCoValue = otherNode.getCoValue(map.id);
|
|
458
|
+
|
|
459
|
+
let nextCallbackCalled = false;
|
|
460
|
+
|
|
461
|
+
queue.enqueue(map.core, () => {});
|
|
462
|
+
queue.enqueue(nextMap.core, () => {
|
|
463
|
+
nextCallbackCalled = true;
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
expect(queue.inFlightCount).toBe(1);
|
|
467
|
+
expect(nextCallbackCalled).toBe(false);
|
|
468
|
+
|
|
469
|
+
// Complete using a different CoValue instance with the same ID.
|
|
470
|
+
queue.trackComplete(sameIdCoValue);
|
|
471
|
+
|
|
472
|
+
expect(nextCallbackCalled).toBe(true);
|
|
473
|
+
expect(queue.inFlightCount).toBe(1);
|
|
474
|
+
});
|
|
475
|
+
|
|
420
476
|
test("should allow re-enqueue after completion", () => {
|
|
421
477
|
setMaxInFlightLoadsPerPeer(1);
|
|
422
478
|
const queue = new OutgoingLoadQueue(TEST_PEER_ID);
|
|
@@ -445,7 +501,7 @@ describe("OutgoingLoadQueue", () => {
|
|
|
445
501
|
expect(secondCallbackCount).toBe(1);
|
|
446
502
|
});
|
|
447
503
|
|
|
448
|
-
test("should not complete if CoValue is streaming", () => {
|
|
504
|
+
test("should not complete if CoValue is streaming for content updates", () => {
|
|
449
505
|
setMaxInFlightLoadsPerPeer(1);
|
|
450
506
|
const queue = new OutgoingLoadQueue(TEST_PEER_ID);
|
|
451
507
|
const node = createTestNode();
|
|
@@ -468,7 +524,7 @@ describe("OutgoingLoadQueue", () => {
|
|
|
468
524
|
expect(callback2Called).toBe(false);
|
|
469
525
|
|
|
470
526
|
// trackComplete should not complete because map1 is streaming
|
|
471
|
-
queue.trackComplete(map1.core);
|
|
527
|
+
queue.trackComplete(map1.core, "content");
|
|
472
528
|
|
|
473
529
|
// Should still be in-flight and pending should not be processed
|
|
474
530
|
expect(queue.inFlightCount).toBe(1);
|
|
@@ -479,7 +535,35 @@ describe("OutgoingLoadQueue", () => {
|
|
|
479
535
|
vi.spyOn(map1.core, "isStreaming").mockReturnValue(false);
|
|
480
536
|
|
|
481
537
|
// Now trackComplete should work
|
|
482
|
-
queue.trackComplete(map1.core);
|
|
538
|
+
queue.trackComplete(map1.core, "content");
|
|
539
|
+
|
|
540
|
+
expect(queue.inFlightCount).toBe(1); // map2 is now in-flight
|
|
541
|
+
expect(callback2Called).toBe(true);
|
|
542
|
+
expect(queue.pendingCount).toBe(0);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
test("should complete if CoValue is streaming for known-state updates", () => {
|
|
546
|
+
setMaxInFlightLoadsPerPeer(1);
|
|
547
|
+
const queue = new OutgoingLoadQueue(TEST_PEER_ID);
|
|
548
|
+
const node = createTestNode();
|
|
549
|
+
const group = node.createGroup();
|
|
550
|
+
|
|
551
|
+
const map1 = group.createMap();
|
|
552
|
+
const map2 = group.createMap();
|
|
553
|
+
|
|
554
|
+
vi.spyOn(map1.core, "isStreaming").mockReturnValue(true);
|
|
555
|
+
|
|
556
|
+
let callback2Called = false;
|
|
557
|
+
|
|
558
|
+
queue.enqueue(map1.core, () => {});
|
|
559
|
+
queue.enqueue(map2.core, () => {
|
|
560
|
+
callback2Called = true;
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
expect(queue.inFlightCount).toBe(1);
|
|
564
|
+
expect(callback2Called).toBe(false);
|
|
565
|
+
|
|
566
|
+
queue.trackComplete(map1.core, "known");
|
|
483
567
|
|
|
484
568
|
expect(queue.inFlightCount).toBe(1); // map2 is now in-flight
|
|
485
569
|
expect(callback2Called).toBe(true);
|
|
@@ -731,62 +815,145 @@ describe("OutgoingLoadQueue", () => {
|
|
|
731
815
|
|
|
732
816
|
test("should balance push/pull metrics when clearing pending items", async () => {
|
|
733
817
|
const metricReader = createTestMetricReader();
|
|
818
|
+
onTestFinished(tearDownTestMetricReader);
|
|
734
819
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
820
|
+
setMaxInFlightLoadsPerPeer(1);
|
|
821
|
+
const queue = new OutgoingLoadQueue(TEST_PEER_ID);
|
|
822
|
+
const node = createTestNode();
|
|
823
|
+
const group = node.createGroup();
|
|
824
|
+
|
|
825
|
+
// Block the queue first (this item is pushed then immediately shifted to go in-flight)
|
|
826
|
+
const blockerMap = group.createMap();
|
|
827
|
+
queue.enqueue(blockerMap.core, () => {});
|
|
828
|
+
|
|
829
|
+
// Enqueue multiple items that will be pending
|
|
830
|
+
const map1 = group.createMap();
|
|
831
|
+
const map2 = group.createMap();
|
|
832
|
+
const map3 = group.createMap();
|
|
833
|
+
|
|
834
|
+
queue.enqueue(map1.core, () => {});
|
|
835
|
+
queue.enqueue(map2.core, () => {});
|
|
836
|
+
queue.enqueue(map3.core, () => {});
|
|
837
|
+
|
|
838
|
+
expect(queue.pendingCount).toBe(3);
|
|
839
|
+
|
|
840
|
+
// Get metrics before clear
|
|
841
|
+
// 4 items pushed (blocker + 3 pending), 1 pulled (blocker was shifted to go in-flight)
|
|
842
|
+
const pushedBefore = await metricReader.getMetricValue(
|
|
843
|
+
"jazz.messagequeue.load-requests-queue.pushed",
|
|
844
|
+
{ priority: "high" },
|
|
845
|
+
);
|
|
846
|
+
const pulledBefore = await metricReader.getMetricValue(
|
|
847
|
+
"jazz.messagequeue.load-requests-queue.pulled",
|
|
848
|
+
{ priority: "high" },
|
|
849
|
+
);
|
|
850
|
+
|
|
851
|
+
expect(pushedBefore).toBe(4);
|
|
852
|
+
expect(pulledBefore).toBe(1);
|
|
853
|
+
|
|
854
|
+
// Clear the queue - this should drain pending items, incrementing pulled counter
|
|
855
|
+
queue.clear();
|
|
856
|
+
|
|
857
|
+
// Get metrics after clear - pushed and pulled should now be equal
|
|
858
|
+
const pushedAfter = await metricReader.getMetricValue(
|
|
859
|
+
"jazz.messagequeue.load-requests-queue.pushed",
|
|
860
|
+
{ priority: "high" },
|
|
861
|
+
);
|
|
862
|
+
const pulledAfter = await metricReader.getMetricValue(
|
|
863
|
+
"jazz.messagequeue.load-requests-queue.pulled",
|
|
864
|
+
{ priority: "high" },
|
|
865
|
+
);
|
|
866
|
+
|
|
867
|
+
// All 4 pushed items should now be pulled (balanced)
|
|
868
|
+
expect(pushedAfter).toBe(4);
|
|
869
|
+
expect(pulledAfter).toBe(4);
|
|
870
|
+
expect(pushedAfter).toBe(pulledAfter);
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
test("should track in-flight loads with OpenTelemetry", async () => {
|
|
874
|
+
const metricReader = createTestMetricReader();
|
|
875
|
+
onTestFinished(tearDownTestMetricReader);
|
|
876
|
+
|
|
877
|
+
setMaxInFlightLoadsPerPeer(2);
|
|
878
|
+
const queue = new OutgoingLoadQueue(TEST_PEER_ID);
|
|
879
|
+
const node = createTestNode();
|
|
880
|
+
const group = node.createGroup();
|
|
881
|
+
|
|
882
|
+
const map1 = group.createMap();
|
|
883
|
+
const map2 = group.createMap();
|
|
884
|
+
|
|
885
|
+
expect(
|
|
886
|
+
await metricReader.getMetricValue("jazz.loadqueue.outgoing.inflight"),
|
|
887
|
+
).toBe(0);
|
|
888
|
+
|
|
889
|
+
queue.enqueue(map1.core, () => {});
|
|
890
|
+
expect(
|
|
891
|
+
await metricReader.getMetricValue("jazz.loadqueue.outgoing.inflight"),
|
|
892
|
+
).toBe(1);
|
|
893
|
+
|
|
894
|
+
queue.enqueue(map2.core, () => {});
|
|
895
|
+
expect(
|
|
896
|
+
await metricReader.getMetricValue("jazz.loadqueue.outgoing.inflight"),
|
|
897
|
+
).toBe(2);
|
|
898
|
+
|
|
899
|
+
queue.trackComplete(map1.core);
|
|
900
|
+
expect(
|
|
901
|
+
await metricReader.getMetricValue("jazz.loadqueue.outgoing.inflight"),
|
|
902
|
+
).toBe(1);
|
|
903
|
+
|
|
904
|
+
queue.clear();
|
|
905
|
+
expect(
|
|
906
|
+
await metricReader.getMetricValue("jazz.loadqueue.outgoing.inflight"),
|
|
907
|
+
).toBe(0);
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
test("should decrement in-flight metric when a load times out", async () => {
|
|
911
|
+
CO_VALUE_LOADING_CONFIG.TIMEOUT = 5;
|
|
912
|
+
|
|
913
|
+
const metricReader = createTestMetricReader();
|
|
914
|
+
onTestFinished(tearDownTestMetricReader);
|
|
915
|
+
|
|
916
|
+
const queue = new OutgoingLoadQueue(TEST_PEER_ID);
|
|
917
|
+
const node = createTestNode();
|
|
918
|
+
const coValue = node.getCoValue("co_zTestMetricTimeout00001" as any);
|
|
919
|
+
|
|
920
|
+
queue.enqueue(coValue, () => {});
|
|
921
|
+
expect(
|
|
922
|
+
await metricReader.getMetricValue("jazz.loadqueue.outgoing.inflight"),
|
|
923
|
+
).toBe(1);
|
|
924
|
+
|
|
925
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
926
|
+
expect(
|
|
927
|
+
await metricReader.getMetricValue("jazz.loadqueue.outgoing.inflight"),
|
|
928
|
+
).toBe(0);
|
|
929
|
+
});
|
|
930
|
+
|
|
931
|
+
test("should not change in-flight metric when completing unknown CoValue", async () => {
|
|
932
|
+
const metricReader = createTestMetricReader();
|
|
933
|
+
onTestFinished(tearDownTestMetricReader);
|
|
934
|
+
|
|
935
|
+
const queue = new OutgoingLoadQueue(TEST_PEER_ID);
|
|
936
|
+
const node = createTestNode();
|
|
937
|
+
const group = node.createGroup();
|
|
938
|
+
const knownMap = group.createMap();
|
|
939
|
+
const unknownCoValue = node.getCoValue(
|
|
940
|
+
"co_zTestUnknownMetric0001" as any,
|
|
941
|
+
);
|
|
942
|
+
|
|
943
|
+
queue.enqueue(knownMap.core, () => {});
|
|
944
|
+
expect(
|
|
945
|
+
await metricReader.getMetricValue("jazz.loadqueue.outgoing.inflight"),
|
|
946
|
+
).toBe(1);
|
|
947
|
+
|
|
948
|
+
queue.trackComplete(unknownCoValue);
|
|
949
|
+
expect(
|
|
950
|
+
await metricReader.getMetricValue("jazz.loadqueue.outgoing.inflight"),
|
|
951
|
+
).toBe(1);
|
|
952
|
+
|
|
953
|
+
queue.trackComplete(knownMap.core);
|
|
954
|
+
expect(
|
|
955
|
+
await metricReader.getMetricValue("jazz.loadqueue.outgoing.inflight"),
|
|
956
|
+
).toBe(0);
|
|
790
957
|
});
|
|
791
958
|
});
|
|
792
959
|
|
|
@@ -36,7 +36,7 @@ describe("SQLiteClientAsync", () => {
|
|
|
36
36
|
|
|
37
37
|
const signatures = await dbClient.getSignatures(0, 0);
|
|
38
38
|
expect(signatures.length).toBe(10);
|
|
39
|
-
signatures.forEach(
|
|
39
|
+
signatures.forEach(({ signature }, i) => {
|
|
40
40
|
expect(signature).toBe(`signature_z${i}`);
|
|
41
41
|
});
|
|
42
42
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { afterEach, describe, expect, test, vi } from "vitest";
|
|
2
|
-
import { CoID, RawCoMap, logger } from "../exports.js";
|
|
2
|
+
import { CoID, RawCoID, RawCoMap, logger } from "../exports.js";
|
|
3
3
|
import { CoValueCore } from "../exports.js";
|
|
4
4
|
import { NewContentMessage } from "../sync.js";
|
|
5
5
|
import {
|
|
@@ -1413,4 +1413,164 @@ describe("StorageApiAsync", () => {
|
|
|
1413
1413
|
expect(dbClientSpy).toHaveBeenCalledTimes(0);
|
|
1414
1414
|
});
|
|
1415
1415
|
});
|
|
1416
|
+
|
|
1417
|
+
describe("getCoValueIDs", () => {
|
|
1418
|
+
test("should return empty array when storage has no CoValues", async () => {
|
|
1419
|
+
const client = setupTestNode();
|
|
1420
|
+
const { storage } = await client.addAsyncStorage({
|
|
1421
|
+
ourName: "test",
|
|
1422
|
+
storageName: "test-storage",
|
|
1423
|
+
});
|
|
1424
|
+
|
|
1425
|
+
const ids = await new Promise<{ id: RawCoID }[]>((resolve) => {
|
|
1426
|
+
storage.getCoValueIDs(100, 0, resolve);
|
|
1427
|
+
});
|
|
1428
|
+
|
|
1429
|
+
expect(ids).toEqual([]);
|
|
1430
|
+
});
|
|
1431
|
+
|
|
1432
|
+
test("should return CoValue IDs in batch after storing CoValues", async () => {
|
|
1433
|
+
const dbPath = getDbPath();
|
|
1434
|
+
const fixtures = setupTestNode();
|
|
1435
|
+
await fixtures.addAsyncStorage({
|
|
1436
|
+
ourName: "test",
|
|
1437
|
+
storageName: "test-storage",
|
|
1438
|
+
filename: dbPath,
|
|
1439
|
+
});
|
|
1440
|
+
|
|
1441
|
+
const client = setupTestNode();
|
|
1442
|
+
const { storage } = await client.addAsyncStorage({
|
|
1443
|
+
ourName: "test",
|
|
1444
|
+
storageName: "test-storage",
|
|
1445
|
+
filename: dbPath,
|
|
1446
|
+
});
|
|
1447
|
+
|
|
1448
|
+
// Create CoValues and sync to storage
|
|
1449
|
+
const group = fixtures.node.createGroup();
|
|
1450
|
+
group.addMember("everyone", "reader");
|
|
1451
|
+
const map = group.createMap();
|
|
1452
|
+
map.set("key", "value", "trusting");
|
|
1453
|
+
await map.core.waitForSync();
|
|
1454
|
+
|
|
1455
|
+
const ids = await new Promise<{ id: RawCoID }[]>((resolve) => {
|
|
1456
|
+
storage.getCoValueIDs(100, 0, resolve);
|
|
1457
|
+
});
|
|
1458
|
+
|
|
1459
|
+
expect(ids.map((e) => e.id)).toContain(group.id);
|
|
1460
|
+
expect(ids.map((e) => e.id)).toContain(map.id);
|
|
1461
|
+
expect(ids.length).toEqual(2);
|
|
1462
|
+
});
|
|
1463
|
+
|
|
1464
|
+
test("should paginate when there are more CoValues than the limit and return each ID only once", async () => {
|
|
1465
|
+
const dbPath = getDbPath();
|
|
1466
|
+
const fixtures = setupTestNode();
|
|
1467
|
+
await fixtures.addAsyncStorage({
|
|
1468
|
+
ourName: "test",
|
|
1469
|
+
storageName: "test-storage",
|
|
1470
|
+
filename: dbPath,
|
|
1471
|
+
});
|
|
1472
|
+
|
|
1473
|
+
const client = setupTestNode();
|
|
1474
|
+
const { storage } = await client.addAsyncStorage({
|
|
1475
|
+
ourName: "test",
|
|
1476
|
+
storageName: "test-storage",
|
|
1477
|
+
filename: dbPath,
|
|
1478
|
+
});
|
|
1479
|
+
|
|
1480
|
+
// Create more CoValues than the page size (1 group + 4 maps = 5 CoValues, limit = 2)
|
|
1481
|
+
const group = fixtures.node.createGroup();
|
|
1482
|
+
group.addMember("everyone", "reader");
|
|
1483
|
+
const expectedIds = new Set<RawCoID>([group.id]);
|
|
1484
|
+
const maps: ReturnType<typeof group.createMap>[] = [];
|
|
1485
|
+
for (let i = 0; i < 4; i++) {
|
|
1486
|
+
const map = group.createMap();
|
|
1487
|
+
map.set(`key${i}`, `value${i}`, "trusting");
|
|
1488
|
+
maps.push(map);
|
|
1489
|
+
expectedIds.add(map.id);
|
|
1490
|
+
}
|
|
1491
|
+
await maps[maps.length - 1]!.core.waitForSync();
|
|
1492
|
+
|
|
1493
|
+
const limit = 2;
|
|
1494
|
+
const allIds: RawCoID[] = [];
|
|
1495
|
+
await new Promise<void>((resolve) => {
|
|
1496
|
+
const fetchBatch = (offset: number) => {
|
|
1497
|
+
storage.getCoValueIDs(limit, offset, (batch) => {
|
|
1498
|
+
for (const { id } of batch) {
|
|
1499
|
+
allIds.push(id);
|
|
1500
|
+
}
|
|
1501
|
+
if (batch.length >= limit) {
|
|
1502
|
+
fetchBatch(offset + batch.length);
|
|
1503
|
+
} else {
|
|
1504
|
+
resolve();
|
|
1505
|
+
}
|
|
1506
|
+
});
|
|
1507
|
+
};
|
|
1508
|
+
fetchBatch(0);
|
|
1509
|
+
});
|
|
1510
|
+
|
|
1511
|
+
expect(allIds).toHaveLength(expectedIds.size);
|
|
1512
|
+
const seen = new Set<RawCoID>();
|
|
1513
|
+
for (const id of allIds) {
|
|
1514
|
+
expect(seen.has(id)).toBe(false);
|
|
1515
|
+
seen.add(id);
|
|
1516
|
+
expect(expectedIds.has(id)).toBe(true);
|
|
1517
|
+
}
|
|
1518
|
+
});
|
|
1519
|
+
});
|
|
1520
|
+
|
|
1521
|
+
describe("getCoValueCount", () => {
|
|
1522
|
+
test("should return 0 when storage has no CoValues", async () => {
|
|
1523
|
+
const client = setupTestNode();
|
|
1524
|
+
const { storage } = await client.addAsyncStorage({
|
|
1525
|
+
ourName: "test",
|
|
1526
|
+
storageName: "test-storage",
|
|
1527
|
+
});
|
|
1528
|
+
|
|
1529
|
+
const count = await new Promise<number>((resolve) => {
|
|
1530
|
+
storage.getCoValueCount(resolve);
|
|
1531
|
+
});
|
|
1532
|
+
|
|
1533
|
+
expect(count).toBe(0);
|
|
1534
|
+
});
|
|
1535
|
+
|
|
1536
|
+
test("should return CoValue count after storing CoValues", async () => {
|
|
1537
|
+
const dbPath = getDbPath();
|
|
1538
|
+
const fixtures = setupTestNode();
|
|
1539
|
+
await fixtures.addAsyncStorage({
|
|
1540
|
+
ourName: "test",
|
|
1541
|
+
storageName: "test-storage",
|
|
1542
|
+
filename: dbPath,
|
|
1543
|
+
});
|
|
1544
|
+
|
|
1545
|
+
const client = setupTestNode();
|
|
1546
|
+
const { storage } = await client.addAsyncStorage({
|
|
1547
|
+
ourName: "test",
|
|
1548
|
+
storageName: "test-storage",
|
|
1549
|
+
filename: dbPath,
|
|
1550
|
+
});
|
|
1551
|
+
|
|
1552
|
+
const countEmpty = await new Promise<number>((resolve) => {
|
|
1553
|
+
storage.getCoValueCount(resolve);
|
|
1554
|
+
});
|
|
1555
|
+
expect(countEmpty).toBe(0);
|
|
1556
|
+
|
|
1557
|
+
const group = fixtures.node.createGroup();
|
|
1558
|
+
group.addMember("everyone", "reader");
|
|
1559
|
+
await group.core.waitForSync();
|
|
1560
|
+
|
|
1561
|
+
const countOne = await new Promise<number>((resolve) => {
|
|
1562
|
+
storage.getCoValueCount(resolve);
|
|
1563
|
+
});
|
|
1564
|
+
expect(countOne).toBe(1);
|
|
1565
|
+
|
|
1566
|
+
const map = group.createMap();
|
|
1567
|
+
map.set("key", "value", "trusting");
|
|
1568
|
+
await map.core.waitForSync();
|
|
1569
|
+
|
|
1570
|
+
const countTwo = await new Promise<number>((resolve) => {
|
|
1571
|
+
storage.getCoValueCount(resolve);
|
|
1572
|
+
});
|
|
1573
|
+
expect(countTwo).toBe(2);
|
|
1574
|
+
});
|
|
1575
|
+
});
|
|
1416
1576
|
});
|
|
@@ -1237,4 +1237,180 @@ describe("StorageApiSync", () => {
|
|
|
1237
1237
|
expect(result2).toEqual(result1);
|
|
1238
1238
|
});
|
|
1239
1239
|
});
|
|
1240
|
+
|
|
1241
|
+
describe("getCoValueIDs", () => {
|
|
1242
|
+
test("should return empty array when storage has no CoValues", async () => {
|
|
1243
|
+
const client = setupTestNode();
|
|
1244
|
+
const { storage } = client.addStorage({
|
|
1245
|
+
storage: createSyncStorage({
|
|
1246
|
+
nodeName: "test",
|
|
1247
|
+
storageName: "test-storage",
|
|
1248
|
+
}),
|
|
1249
|
+
});
|
|
1250
|
+
|
|
1251
|
+
const ids = await new Promise<{ id: RawCoID }[]>((resolve) => {
|
|
1252
|
+
storage.getCoValueIDs(100, 0, resolve);
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1255
|
+
expect(ids).toEqual([]);
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
test("should return CoValue IDs in batch after storing CoValues", async () => {
|
|
1259
|
+
const dbPath = getDbPath();
|
|
1260
|
+
const fixtures = setupTestNode();
|
|
1261
|
+
fixtures.addStorage({
|
|
1262
|
+
storage: createSyncStorage({
|
|
1263
|
+
filename: dbPath,
|
|
1264
|
+
nodeName: "test",
|
|
1265
|
+
storageName: "test-storage",
|
|
1266
|
+
}),
|
|
1267
|
+
});
|
|
1268
|
+
|
|
1269
|
+
const client = setupTestNode();
|
|
1270
|
+
const { storage } = client.addStorage({
|
|
1271
|
+
storage: createSyncStorage({
|
|
1272
|
+
filename: dbPath,
|
|
1273
|
+
nodeName: "test",
|
|
1274
|
+
storageName: "test-storage",
|
|
1275
|
+
}),
|
|
1276
|
+
});
|
|
1277
|
+
|
|
1278
|
+
// Create CoValues and sync to storage
|
|
1279
|
+
const group = fixtures.node.createGroup();
|
|
1280
|
+
group.addMember("everyone", "reader");
|
|
1281
|
+
const map = group.createMap();
|
|
1282
|
+
map.set("key", "value", "trusting");
|
|
1283
|
+
await map.core.waitForSync();
|
|
1284
|
+
|
|
1285
|
+
const ids = await new Promise<{ id: RawCoID }[]>((resolve) => {
|
|
1286
|
+
storage.getCoValueIDs(100, 0, resolve);
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
expect(ids.map((e) => e.id)).toContain(group.id);
|
|
1290
|
+
expect(ids.map((e) => e.id)).toContain(map.id);
|
|
1291
|
+
expect(ids.length).toEqual(2);
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
test("should paginate when there are more CoValues than the limit and return each ID only once", async () => {
|
|
1295
|
+
const dbPath = getDbPath();
|
|
1296
|
+
const fixtures = setupTestNode();
|
|
1297
|
+
fixtures.addStorage({
|
|
1298
|
+
storage: createSyncStorage({
|
|
1299
|
+
filename: dbPath,
|
|
1300
|
+
nodeName: "test",
|
|
1301
|
+
storageName: "test-storage",
|
|
1302
|
+
}),
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
const client = setupTestNode();
|
|
1306
|
+
const { storage } = client.addStorage({
|
|
1307
|
+
storage: createSyncStorage({
|
|
1308
|
+
filename: dbPath,
|
|
1309
|
+
nodeName: "test",
|
|
1310
|
+
storageName: "test-storage",
|
|
1311
|
+
}),
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
// Create more CoValues than the page size (1 group + 4 maps = 5 CoValues, limit = 2)
|
|
1315
|
+
const group = fixtures.node.createGroup();
|
|
1316
|
+
group.addMember("everyone", "reader");
|
|
1317
|
+
const expectedIds = new Set<RawCoID>([group.id]);
|
|
1318
|
+
const maps: ReturnType<typeof group.createMap>[] = [];
|
|
1319
|
+
for (let i = 0; i < 4; i++) {
|
|
1320
|
+
const map = group.createMap();
|
|
1321
|
+
map.set(`key${i}`, `value${i}`, "trusting");
|
|
1322
|
+
maps.push(map);
|
|
1323
|
+
expectedIds.add(map.id);
|
|
1324
|
+
}
|
|
1325
|
+
await maps[maps.length - 1]!.core.waitForSync();
|
|
1326
|
+
|
|
1327
|
+
const limit = 2;
|
|
1328
|
+
const allIds: RawCoID[] = [];
|
|
1329
|
+
await new Promise<void>((resolve) => {
|
|
1330
|
+
const fetchBatch = (offset: number) => {
|
|
1331
|
+
storage.getCoValueIDs(limit, offset, (batch) => {
|
|
1332
|
+
for (const { id } of batch) {
|
|
1333
|
+
allIds.push(id);
|
|
1334
|
+
}
|
|
1335
|
+
if (batch.length >= limit) {
|
|
1336
|
+
fetchBatch(offset + batch.length);
|
|
1337
|
+
} else {
|
|
1338
|
+
resolve();
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1341
|
+
};
|
|
1342
|
+
fetchBatch(0);
|
|
1343
|
+
});
|
|
1344
|
+
|
|
1345
|
+
expect(allIds).toHaveLength(expectedIds.size);
|
|
1346
|
+
const seen = new Set<RawCoID>();
|
|
1347
|
+
for (const id of allIds) {
|
|
1348
|
+
expect(seen.has(id)).toBe(false);
|
|
1349
|
+
seen.add(id);
|
|
1350
|
+
expect(expectedIds.has(id)).toBe(true);
|
|
1351
|
+
}
|
|
1352
|
+
});
|
|
1353
|
+
});
|
|
1354
|
+
|
|
1355
|
+
describe("getCoValueCount", () => {
|
|
1356
|
+
test("should return 0 when storage has no CoValues", async () => {
|
|
1357
|
+
const client = setupTestNode();
|
|
1358
|
+
const { storage } = client.addStorage({
|
|
1359
|
+
storage: createSyncStorage({
|
|
1360
|
+
nodeName: "test",
|
|
1361
|
+
storageName: "test-storage",
|
|
1362
|
+
}),
|
|
1363
|
+
});
|
|
1364
|
+
|
|
1365
|
+
const count = await new Promise<number>((resolve) => {
|
|
1366
|
+
storage.getCoValueCount(resolve);
|
|
1367
|
+
});
|
|
1368
|
+
|
|
1369
|
+
expect(count).toBe(0);
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1372
|
+
test("should return CoValue count after storing CoValues", async () => {
|
|
1373
|
+
const dbPath = getDbPath();
|
|
1374
|
+
const fixtures = setupTestNode();
|
|
1375
|
+
fixtures.addStorage({
|
|
1376
|
+
storage: createSyncStorage({
|
|
1377
|
+
filename: dbPath,
|
|
1378
|
+
nodeName: "test",
|
|
1379
|
+
storageName: "test-storage",
|
|
1380
|
+
}),
|
|
1381
|
+
});
|
|
1382
|
+
|
|
1383
|
+
const client = setupTestNode();
|
|
1384
|
+
const { storage } = client.addStorage({
|
|
1385
|
+
storage: createSyncStorage({
|
|
1386
|
+
filename: dbPath,
|
|
1387
|
+
nodeName: "test",
|
|
1388
|
+
storageName: "test-storage",
|
|
1389
|
+
}),
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
const countEmpty = await new Promise<number>((resolve) => {
|
|
1393
|
+
storage.getCoValueCount(resolve);
|
|
1394
|
+
});
|
|
1395
|
+
expect(countEmpty).toBe(0);
|
|
1396
|
+
|
|
1397
|
+
const group = fixtures.node.createGroup();
|
|
1398
|
+
group.addMember("everyone", "reader");
|
|
1399
|
+
await group.core.waitForSync();
|
|
1400
|
+
|
|
1401
|
+
const countOne = await new Promise<number>((resolve) => {
|
|
1402
|
+
storage.getCoValueCount(resolve);
|
|
1403
|
+
});
|
|
1404
|
+
expect(countOne).toBe(1);
|
|
1405
|
+
|
|
1406
|
+
const map = group.createMap();
|
|
1407
|
+
map.set("key", "value", "trusting");
|
|
1408
|
+
await map.core.waitForSync();
|
|
1409
|
+
|
|
1410
|
+
const countTwo = await new Promise<number>((resolve) => {
|
|
1411
|
+
storage.getCoValueCount(resolve);
|
|
1412
|
+
});
|
|
1413
|
+
expect(countTwo).toBe(2);
|
|
1414
|
+
});
|
|
1415
|
+
});
|
|
1240
1416
|
});
|