cojson 0.20.9 → 0.20.11
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 +29 -0
- package/dist/OngoingStorageReconciliationTracker.d.ts +16 -0
- package/dist/OngoingStorageReconciliationTracker.d.ts.map +1 -0
- package/dist/OngoingStorageReconciliationTracker.js +75 -0
- package/dist/OngoingStorageReconciliationTracker.js.map +1 -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 +53 -2
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +300 -44
- package/dist/sync.js.map +1 -1
- package/dist/tests/OngoingStorageReconciliationTracker.test.d.ts +2 -0
- package/dist/tests/OngoingStorageReconciliationTracker.test.d.ts.map +1 -0
- package/dist/tests/OngoingStorageReconciliationTracker.test.js +60 -0
- package/dist/tests/OngoingStorageReconciliationTracker.test.js.map +1 -0
- 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 +502 -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/OngoingStorageReconciliationTracker.ts +97 -0
- 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 +359 -14
- package/src/tests/OngoingStorageReconciliationTracker.test.ts +85 -0
- 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 +696 -0
- package/src/tests/testUtils.ts +10 -1
|
@@ -11,13 +11,16 @@ import type {
|
|
|
11
11
|
DBTransactionInterfaceAsync,
|
|
12
12
|
SessionRow,
|
|
13
13
|
SignatureAfterRow,
|
|
14
|
+
StorageReconciliationLockRow,
|
|
14
15
|
StoredCoValueRow,
|
|
15
16
|
StoredSessionRow,
|
|
16
17
|
TransactionRow,
|
|
18
|
+
StorageReconciliationAcquireResult,
|
|
17
19
|
} from "../types.js";
|
|
18
20
|
import { DeletedCoValueDeletionStatus } from "../types.js";
|
|
19
21
|
import type { SQLiteDatabaseDriverAsync } from "./types.js";
|
|
20
22
|
import type { PeerID } from "../../sync.js";
|
|
23
|
+
import { STORAGE_RECONCILIATION_CONFIG } from "../../config.js";
|
|
21
24
|
|
|
22
25
|
export type RawCoValueRow = {
|
|
23
26
|
id: RawCoID;
|
|
@@ -153,6 +156,37 @@ export class SQLiteTransactionAsync implements DBTransactionInterfaceAsync {
|
|
|
153
156
|
],
|
|
154
157
|
);
|
|
155
158
|
}
|
|
159
|
+
|
|
160
|
+
async getStorageReconciliationLock(
|
|
161
|
+
key: string,
|
|
162
|
+
): Promise<StorageReconciliationLockRow | undefined> {
|
|
163
|
+
return this.tx.get<StorageReconciliationLockRow>(
|
|
164
|
+
"SELECT * FROM storageReconciliationLocks WHERE key = ?",
|
|
165
|
+
[key],
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async putStorageReconciliationLock(
|
|
170
|
+
entry: StorageReconciliationLockRow,
|
|
171
|
+
): Promise<void> {
|
|
172
|
+
const {
|
|
173
|
+
key,
|
|
174
|
+
holderSessionId,
|
|
175
|
+
acquiredAt,
|
|
176
|
+
releasedAt,
|
|
177
|
+
lastProcessedOffset,
|
|
178
|
+
} = entry;
|
|
179
|
+
await this.tx.run(
|
|
180
|
+
`INSERT OR REPLACE INTO storageReconciliationLocks (key, holderSessionId, acquiredAt, releasedAt, lastProcessedOffset) VALUES (?, ?, ?, ?, ?)`,
|
|
181
|
+
[
|
|
182
|
+
key,
|
|
183
|
+
holderSessionId,
|
|
184
|
+
acquiredAt,
|
|
185
|
+
releasedAt ?? null,
|
|
186
|
+
lastProcessedOffset,
|
|
187
|
+
],
|
|
188
|
+
);
|
|
189
|
+
}
|
|
156
190
|
}
|
|
157
191
|
|
|
158
192
|
export class SQLiteClientAsync implements DBClientInterfaceAsync {
|
|
@@ -334,6 +368,111 @@ export class SQLiteClientAsync implements DBClientInterfaceAsync {
|
|
|
334
368
|
]);
|
|
335
369
|
}
|
|
336
370
|
|
|
371
|
+
async getCoValueIDs(
|
|
372
|
+
limit: number,
|
|
373
|
+
offset: number,
|
|
374
|
+
): Promise<{ id: RawCoID }[]> {
|
|
375
|
+
return this.db.query<{ id: RawCoID }>(
|
|
376
|
+
"SELECT id FROM coValues WHERE rowID > ? ORDER BY rowID LIMIT ?",
|
|
377
|
+
[offset, limit],
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
async getCoValueCount(): Promise<number> {
|
|
382
|
+
const row = await this.db.get<{ count: number }>(
|
|
383
|
+
"SELECT COUNT(*) as count FROM coValues",
|
|
384
|
+
[],
|
|
385
|
+
);
|
|
386
|
+
return row?.count ?? 0;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async tryAcquireStorageReconciliationLock(
|
|
390
|
+
sessionId: SessionID,
|
|
391
|
+
peerId: PeerID,
|
|
392
|
+
): Promise<StorageReconciliationAcquireResult> {
|
|
393
|
+
let result: StorageReconciliationAcquireResult = {
|
|
394
|
+
acquired: false,
|
|
395
|
+
reason: "not_due",
|
|
396
|
+
};
|
|
397
|
+
await this.transaction(async (tx) => {
|
|
398
|
+
const now = Date.now();
|
|
399
|
+
const lockKey = `lock#${peerId}`;
|
|
400
|
+
|
|
401
|
+
const lockRow = await tx.getStorageReconciliationLock(lockKey);
|
|
402
|
+
if (
|
|
403
|
+
lockRow?.releasedAt &&
|
|
404
|
+
now - lockRow.releasedAt <
|
|
405
|
+
STORAGE_RECONCILIATION_CONFIG.RECONCILIATION_INTERVAL_MS
|
|
406
|
+
) {
|
|
407
|
+
result = { acquired: false, reason: "not_due" };
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
const expiresAt = lockRow
|
|
411
|
+
? lockRow.acquiredAt + STORAGE_RECONCILIATION_CONFIG.LOCK_TTL_MS
|
|
412
|
+
: 0;
|
|
413
|
+
const isLockHeldByOtherSession = lockRow?.holderSessionId !== sessionId;
|
|
414
|
+
if (
|
|
415
|
+
lockRow &&
|
|
416
|
+
!lockRow.releasedAt &&
|
|
417
|
+
expiresAt >= now &&
|
|
418
|
+
isLockHeldByOtherSession
|
|
419
|
+
) {
|
|
420
|
+
result = { acquired: false, reason: "lock_held" };
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const lastProcessedOffset =
|
|
425
|
+
lockRow && !lockRow.releasedAt ? (lockRow.lastProcessedOffset ?? 0) : 0;
|
|
426
|
+
await tx.putStorageReconciliationLock({
|
|
427
|
+
key: lockKey,
|
|
428
|
+
holderSessionId: sessionId,
|
|
429
|
+
acquiredAt: now,
|
|
430
|
+
lastProcessedOffset,
|
|
431
|
+
});
|
|
432
|
+
result = { acquired: true, lastProcessedOffset };
|
|
433
|
+
});
|
|
434
|
+
return result;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
async renewStorageReconciliationLock(
|
|
438
|
+
sessionId: SessionID,
|
|
439
|
+
peerId: PeerID,
|
|
440
|
+
offset: number,
|
|
441
|
+
): Promise<void> {
|
|
442
|
+
await this.transaction(async (tx) => {
|
|
443
|
+
const lockKey = `lock#${peerId}`;
|
|
444
|
+
const lockRow = await tx.getStorageReconciliationLock(lockKey);
|
|
445
|
+
if (
|
|
446
|
+
lockRow &&
|
|
447
|
+
lockRow.holderSessionId === sessionId &&
|
|
448
|
+
!lockRow.releasedAt
|
|
449
|
+
) {
|
|
450
|
+
await tx.putStorageReconciliationLock({
|
|
451
|
+
...lockRow,
|
|
452
|
+
lastProcessedOffset: offset,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
async releaseStorageReconciliationLock(
|
|
459
|
+
sessionId: SessionID,
|
|
460
|
+
peerId: PeerID,
|
|
461
|
+
): Promise<void> {
|
|
462
|
+
await this.transaction(async (tx) => {
|
|
463
|
+
const lockKey = `lock#${peerId}`;
|
|
464
|
+
const releasedAt = Date.now();
|
|
465
|
+
const lockRow = await tx.getStorageReconciliationLock(lockKey);
|
|
466
|
+
if (lockRow && lockRow.holderSessionId === sessionId) {
|
|
467
|
+
await tx.putStorageReconciliationLock({
|
|
468
|
+
...lockRow,
|
|
469
|
+
releasedAt,
|
|
470
|
+
lastProcessedOffset: 0,
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
|
|
337
476
|
async getCoValueKnownState(
|
|
338
477
|
coValueId: string,
|
|
339
478
|
): Promise<CoValueKnownState | undefined> {
|
|
@@ -30,6 +30,7 @@ import type {
|
|
|
30
30
|
SignatureAfterRow,
|
|
31
31
|
StoredCoValueRow,
|
|
32
32
|
StoredSessionRow,
|
|
33
|
+
StorageReconciliationAcquireResult,
|
|
33
34
|
} from "./types.js";
|
|
34
35
|
import { isDeleteSessionID } from "../ids.js";
|
|
35
36
|
|
|
@@ -517,6 +518,40 @@ export class StorageApiAsync implements StorageAPI {
|
|
|
517
518
|
this.dbClient.trackCoValuesSyncState(updates).then(() => done?.());
|
|
518
519
|
}
|
|
519
520
|
|
|
521
|
+
getCoValueIDs(
|
|
522
|
+
limit: number,
|
|
523
|
+
offset: number,
|
|
524
|
+
callback: (batch: { id: RawCoID }[]) => void,
|
|
525
|
+
): void {
|
|
526
|
+
this.dbClient.getCoValueIDs(limit, offset).then(callback);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
getCoValueCount(callback: (count: number) => void): void {
|
|
530
|
+
this.dbClient.getCoValueCount().then(callback);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
tryAcquireStorageReconciliationLock(
|
|
534
|
+
sessionId: SessionID,
|
|
535
|
+
peerId: PeerID,
|
|
536
|
+
callback: (result: StorageReconciliationAcquireResult) => void,
|
|
537
|
+
): void {
|
|
538
|
+
this.dbClient
|
|
539
|
+
.tryAcquireStorageReconciliationLock(sessionId, peerId)
|
|
540
|
+
.then(callback);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
renewStorageReconciliationLock(
|
|
544
|
+
sessionId: SessionID,
|
|
545
|
+
peerId: PeerID,
|
|
546
|
+
offset: number,
|
|
547
|
+
): void {
|
|
548
|
+
this.dbClient.renewStorageReconciliationLock(sessionId, peerId, offset);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
releaseStorageReconciliationLock(sessionId: SessionID, peerId: PeerID): void {
|
|
552
|
+
this.dbClient.releaseStorageReconciliationLock(sessionId, peerId);
|
|
553
|
+
}
|
|
554
|
+
|
|
520
555
|
getUnsyncedCoValueIDs(
|
|
521
556
|
callback: (unsyncedCoValueIDs: RawCoID[]) => void,
|
|
522
557
|
): void {
|
|
@@ -529,11 +564,13 @@ export class StorageApiAsync implements StorageAPI {
|
|
|
529
564
|
|
|
530
565
|
onCoValueUnmounted(id: RawCoID): void {
|
|
531
566
|
this.inMemoryCoValues.delete(id);
|
|
567
|
+
this.knownStates.deleteKnownState(id);
|
|
532
568
|
}
|
|
533
569
|
|
|
534
570
|
close() {
|
|
535
571
|
this.deletedCoValuesEraserScheduler?.dispose();
|
|
536
572
|
this.inMemoryCoValues.clear();
|
|
573
|
+
this.knownStates.clear();
|
|
537
574
|
return this.storeQueue.close();
|
|
538
575
|
}
|
|
539
576
|
}
|
|
@@ -29,6 +29,7 @@ import type {
|
|
|
29
29
|
SignatureAfterRow,
|
|
30
30
|
StoredCoValueRow,
|
|
31
31
|
StoredSessionRow,
|
|
32
|
+
StorageReconciliationAcquireResult,
|
|
32
33
|
} from "./types.js";
|
|
33
34
|
import { DeletedCoValuesEraserScheduler } from "./DeletedCoValuesEraserScheduler.js";
|
|
34
35
|
import {
|
|
@@ -68,26 +69,48 @@ export class StorageApiSync implements StorageAPI {
|
|
|
68
69
|
return this.knownStates.getKnownState(id);
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
getCoValueIDs(
|
|
73
|
+
limit: number,
|
|
74
|
+
offset: number,
|
|
75
|
+
callback: (batch: { id: RawCoID }[]) => void,
|
|
74
76
|
): void {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
callback(cached);
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
77
|
+
const batch = this.dbClient.getCoValueIDs(limit, offset);
|
|
78
|
+
callback(batch);
|
|
79
|
+
}
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
getCoValueCount(callback: (count: number) => void): void {
|
|
82
|
+
callback(this.dbClient.getCoValueCount());
|
|
83
|
+
}
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
tryAcquireStorageReconciliationLock(
|
|
86
|
+
sessionId: SessionID,
|
|
87
|
+
peerId: PeerID,
|
|
88
|
+
callback: (result: StorageReconciliationAcquireResult) => void,
|
|
89
|
+
): void {
|
|
90
|
+
const result = this.dbClient.tryAcquireStorageReconciliationLock(
|
|
91
|
+
sessionId,
|
|
92
|
+
peerId,
|
|
93
|
+
);
|
|
94
|
+
callback(result);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
renewStorageReconciliationLock(
|
|
98
|
+
sessionId: SessionID,
|
|
99
|
+
peerId: PeerID,
|
|
100
|
+
offset: number,
|
|
101
|
+
): void {
|
|
102
|
+
this.dbClient.renewStorageReconciliationLock(sessionId, peerId, offset);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
releaseStorageReconciliationLock(sessionId: SessionID, peerId: PeerID): void {
|
|
106
|
+
this.dbClient.releaseStorageReconciliationLock(sessionId, peerId);
|
|
107
|
+
}
|
|
89
108
|
|
|
90
|
-
|
|
109
|
+
loadKnownState(
|
|
110
|
+
id: string,
|
|
111
|
+
callback: (knownState: CoValueKnownState | undefined) => void,
|
|
112
|
+
): void {
|
|
113
|
+
callback(this.dbClient.getCoValueKnownState(id));
|
|
91
114
|
}
|
|
92
115
|
|
|
93
116
|
async load(
|
|
@@ -515,11 +538,13 @@ export class StorageApiSync implements StorageAPI {
|
|
|
515
538
|
|
|
516
539
|
onCoValueUnmounted(id: RawCoID): void {
|
|
517
540
|
this.inMemoryCoValues.delete(id);
|
|
541
|
+
this.knownStates.deleteKnownState(id);
|
|
518
542
|
}
|
|
519
543
|
|
|
520
544
|
close() {
|
|
521
545
|
this.deletedCoValuesEraserScheduler?.dispose();
|
|
522
546
|
this.inMemoryCoValues.clear();
|
|
547
|
+
this.knownStates.clear();
|
|
523
548
|
return undefined;
|
|
524
549
|
}
|
|
525
550
|
}
|
package/src/storage/types.ts
CHANGED
|
@@ -13,6 +13,10 @@ export type CorrectionCallback = (
|
|
|
13
13
|
correction: CoValueKnownState,
|
|
14
14
|
) => NewContentMessage[] | undefined;
|
|
15
15
|
|
|
16
|
+
export type StorageReconciliationAcquireResult =
|
|
17
|
+
| { acquired: true; lastProcessedOffset: number }
|
|
18
|
+
| { acquired: false; reason: "not_due" | "lock_held" };
|
|
19
|
+
|
|
16
20
|
/**
|
|
17
21
|
* Deletion work queue status for `deletedCoValues` (SQLite).
|
|
18
22
|
*
|
|
@@ -86,6 +90,52 @@ export interface StorageAPI {
|
|
|
86
90
|
*/
|
|
87
91
|
stopTrackingSyncState(id: RawCoID): void;
|
|
88
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Get a batch of CoValue IDs from storage.
|
|
95
|
+
* Used for full storage reconciliation. Call repeatedly with increasing offset
|
|
96
|
+
* until the returned batch has length < limit (or 0) to enumerate all IDs.
|
|
97
|
+
* @param limit - Max number of IDs to return (e.g. 100).
|
|
98
|
+
* @param offset - Number of IDs to skip (0 for first batch).
|
|
99
|
+
* @param callback - Called with the batch. Ordering must be stable (e.g. by id).
|
|
100
|
+
*/
|
|
101
|
+
getCoValueIDs(
|
|
102
|
+
limit: number,
|
|
103
|
+
offset: number,
|
|
104
|
+
callback: (batch: { id: RawCoID }[]) => void,
|
|
105
|
+
): void;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get the total number of CoValues in storage.
|
|
109
|
+
*/
|
|
110
|
+
getCoValueCount(callback: (count: number) => void): void;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Try to acquire the storage reconciliation lock for a given peer.
|
|
114
|
+
* Atomically checks if reconciliation is due for this peer (lastRun older than 30 days or missing)
|
|
115
|
+
* and if no other process/tab holds the lock for this peer, then acquires it.
|
|
116
|
+
*/
|
|
117
|
+
tryAcquireStorageReconciliationLock(
|
|
118
|
+
sessionId: SessionID,
|
|
119
|
+
peerId: PeerID,
|
|
120
|
+
callback: (result: StorageReconciliationAcquireResult) => void,
|
|
121
|
+
): void;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Update the last processed offset for the storage reconciliation lock held for this peer.
|
|
125
|
+
* Only call after a batch has been acked; used to resume from this offset on interrupt.
|
|
126
|
+
*/
|
|
127
|
+
renewStorageReconciliationLock(
|
|
128
|
+
sessionId: SessionID,
|
|
129
|
+
peerId: PeerID,
|
|
130
|
+
offset: number,
|
|
131
|
+
): void;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Release the storage reconciliation lock for a peer and record completion. Only call on successful completion.
|
|
135
|
+
* On failure/interrupt, do not call; the lock expires after LOCK_TTL_MS and another process can retry for this peer.
|
|
136
|
+
*/
|
|
137
|
+
releaseStorageReconciliationLock(sessionId: SessionID, peerId: PeerID): void;
|
|
138
|
+
|
|
89
139
|
/**
|
|
90
140
|
* Load only the knownState (header presence + session counters) for a CoValue.
|
|
91
141
|
* This is more efficient than load() when we only need to check if a peer needs new content.
|
|
@@ -136,6 +186,15 @@ export type SignatureAfterRow = {
|
|
|
136
186
|
signature: Signature;
|
|
137
187
|
};
|
|
138
188
|
|
|
189
|
+
export type StorageReconciliationLockRow = {
|
|
190
|
+
key: string;
|
|
191
|
+
holderSessionId: SessionID;
|
|
192
|
+
acquiredAt: number;
|
|
193
|
+
releasedAt?: number;
|
|
194
|
+
/** Offset up to which all batches have been acked; used to resume after interrupt. */
|
|
195
|
+
lastProcessedOffset: number;
|
|
196
|
+
};
|
|
197
|
+
|
|
139
198
|
export interface DBTransactionInterfaceAsync {
|
|
140
199
|
getSingleCoValueSession(
|
|
141
200
|
coValueRowId: number,
|
|
@@ -176,6 +235,14 @@ export interface DBTransactionInterfaceAsync {
|
|
|
176
235
|
deleteCoValueContent(
|
|
177
236
|
coValueRow: Pick<StoredCoValueRow, "rowID" | "id">,
|
|
178
237
|
): Promise<unknown>;
|
|
238
|
+
|
|
239
|
+
getStorageReconciliationLock(
|
|
240
|
+
key: string,
|
|
241
|
+
): Promise<StorageReconciliationLockRow | undefined>;
|
|
242
|
+
|
|
243
|
+
putStorageReconciliationLock(
|
|
244
|
+
entry: StorageReconciliationLockRow,
|
|
245
|
+
): Promise<void>;
|
|
179
246
|
}
|
|
180
247
|
|
|
181
248
|
export interface DBClientInterfaceAsync {
|
|
@@ -232,6 +299,26 @@ export interface DBClientInterfaceAsync {
|
|
|
232
299
|
getCoValueKnownState(
|
|
233
300
|
coValueId: string,
|
|
234
301
|
): Promise<CoValueKnownState | undefined>;
|
|
302
|
+
|
|
303
|
+
getCoValueIDs(limit: number, offset: number): Promise<{ id: RawCoID }[]>;
|
|
304
|
+
|
|
305
|
+
getCoValueCount(): Promise<number>;
|
|
306
|
+
|
|
307
|
+
tryAcquireStorageReconciliationLock(
|
|
308
|
+
sessionId: SessionID,
|
|
309
|
+
peerId: PeerID,
|
|
310
|
+
): Promise<StorageReconciliationAcquireResult>;
|
|
311
|
+
|
|
312
|
+
renewStorageReconciliationLock(
|
|
313
|
+
sessionId: SessionID,
|
|
314
|
+
peerId: PeerID,
|
|
315
|
+
offset: number,
|
|
316
|
+
): Promise<void>;
|
|
317
|
+
|
|
318
|
+
releaseStorageReconciliationLock(
|
|
319
|
+
sessionId: SessionID,
|
|
320
|
+
peerId: PeerID,
|
|
321
|
+
): Promise<void>;
|
|
235
322
|
}
|
|
236
323
|
|
|
237
324
|
export interface DBTransactionInterfaceSync {
|
|
@@ -270,6 +357,12 @@ export interface DBTransactionInterfaceSync {
|
|
|
270
357
|
idx: number;
|
|
271
358
|
signature: Signature;
|
|
272
359
|
}): number | undefined | unknown;
|
|
360
|
+
|
|
361
|
+
getStorageReconciliationLock(
|
|
362
|
+
key: string,
|
|
363
|
+
): StorageReconciliationLockRow | undefined;
|
|
364
|
+
|
|
365
|
+
putStorageReconciliationLock(entry: StorageReconciliationLockRow): void;
|
|
273
366
|
}
|
|
274
367
|
|
|
275
368
|
export interface DBClientInterfaceSync {
|
|
@@ -317,4 +410,21 @@ export interface DBClientInterfaceSync {
|
|
|
317
410
|
* Returns undefined if the CoValue doesn't exist.
|
|
318
411
|
*/
|
|
319
412
|
getCoValueKnownState(coValueId: string): CoValueKnownState | undefined;
|
|
413
|
+
|
|
414
|
+
getCoValueIDs(limit: number, offset: number): { id: RawCoID }[];
|
|
415
|
+
|
|
416
|
+
getCoValueCount(): number;
|
|
417
|
+
|
|
418
|
+
tryAcquireStorageReconciliationLock(
|
|
419
|
+
sessionId: SessionID,
|
|
420
|
+
peerId: PeerID,
|
|
421
|
+
): StorageReconciliationAcquireResult;
|
|
422
|
+
|
|
423
|
+
renewStorageReconciliationLock(
|
|
424
|
+
sessionId: SessionID,
|
|
425
|
+
peerId: PeerID,
|
|
426
|
+
offset: number,
|
|
427
|
+
): void;
|
|
428
|
+
|
|
429
|
+
releaseStorageReconciliationLock(sessionId: SessionID, peerId: PeerID): void;
|
|
320
430
|
}
|