@tanstack/db-sqlite-persistence-core 0.4.5 → 0.4.8
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/README.md +22 -2
- package/dist/cjs/broadcast-coordinator.cjs +169 -18
- package/dist/cjs/broadcast-coordinator.cjs.map +1 -1
- package/dist/cjs/broadcast-coordinator.d.cts +11 -2
- package/dist/cjs/committed-tx-anchor.cjs +9 -0
- package/dist/cjs/committed-tx-anchor.cjs.map +1 -0
- package/dist/cjs/committed-tx-anchor.d.cts +1 -0
- package/dist/cjs/persisted.cjs +199 -43
- package/dist/cjs/persisted.cjs.map +1 -1
- package/dist/cjs/persisted.d.cts +46 -0
- package/dist/cjs/sqlite-core-adapter.cjs +94 -3
- package/dist/cjs/sqlite-core-adapter.cjs.map +1 -1
- package/dist/cjs/sqlite-core-adapter.d.cts +10 -1
- package/dist/esm/broadcast-coordinator.d.ts +11 -2
- package/dist/esm/broadcast-coordinator.js +170 -19
- package/dist/esm/broadcast-coordinator.js.map +1 -1
- package/dist/esm/committed-tx-anchor.d.ts +1 -0
- package/dist/esm/committed-tx-anchor.js +9 -0
- package/dist/esm/committed-tx-anchor.js.map +1 -0
- package/dist/esm/persisted.d.ts +46 -0
- package/dist/esm/persisted.js +201 -45
- package/dist/esm/persisted.js.map +1 -1
- package/dist/esm/sqlite-core-adapter.d.ts +10 -1
- package/dist/esm/sqlite-core-adapter.js +94 -3
- package/dist/esm/sqlite-core-adapter.js.map +1 -1
- package/package.json +2 -2
- package/src/broadcast-coordinator.ts +267 -44
- package/src/committed-tx-anchor.ts +13 -0
- package/src/persisted.ts +315 -53
- package/src/sqlite-core-adapter.ts +143 -3
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
function isValidCommittedTxAnchor(value) {
|
|
4
|
+
if (!value || typeof value !== `object`) return false;
|
|
5
|
+
const anchor = value;
|
|
6
|
+
return typeof anchor.latestRowVersion === `number` && Number.isSafeInteger(anchor.latestRowVersion) && anchor.latestRowVersion >= 0 && typeof anchor.resetEpoch === `number` && Number.isSafeInteger(anchor.resetEpoch) && anchor.resetEpoch >= 0;
|
|
7
|
+
}
|
|
8
|
+
exports.isValidCommittedTxAnchor = isValidCommittedTxAnchor;
|
|
9
|
+
//# sourceMappingURL=committed-tx-anchor.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"committed-tx-anchor.cjs","sources":["../../src/committed-tx-anchor.ts"],"sourcesContent":["// RPC payloads and direct adapter calls can bypass TypeScript's anchor type.\nexport function isValidCommittedTxAnchor(value: unknown): boolean {\n if (!value || typeof value !== `object`) return false\n const anchor = value as Record<string, unknown>\n return (\n typeof anchor.latestRowVersion === `number` &&\n Number.isSafeInteger(anchor.latestRowVersion) &&\n anchor.latestRowVersion >= 0 &&\n typeof anchor.resetEpoch === `number` &&\n Number.isSafeInteger(anchor.resetEpoch) &&\n anchor.resetEpoch >= 0\n )\n}\n"],"names":[],"mappings":";;AACO,SAAS,yBAAyB,OAAyB;AAChE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,SAAS;AACf,SACE,OAAO,OAAO,qBAAqB,YACnC,OAAO,cAAc,OAAO,gBAAgB,KAC5C,OAAO,oBAAoB,KAC3B,OAAO,OAAO,eAAe,YAC7B,OAAO,cAAc,OAAO,UAAU,KACtC,OAAO,cAAc;AAEzB;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isValidCommittedTxAnchor(value: unknown): boolean;
|
package/dist/cjs/persisted.cjs
CHANGED
|
@@ -393,7 +393,7 @@ function isTxCommittedPayload(payload) {
|
|
|
393
393
|
return false;
|
|
394
394
|
}
|
|
395
395
|
if (payload.requiresFullReload) {
|
|
396
|
-
return true;
|
|
396
|
+
return payload.reconciliationReload === void 0 || payload.reconciliationReload === true;
|
|
397
397
|
}
|
|
398
398
|
return Array.isArray(payload.changedRows) && Array.isArray(payload.deletedKeys) && (payload.rowMetadataMutations === void 0 || Array.isArray(payload.rowMetadataMutations)) && (payload.collectionMetadataMutations === void 0 || Array.isArray(payload.collectionMetadataMutations));
|
|
399
399
|
}
|
|
@@ -459,6 +459,7 @@ class PersistedCollectionRuntime {
|
|
|
459
459
|
this.latestTerm = 0;
|
|
460
460
|
this.latestSeq = 0;
|
|
461
461
|
this.latestRowVersion = 0;
|
|
462
|
+
this.publicRowVersion = 0;
|
|
462
463
|
this.localTerm = 1;
|
|
463
464
|
this.localSeq = 0;
|
|
464
465
|
this.localRowVersion = 0;
|
|
@@ -1136,6 +1137,8 @@ class PersistedCollectionRuntime {
|
|
|
1136
1137
|
this.lifecycleGeneration++;
|
|
1137
1138
|
this.persistedReadiness?.set({ status: `loading` });
|
|
1138
1139
|
this.startupSettled = false;
|
|
1140
|
+
this.publicRowVersion = 0;
|
|
1141
|
+
this.resetTermFence = void 0;
|
|
1139
1142
|
this.hydratedDemands.clear();
|
|
1140
1143
|
this.startupMetadataPromise = null;
|
|
1141
1144
|
this.startPromise = null;
|
|
@@ -1180,6 +1183,7 @@ class PersistedCollectionRuntime {
|
|
|
1180
1183
|
}
|
|
1181
1184
|
async hydrateSubsetUnsafe(options, config, adapter) {
|
|
1182
1185
|
let rowsLoaded = false;
|
|
1186
|
+
let boundRowVersion;
|
|
1183
1187
|
let replayFailure;
|
|
1184
1188
|
const resetSequence = this.resetSequence;
|
|
1185
1189
|
this.hydrationSequence++;
|
|
@@ -1199,6 +1203,7 @@ class PersistedCollectionRuntime {
|
|
|
1199
1203
|
if (config.lifecycleGeneration !== this.lifecycleGeneration) return;
|
|
1200
1204
|
if (resetSequence !== this.resetSequence) return;
|
|
1201
1205
|
this.bindResumeSnapshotEvidence(snapshot);
|
|
1206
|
+
boundRowVersion = snapshot.latestRowVersion;
|
|
1202
1207
|
} else {
|
|
1203
1208
|
rows = await this.loadSubsetRowsUnsafe(options, adapter);
|
|
1204
1209
|
}
|
|
@@ -1211,6 +1216,12 @@ class PersistedCollectionRuntime {
|
|
|
1211
1216
|
}
|
|
1212
1217
|
const applied = this.applyRowsToCollection(rows);
|
|
1213
1218
|
await db.whenSyncAccepted(applied);
|
|
1219
|
+
if (boundRowVersion !== void 0) {
|
|
1220
|
+
this.publicRowVersion = Math.max(
|
|
1221
|
+
this.publicRowVersion,
|
|
1222
|
+
boundRowVersion
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1214
1225
|
}
|
|
1215
1226
|
} finally {
|
|
1216
1227
|
if (this.activeHydrationContext === hydrationContext && this.hydratingGeneration === config.lifecycleGeneration) {
|
|
@@ -1374,7 +1385,9 @@ class PersistedCollectionRuntime {
|
|
|
1374
1385
|
}
|
|
1375
1386
|
isExpectedResumeGeneration(generation) {
|
|
1376
1387
|
const expected = this.persistedResumeGeneration;
|
|
1377
|
-
return expected !== void 0 && expected.
|
|
1388
|
+
return expected !== void 0 && expected.latestRowVersion === generation.latestRowVersion && expected.resetEpoch === generation.resetEpoch && (expected.latestTerm === generation.latestTerm && expected.latestSeq === generation.latestSeq || // A no-write election reserves a new term at sequence zero. It does
|
|
1389
|
+
// not change the persisted rows or the source resume cursor.
|
|
1390
|
+
generation.latestTerm > expected.latestTerm && generation.latestSeq === 0);
|
|
1378
1391
|
}
|
|
1379
1392
|
bindResumeSnapshotEvidence(snapshot) {
|
|
1380
1393
|
const generation = this.getResumeSnapshotGeneration(snapshot);
|
|
@@ -1416,6 +1429,27 @@ class PersistedCollectionRuntime {
|
|
|
1416
1429
|
return this.syncControls.commit?.() ?? true;
|
|
1417
1430
|
});
|
|
1418
1431
|
}
|
|
1432
|
+
matchesCollectionSnapshot(rows, collectionMetadata) {
|
|
1433
|
+
if (!this.collection || !this.syncControls.metadata) return false;
|
|
1434
|
+
const base = this.collection.base;
|
|
1435
|
+
if (base.size !== rows.size) return false;
|
|
1436
|
+
for (const [key, row] of rows) {
|
|
1437
|
+
if (!base.has(key) || !db.equalPersistedSnapshotValues(base.get(key), row.value) || !db.equalPersistedSnapshotValues(
|
|
1438
|
+
this.syncControls.metadata.row.get(key),
|
|
1439
|
+
row.metadata
|
|
1440
|
+
)) {
|
|
1441
|
+
return false;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
const currentMetadata = this.syncControls.metadata.collection.list();
|
|
1445
|
+
if (currentMetadata.length !== collectionMetadata.length) return false;
|
|
1446
|
+
const expectedMetadata = new Map(
|
|
1447
|
+
collectionMetadata.map(({ key, value }) => [key, value])
|
|
1448
|
+
);
|
|
1449
|
+
return currentMetadata.every(
|
|
1450
|
+
({ key, value }) => expectedMetadata.has(key) && db.equalPersistedSnapshotValues(expectedMetadata.get(key), value)
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1419
1453
|
async flushQueuedHydrationTransactionsUnsafe(adapter) {
|
|
1420
1454
|
let operationFailure;
|
|
1421
1455
|
while (this.queuedHydrationTransactions.length > 0) {
|
|
@@ -1500,16 +1534,55 @@ class PersistedCollectionRuntime {
|
|
|
1500
1534
|
if (!transaction.truncate && transaction.operations.length === 0 && transaction.rowMetadataWrites.size === 0 && transaction.collectionMetadataWrites.size === 0) {
|
|
1501
1535
|
return;
|
|
1502
1536
|
}
|
|
1537
|
+
const anchor = this.persistedResumeGeneration && {
|
|
1538
|
+
latestRowVersion: this.latestRowVersion,
|
|
1539
|
+
resetEpoch: this.persistedResumeGeneration.resetEpoch
|
|
1540
|
+
};
|
|
1503
1541
|
const streamPosition = this.nextLocalStreamPosition();
|
|
1504
1542
|
const tx = this.createPersistedTxFromOperations(transaction, streamPosition);
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1543
|
+
let response;
|
|
1544
|
+
try {
|
|
1545
|
+
response = await this.persistence.coordinator.requestApplyCommittedTx(
|
|
1546
|
+
this.collectionId,
|
|
1547
|
+
tx,
|
|
1548
|
+
// A hydration-scoped adapter is already inside the shared scheduler.
|
|
1549
|
+
// Ordinary source commits enter it before taking the writer lock.
|
|
1550
|
+
scopedAdapter
|
|
1551
|
+
);
|
|
1552
|
+
} catch (error) {
|
|
1553
|
+
if (!(error instanceof errors.IndeterminateCommitError) || !anchor || !this.persistence.coordinator.reconcileCommittedTx) {
|
|
1554
|
+
throw error;
|
|
1555
|
+
}
|
|
1556
|
+
let reconciled;
|
|
1557
|
+
try {
|
|
1558
|
+
reconciled = await this.persistence.coordinator.reconcileCommittedTx(
|
|
1559
|
+
this.collectionId,
|
|
1560
|
+
tx,
|
|
1561
|
+
anchor,
|
|
1562
|
+
scopedAdapter
|
|
1563
|
+
);
|
|
1564
|
+
} catch (reconcileError) {
|
|
1565
|
+
if (reconcileError instanceof errors.PersistedCollectionDurabilityError) {
|
|
1566
|
+
throw reconcileError;
|
|
1567
|
+
}
|
|
1568
|
+
throw error;
|
|
1569
|
+
}
|
|
1570
|
+
if (!reconciled.ok) {
|
|
1571
|
+
if (reconciled.code === `PERSISTENCE_ERROR`) {
|
|
1572
|
+
throw new errors.PersistedCollectionDurabilityError(
|
|
1573
|
+
`Failed to reconcile collection "${this.collectionId}": ${reconciled.error}`,
|
|
1574
|
+
{
|
|
1575
|
+
cause: reconciled,
|
|
1576
|
+
code: reconciled.sourceCode ?? reconciled.code,
|
|
1577
|
+
path: reconciled.path
|
|
1578
|
+
}
|
|
1579
|
+
);
|
|
1580
|
+
}
|
|
1581
|
+
throw error;
|
|
1582
|
+
}
|
|
1583
|
+
this.recordDurableSourceCommit(transaction, reconciled.committed);
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1513
1586
|
if (!response.ok) {
|
|
1514
1587
|
if (response.code === `PERSISTENCE_ERROR`) {
|
|
1515
1588
|
const error = new errors.PersistedCollectionDurabilityError(
|
|
@@ -1526,18 +1599,24 @@ class PersistedCollectionRuntime {
|
|
|
1526
1599
|
`failed to apply external sync transaction through coordinator: ${response.error}`
|
|
1527
1600
|
);
|
|
1528
1601
|
}
|
|
1602
|
+
this.recordDurableSourceCommit(transaction, {
|
|
1603
|
+
term: response.term,
|
|
1604
|
+
seq: response.seq,
|
|
1605
|
+
rowVersion: response.latestRowVersion
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
recordDurableSourceCommit(transaction, position) {
|
|
1529
1609
|
if (transaction.lifecycleGeneration !== this.lifecycleGeneration) return;
|
|
1530
|
-
this.observeStreamPosition(
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
);
|
|
1610
|
+
this.observeStreamPosition(position.term, position.seq, position.rowVersion);
|
|
1611
|
+
if (position.rowVersion === this.publicRowVersion + 1) {
|
|
1612
|
+
this.publicRowVersion = position.rowVersion;
|
|
1613
|
+
}
|
|
1535
1614
|
if (transaction.expectedResumeGenerationOwner === this.resumeGenerationOwner && this.persistedResumeGeneration !== void 0) {
|
|
1536
1615
|
this.persistedResumeGeneration = {
|
|
1537
1616
|
...this.persistedResumeGeneration,
|
|
1538
|
-
latestTerm:
|
|
1539
|
-
latestSeq:
|
|
1540
|
-
latestRowVersion:
|
|
1617
|
+
latestTerm: position.term,
|
|
1618
|
+
latestSeq: position.seq,
|
|
1619
|
+
latestRowVersion: position.rowVersion
|
|
1541
1620
|
};
|
|
1542
1621
|
}
|
|
1543
1622
|
}
|
|
@@ -1900,9 +1979,17 @@ class PersistedCollectionRuntime {
|
|
|
1900
1979
|
}
|
|
1901
1980
|
if (isCollectionResetPayload(payload)) {
|
|
1902
1981
|
void this.applyMutex.run(async () => {
|
|
1903
|
-
await this.runInHydrationScope(
|
|
1904
|
-
|
|
1905
|
-
|
|
1982
|
+
await this.runInHydrationScope(async (adapter) => {
|
|
1983
|
+
await this.truncateAndReloadUnsafe(adapter, lifecycleGeneration);
|
|
1984
|
+
await this.rebaseDurableStreamFromSnapshotUnsafe(
|
|
1985
|
+
adapter,
|
|
1986
|
+
lifecycleGeneration
|
|
1987
|
+
);
|
|
1988
|
+
if (lifecycleGeneration === this.lifecycleGeneration) {
|
|
1989
|
+
this.publicRowVersion = 0;
|
|
1990
|
+
this.resetTermFence = this.latestTerm;
|
|
1991
|
+
}
|
|
1992
|
+
});
|
|
1906
1993
|
if (lifecycleGeneration === this.lifecycleGeneration) {
|
|
1907
1994
|
await this.flushQueuedTxCommittedUnsafe();
|
|
1908
1995
|
}
|
|
@@ -1925,21 +2012,42 @@ class PersistedCollectionRuntime {
|
|
|
1925
2012
|
}
|
|
1926
2013
|
async processCommittedTxUnsafe(txCommitted, lifecycleGeneration = this.lifecycleGeneration) {
|
|
1927
2014
|
if (lifecycleGeneration !== this.lifecycleGeneration) return;
|
|
1928
|
-
if (
|
|
1929
|
-
|
|
2015
|
+
if (this.resetTermFence !== void 0) {
|
|
2016
|
+
if (txCommitted.term <= this.resetTermFence) {
|
|
2017
|
+
await this.runInHydrationScope(
|
|
2018
|
+
(adapter) => this.reloadActiveSubsetsUnsafe(adapter)
|
|
2019
|
+
);
|
|
2020
|
+
return;
|
|
2021
|
+
}
|
|
1930
2022
|
}
|
|
1931
|
-
if (txCommitted.
|
|
2023
|
+
if (txCommitted.requiresFullReload && txCommitted.reconciliationReload) {
|
|
2024
|
+
await this.invalidateFromCommittedTxUnsafe(
|
|
2025
|
+
txCommitted,
|
|
2026
|
+
this.persistence.adapter
|
|
2027
|
+
);
|
|
2028
|
+
if (lifecycleGeneration === this.lifecycleGeneration) {
|
|
2029
|
+
this.observeStreamPosition(
|
|
2030
|
+
txCommitted.term,
|
|
2031
|
+
txCommitted.seq,
|
|
2032
|
+
txCommitted.latestRowVersion
|
|
2033
|
+
);
|
|
2034
|
+
this.publicRowVersion = Math.max(
|
|
2035
|
+
this.publicRowVersion,
|
|
2036
|
+
txCommitted.latestRowVersion
|
|
2037
|
+
);
|
|
2038
|
+
await this.flushQueuedTxCommittedUnsafe();
|
|
2039
|
+
}
|
|
1932
2040
|
return;
|
|
1933
2041
|
}
|
|
2042
|
+
if (txCommitted.latestRowVersion <= this.publicRowVersion) return;
|
|
2043
|
+
const observedButUnpublished = txCommitted.term < this.latestTerm || txCommitted.term === this.latestTerm && txCommitted.seq <= this.latestSeq;
|
|
1934
2044
|
const hasGapInCurrentTerm = txCommitted.term === this.latestTerm && txCommitted.seq > this.latestSeq + 1;
|
|
1935
2045
|
const hasGapAcrossTerms = txCommitted.term > this.latestTerm && txCommitted.seq > 1;
|
|
1936
|
-
const hasGap = hasGapInCurrentTerm || hasGapAcrossTerms;
|
|
2046
|
+
const hasGap = observedButUnpublished || txCommitted.latestRowVersion > this.publicRowVersion + 1 || hasGapInCurrentTerm || hasGapAcrossTerms;
|
|
1937
2047
|
if (hasGap) {
|
|
1938
2048
|
await this.recoverFromSeqGapUnsafe(lifecycleGeneration);
|
|
1939
2049
|
if (lifecycleGeneration !== this.lifecycleGeneration) return;
|
|
1940
|
-
if (txCommitted.
|
|
1941
|
-
return;
|
|
1942
|
-
}
|
|
2050
|
+
if (txCommitted.latestRowVersion <= this.publicRowVersion) return;
|
|
1943
2051
|
}
|
|
1944
2052
|
this.observeStreamPosition(
|
|
1945
2053
|
txCommitted.term,
|
|
@@ -1951,9 +2059,27 @@ class PersistedCollectionRuntime {
|
|
|
1951
2059
|
this.persistence.adapter
|
|
1952
2060
|
);
|
|
1953
2061
|
if (lifecycleGeneration === this.lifecycleGeneration) {
|
|
2062
|
+
this.publicRowVersion = Math.max(
|
|
2063
|
+
this.publicRowVersion,
|
|
2064
|
+
txCommitted.latestRowVersion
|
|
2065
|
+
);
|
|
1954
2066
|
await this.flushQueuedTxCommittedUnsafe();
|
|
1955
2067
|
}
|
|
1956
2068
|
}
|
|
2069
|
+
async rebaseDurableStreamFromSnapshotUnsafe(adapter, lifecycleGeneration) {
|
|
2070
|
+
if (lifecycleGeneration !== this.lifecycleGeneration) return;
|
|
2071
|
+
const snapshot = await adapter.loadResumeSnapshot(this.collectionId, {
|
|
2072
|
+
includeRows: false
|
|
2073
|
+
});
|
|
2074
|
+
if (lifecycleGeneration !== this.lifecycleGeneration) return;
|
|
2075
|
+
this.latestRowVersion = snapshot.latestRowVersion;
|
|
2076
|
+
this.localRowVersion = snapshot.latestRowVersion;
|
|
2077
|
+
this.observeStreamPosition(
|
|
2078
|
+
snapshot.latestTerm,
|
|
2079
|
+
snapshot.latestSeq,
|
|
2080
|
+
snapshot.latestRowVersion
|
|
2081
|
+
);
|
|
2082
|
+
}
|
|
1957
2083
|
async recoverFromSeqGapUnsafe(lifecycleGeneration) {
|
|
1958
2084
|
if (lifecycleGeneration !== this.lifecycleGeneration) return;
|
|
1959
2085
|
if (this.persistence.coordinator.pullSince && this.latestRowVersion >= 0) {
|
|
@@ -1961,7 +2087,7 @@ class PersistedCollectionRuntime {
|
|
|
1961
2087
|
try {
|
|
1962
2088
|
pullResponse = await this.persistence.coordinator.pullSince(
|
|
1963
2089
|
this.collectionId,
|
|
1964
|
-
this.
|
|
2090
|
+
this.publicRowVersion
|
|
1965
2091
|
);
|
|
1966
2092
|
} catch (error) {
|
|
1967
2093
|
console.warn(`Failed pullSince recovery attempt:`, error);
|
|
@@ -1978,6 +2104,10 @@ class PersistedCollectionRuntime {
|
|
|
1978
2104
|
await this.runInHydrationScope(
|
|
1979
2105
|
(adapter) => this.reloadActiveSubsetsUnsafe(adapter)
|
|
1980
2106
|
);
|
|
2107
|
+
this.publicRowVersion = Math.max(
|
|
2108
|
+
this.publicRowVersion,
|
|
2109
|
+
pullResponse.latestRowVersion
|
|
2110
|
+
);
|
|
1981
2111
|
return;
|
|
1982
2112
|
}
|
|
1983
2113
|
const deltas = pullResponse.deltas;
|
|
@@ -1985,6 +2115,10 @@ class PersistedCollectionRuntime {
|
|
|
1985
2115
|
await this.runInHydrationScope(
|
|
1986
2116
|
(adapter) => this.reloadActiveSubsetsUnsafe(adapter)
|
|
1987
2117
|
);
|
|
2118
|
+
this.publicRowVersion = Math.max(
|
|
2119
|
+
this.publicRowVersion,
|
|
2120
|
+
pullResponse.latestRowVersion
|
|
2121
|
+
);
|
|
1988
2122
|
return;
|
|
1989
2123
|
}
|
|
1990
2124
|
await this.runInHydrationScope(async (adapter) => {
|
|
@@ -2008,13 +2142,21 @@ class PersistedCollectionRuntime {
|
|
|
2008
2142
|
if (lifecycleGeneration !== this.lifecycleGeneration) return;
|
|
2009
2143
|
}
|
|
2010
2144
|
});
|
|
2145
|
+
this.publicRowVersion = Math.max(
|
|
2146
|
+
this.publicRowVersion,
|
|
2147
|
+
pullResponse.latestRowVersion
|
|
2148
|
+
);
|
|
2011
2149
|
return;
|
|
2012
2150
|
}
|
|
2013
2151
|
}
|
|
2014
2152
|
}
|
|
2015
2153
|
if (lifecycleGeneration !== this.lifecycleGeneration) return;
|
|
2016
2154
|
await this.runInHydrationScope(
|
|
2017
|
-
(adapter) => this.
|
|
2155
|
+
(adapter) => this.reloadActiveSubsetsUnsafe(adapter)
|
|
2156
|
+
);
|
|
2157
|
+
this.publicRowVersion = Math.max(
|
|
2158
|
+
this.publicRowVersion,
|
|
2159
|
+
this.latestRowVersion
|
|
2018
2160
|
);
|
|
2019
2161
|
if (this.mode === `sync-present`) {
|
|
2020
2162
|
for (const options of this.activeSubsets.values()) {
|
|
@@ -2038,12 +2180,12 @@ class PersistedCollectionRuntime {
|
|
|
2038
2180
|
await this.reloadActiveSubsetsUnsafe(adapter);
|
|
2039
2181
|
}
|
|
2040
2182
|
async invalidateFromCommittedTxUnsafe(txCommitted, adapter) {
|
|
2041
|
-
const reloadActiveSubsets = () => this.runInHydrationScope(
|
|
2042
|
-
(scopedAdapter) => this.reloadActiveSubsetsUnsafe(scopedAdapter),
|
|
2183
|
+
const reloadActiveSubsets = (skipUnchangedReplace = false) => this.runInHydrationScope(
|
|
2184
|
+
(scopedAdapter) => this.reloadActiveSubsetsUnsafe(scopedAdapter, skipUnchangedReplace),
|
|
2043
2185
|
adapter
|
|
2044
2186
|
);
|
|
2045
2187
|
if (txCommitted.requiresFullReload) {
|
|
2046
|
-
await reloadActiveSubsets();
|
|
2188
|
+
await reloadActiveSubsets(txCommitted.reconciliationReload === true);
|
|
2047
2189
|
return;
|
|
2048
2190
|
}
|
|
2049
2191
|
const changedKeyCount = txCommitted.changedRows.length + txCommitted.deletedKeys.length;
|
|
@@ -2107,7 +2249,7 @@ class PersistedCollectionRuntime {
|
|
|
2107
2249
|
});
|
|
2108
2250
|
await db.whenSyncAccepted(applied);
|
|
2109
2251
|
}
|
|
2110
|
-
async reloadActiveSubsetsUnsafe(adapter) {
|
|
2252
|
+
async reloadActiveSubsetsUnsafe(adapter, skipUnchangedReplace = false) {
|
|
2111
2253
|
const lifecycleGeneration = this.lifecycleGeneration;
|
|
2112
2254
|
const truncateGeneration = this.sourceTruncateGeneration;
|
|
2113
2255
|
const activeSubsetOptions = this.activeSubsets.size > 0 ? Array.from(this.activeSubsets.values()) : [{}];
|
|
@@ -2133,15 +2275,29 @@ class PersistedCollectionRuntime {
|
|
|
2133
2275
|
for (const key of mergedRows.keys()) {
|
|
2134
2276
|
hydrationContext.suppliedRowKeys.add(key);
|
|
2135
2277
|
}
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2278
|
+
if (skipUnchangedReplace && this.matchesCollectionSnapshot(mergedRows, collectionMetadata)) {
|
|
2279
|
+
const orphanKeys = this.collection?._syncedRowMetadataKeysOutsideBase() ?? [];
|
|
2280
|
+
if (orphanKeys.length > 0) {
|
|
2281
|
+
const applied = this.withInternalApply(() => {
|
|
2282
|
+
this.syncControls.begin?.();
|
|
2283
|
+
for (const key of orphanKeys) {
|
|
2284
|
+
this.syncControls.metadata?.row.delete(key);
|
|
2285
|
+
}
|
|
2286
|
+
return this.syncControls.commit?.() ?? true;
|
|
2287
|
+
});
|
|
2288
|
+
await db.whenSyncAccepted(applied);
|
|
2289
|
+
}
|
|
2290
|
+
} else {
|
|
2291
|
+
const applied = this.replaceCollectionSnapshot(
|
|
2292
|
+
Array.from(mergedRows.entries()).map(([key, row]) => ({
|
|
2293
|
+
key,
|
|
2294
|
+
value: row.value,
|
|
2295
|
+
metadata: row.metadata
|
|
2296
|
+
})),
|
|
2297
|
+
collectionMetadata
|
|
2298
|
+
);
|
|
2299
|
+
await db.whenSyncAccepted(applied);
|
|
2300
|
+
}
|
|
2145
2301
|
} finally {
|
|
2146
2302
|
if (this.activeHydrationContext === hydrationContext && this.hydratingGeneration === lifecycleGeneration) {
|
|
2147
2303
|
this.hydratingGeneration = null;
|