@delali/sirannon-db 0.2.0 → 0.2.1
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/dist/backup-scheduler/index.d.ts +15 -1
- package/dist/backup-scheduler/index.mjs +2 -2
- package/dist/baseline-D93hcIEE.d.ts +17 -0
- package/dist/{change-tracker-DKRVUC3l.d.ts → change-tracker-DDmXB754.d.ts} +56 -8
- package/dist/{chunk-5NOIGN5Y.mjs → chunk-2QLXDHAP.mjs} +1 -1
- package/dist/{chunk-LNY2VVHE.mjs → chunk-7C36BCSN.mjs} +1 -1
- package/dist/{chunk-NVQS53NT.mjs → chunk-7FQRQH5Z.mjs} +53 -64
- package/dist/{chunk-D7LAYTKN.mjs → chunk-7R4ER4FB.mjs} +1 -1
- package/dist/{chunk-FHWTZFI4.mjs → chunk-BQFQ65OL.mjs} +1 -1
- package/dist/{chunk-O7SLN3GI.mjs → chunk-BTTFW4Z4.mjs} +1 -1
- package/dist/{chunk-H6PIVVDN.mjs → chunk-CCZK6LCB.mjs} +38 -25
- package/dist/{chunk-JZGINXTN.mjs → chunk-HCCGEIZ2.mjs} +2 -2
- package/dist/{chunk-67M7KAH6.mjs → chunk-IWGIYDMZ.mjs} +1 -1
- package/dist/{chunk-LFZ37BSX.mjs → chunk-OUSWVNWT.mjs} +1 -1
- package/dist/{chunk-HR5CWTLC.mjs → chunk-P2VJYRVY.mjs} +60 -7
- package/dist/{chunk-UC3SCMIN.mjs → chunk-PBRXXISQ.mjs} +3 -0
- package/dist/{chunk-JU64Y7HM.mjs → chunk-SBL6GN43.mjs} +1 -1
- package/dist/{chunk-EBJXPQQO.mjs → chunk-UPKKSUPA.mjs} +2 -2
- package/dist/{chunk-TJF5GZSV.mjs → chunk-VOSJBZ6Q.mjs} +1 -1
- package/dist/{chunk-PIKHN33N.mjs → chunk-VOYGMAU7.mjs} +9 -1
- package/dist/{chunk-H237TXZW.mjs → chunk-WJ67DTD6.mjs} +48 -6
- package/dist/{chunk-OQVZBEBY.mjs → chunk-XF2HH5E6.mjs} +4 -61
- package/dist/client/index.d.ts +211 -12
- package/dist/client/index.mjs +155 -67
- package/dist/client/topology.d.ts +55 -7
- package/dist/client/topology.mjs +20 -1
- package/dist/{client-base-CLWmH5Ln.d.ts → client-base-CmZO0v3m.d.ts} +133 -24
- package/dist/codegen/cli.mjs +3 -3
- package/dist/codegen/index.d.ts +92 -2
- package/dist/codegen/index.mjs +3 -3
- package/dist/core/index.d.ts +177 -15
- package/dist/core/index.mjs +2481 -2226
- package/dist/core/writer-worker.mjs +3 -3
- package/dist/database-B5Qv1-cU.d.ts +380 -0
- package/dist/driver/better-sqlite3.d.ts +18 -1
- package/dist/driver/better-sqlite3.mjs +5 -5
- package/dist/driver/bun.d.ts +28 -0
- package/dist/driver/expo.d.ts +17 -0
- package/dist/driver/node.d.ts +18 -1
- package/dist/driver/node.mjs +5 -5
- package/dist/driver/wa-sqlite.d.ts +18 -1
- package/dist/{errors-Bw5MdNCu.d.ts → errors-Dei4GdBb.d.ts} +80 -7
- package/dist/file-migrations/index.d.ts +54 -2
- package/dist/file-migrations/index.mjs +3 -3
- package/dist/{operation-registry-9DcvxcE5.d.ts → operation-registry-hlbhqu7q.d.ts} +50 -1
- package/dist/{primary-wins-DPAm2AKG.d.ts → primary-wins-B0np8JS3.d.ts} +25 -1
- package/dist/protocol-rqANt-9Q.d.ts +152 -0
- package/dist/query-types-DL3LtPvY.d.ts +95 -0
- package/dist/react/index.d.ts +58 -3
- package/dist/replication/coordinator/etcd.d.ts +63 -3
- package/dist/replication/coordinator/etcd.mjs +82 -46
- package/dist/replication/index.d.ts +329 -95
- package/dist/replication/index.mjs +256 -141
- package/dist/server/index.d.ts +230 -12
- package/dist/server/index.mjs +63 -50
- package/dist/{server-options-1JHu8pid.d.ts → server-options-Dab_Jvd_.d.ts} +96 -12
- package/dist/sirannon-CMhiJa5Y.d.ts +111 -0
- package/dist/transport/grpc.d.ts +93 -9
- package/dist/transport/grpc.mjs +63 -20
- package/dist/transport/memory.d.ts +50 -20
- package/dist/transport/memory.mjs +25 -0
- package/dist/{types-CL6piSnD.d.ts → types-BCejqzNA.d.ts} +20 -0
- package/dist/types-CMBcFPhb.d.ts +336 -0
- package/dist/types-CjhxcjhA.d.ts +123 -0
- package/dist/types-DyrCiWuc.d.ts +499 -0
- package/dist/types-rVZKnKN-.d.ts +591 -0
- package/package.json +7 -1
- package/dist/baseline-Br77Fnhb.d.ts +0 -6
- package/dist/database-BY0L5Q2n.d.ts +0 -172
- package/dist/protocol-6KrSq2Hy.d.ts +0 -66
- package/dist/sirannon-DaQSyhbJ.d.ts +0 -36
- package/dist/types-B7gmEsZW.d.ts +0 -221
- package/dist/types-BsVabqSI.d.ts +0 -139
- package/dist/types-C_D8IhpO.d.ts +0 -60
- package/dist/types-zhnRXrsb.d.ts +0 -384
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { compatibilityAllowsPromotion } from '../chunk-ER7ODTDA.mjs';
|
|
2
|
-
import { WriteConcernError, AuthorityError, SyncError, TopologyError, CoordinatorError, StalePrimaryError, NodeDrainingError, NodeNotInSyncError, ReadConcernError, ProtocolVersionMismatchError } from '../chunk-
|
|
3
|
-
export { AuthorityError, ConflictError, CoordinatorError, FailoverError, NoSafePrimaryError, NodeDrainingError, NodeNotInSyncError, ProtocolVersionMismatchError, ReadConcernError, StalePrimaryError, SyncError, TopologyError, TransportError, UnsafeRecoveryRequiredError, WriteConcernError } from '../chunk-
|
|
4
|
-
import { LWWResolver, HLC, BatchReader, BatchApplier, StampOps, isWellFormedHlc, Transaction, loadPersistedHlc } from '../chunk-
|
|
5
|
-
export { HLC, LWWResolver } from '../chunk-
|
|
6
|
-
import '../chunk-
|
|
7
|
-
import { DumpOps, dumpSchema, tablesInFkOrder } from '../chunk-
|
|
8
|
-
import { canonicaliseForChecksum, PkResolver, setForeignKeysEnabled,
|
|
9
|
-
import { ReplicationError, BatchValidationError } from '../chunk-
|
|
10
|
-
export { BatchValidationError, ReplicationError } from '../chunk-
|
|
11
|
-
import { CHANGES_TABLE, INTERNAL_TABLE_PREFIX } from '../chunk-
|
|
2
|
+
import { WriteConcernError, AuthorityError, SyncError, TopologyError, CoordinatorError, StalePrimaryError, NodeDrainingError, NodeNotInSyncError, ReadConcernError, ProtocolVersionMismatchError } from '../chunk-BTTFW4Z4.mjs';
|
|
3
|
+
export { AuthorityError, ConflictError, CoordinatorError, FailoverError, NoSafePrimaryError, NodeDrainingError, NodeNotInSyncError, ProtocolVersionMismatchError, ReadConcernError, StalePrimaryError, SyncError, TopologyError, TransportError, UnsafeRecoveryRequiredError, WriteConcernError } from '../chunk-BTTFW4Z4.mjs';
|
|
4
|
+
import { LWWResolver, HLC, BatchReader, BatchApplier, StampOps, isWellFormedHlc, Transaction, loadPersistedHlc } from '../chunk-P2VJYRVY.mjs';
|
|
5
|
+
export { HLC, LWWResolver } from '../chunk-P2VJYRVY.mjs';
|
|
6
|
+
import '../chunk-HCCGEIZ2.mjs';
|
|
7
|
+
import { DumpOps, dumpSchema, tablesInFkOrder } from '../chunk-XF2HH5E6.mjs';
|
|
8
|
+
import { canonicaliseForChecksum, PkResolver, setForeignKeysEnabled, selectMaxChangeHlc, selectMaxColumnVersionHlc, selectMaxAppliedSourceSeq, upsertPeerAckedSeq, selectPeerAckedSeq, selectMaxChangeSeq, selectMinPeerAckedSeq, upsertSyncTableStatus, upsertSyncMeta, selectSyncMetaRow, selectCompletedSyncTableNames, ensureChangesTable, ensureReplicationStateTables, ensureMetaTable, IDENTIFIER_RE, deleteSyncTableStates, extractDroppedTable, insertDdlChange, prepareInsertAppliedChange, selectMaxAppliedSourceSeqByNode } from '../chunk-7FQRQH5Z.mjs';
|
|
9
|
+
import { ReplicationError, BatchValidationError } from '../chunk-2QLXDHAP.mjs';
|
|
10
|
+
export { BatchValidationError, ReplicationError } from '../chunk-2QLXDHAP.mjs';
|
|
11
|
+
import { CHANGES_TABLE, INTERNAL_TABLE_PREFIX } from '../chunk-7R4ER4FB.mjs';
|
|
12
12
|
import '../chunk-GVCNMPOS.mjs';
|
|
13
|
-
import '../chunk-
|
|
13
|
+
import '../chunk-PBRXXISQ.mjs';
|
|
14
14
|
import { EventEmitter } from 'events';
|
|
15
15
|
import { randomBytes, randomUUID, createHash } from 'crypto';
|
|
16
16
|
|
|
@@ -21,6 +21,12 @@ var FieldMergeResolver = class {
|
|
|
21
21
|
constructor(getColumnVersions) {
|
|
22
22
|
this.getColumnVersions = getColumnVersions;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Merges columns only one side changed, and settles overlapping columns by their per-column stamps.
|
|
26
|
+
*
|
|
27
|
+
* @param ctx - The local and incoming versions of one row.
|
|
28
|
+
* @returns Which version to write, or the merged row.
|
|
29
|
+
*/
|
|
24
30
|
async resolve(ctx) {
|
|
25
31
|
const columnVersions = await this.getColumnVersions(ctx.table, ctx.rowId);
|
|
26
32
|
if (columnVersions.size === 0) {
|
|
@@ -95,6 +101,12 @@ var PrimaryWinsResolver = class {
|
|
|
95
101
|
constructor(primaryNodeId) {
|
|
96
102
|
this.primaryNodeId = primaryNodeId;
|
|
97
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Takes the version authored by the configured primary node, and falls back to last-writer-wins otherwise.
|
|
106
|
+
*
|
|
107
|
+
* @param ctx - The local and incoming versions of one row.
|
|
108
|
+
* @returns Which version to write.
|
|
109
|
+
*/
|
|
98
110
|
resolve(ctx) {
|
|
99
111
|
if (ctx.remoteChange.nodeId === this.primaryNodeId) {
|
|
100
112
|
return { action: "accept_remote" };
|
|
@@ -246,7 +258,7 @@ var ReplicationLog = class {
|
|
|
246
258
|
changesTable
|
|
247
259
|
);
|
|
248
260
|
this.stampOps = new StampOps(localNodeId, hlc, changesTable);
|
|
249
|
-
this.dump = new DumpOps(conn,
|
|
261
|
+
this.dump = new DumpOps(conn, this.pkResolver);
|
|
250
262
|
}
|
|
251
263
|
pkResolver;
|
|
252
264
|
state;
|
|
@@ -294,9 +306,6 @@ var ReplicationLog = class {
|
|
|
294
306
|
unregisterActiveSyncSeq(seq) {
|
|
295
307
|
this.state.unregisterActiveSyncSeq(seq);
|
|
296
308
|
}
|
|
297
|
-
dumpTable(table, batchSize) {
|
|
298
|
-
return this.dump.dumpTable(table, batchSize);
|
|
299
|
-
}
|
|
300
309
|
dumpTableOnConnection(conn, table, batchSize) {
|
|
301
310
|
return this.dump.dumpTableOnConnection(conn, table, batchSize);
|
|
302
311
|
}
|
|
@@ -657,6 +666,14 @@ async function refreshCoordinatorState(engine) {
|
|
|
657
666
|
);
|
|
658
667
|
}
|
|
659
668
|
}
|
|
669
|
+
function resolverFor(engine, table) {
|
|
670
|
+
const specific = table ? engine.config.conflictResolvers?.[table] : void 0;
|
|
671
|
+
return specific ?? engine.defaultResolver;
|
|
672
|
+
}
|
|
673
|
+
function resolveWriteConcern(engine, wc) {
|
|
674
|
+
if (wc) return wc;
|
|
675
|
+
return engine.isCoordinatorMode() ? { level: "majority" } : void 0;
|
|
676
|
+
}
|
|
660
677
|
function getCoordinatorMessageFields(engine) {
|
|
661
678
|
const config = engine.config.coordinator;
|
|
662
679
|
if (!config) return {};
|
|
@@ -1409,6 +1426,19 @@ async function forwardStatements(engine, statements, _options) {
|
|
|
1409
1426
|
});
|
|
1410
1427
|
}
|
|
1411
1428
|
|
|
1429
|
+
// src/replication/engine/internal-types.ts
|
|
1430
|
+
function initialSyncState() {
|
|
1431
|
+
return {
|
|
1432
|
+
phase: "ready",
|
|
1433
|
+
sourcePeerId: null,
|
|
1434
|
+
snapshotSeq: null,
|
|
1435
|
+
completedTables: [],
|
|
1436
|
+
totalTables: 0,
|
|
1437
|
+
startedAt: null,
|
|
1438
|
+
error: null
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1412
1442
|
// src/replication/engine/replication-transaction.ts
|
|
1413
1443
|
var ReplicationTransaction = class extends Transaction {
|
|
1414
1444
|
constructor(txConn, hooks) {
|
|
@@ -1435,6 +1465,18 @@ var ReplicationTransaction = class extends Transaction {
|
|
|
1435
1465
|
}
|
|
1436
1466
|
};
|
|
1437
1467
|
|
|
1468
|
+
// src/replication/engine/trigger-refresh.ts
|
|
1469
|
+
async function refreshTriggersAfterDdl(engine) {
|
|
1470
|
+
const tracker = engine.tracker;
|
|
1471
|
+
if (!tracker) return;
|
|
1472
|
+
for (const table of Array.from(tracker.watchedTables)) {
|
|
1473
|
+
try {
|
|
1474
|
+
await tracker.watch(engine.writerConn, table);
|
|
1475
|
+
} catch {
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1438
1480
|
// src/replication/engine/local-executor.ts
|
|
1439
1481
|
var LocalExecutor = class {
|
|
1440
1482
|
constructor(engine) {
|
|
@@ -1488,11 +1530,11 @@ var LocalExecutor = class {
|
|
|
1488
1530
|
if (droppedTable !== null && engine.tracker) {
|
|
1489
1531
|
await engine.tracker.pruneDroppedTables(engine.writerConn, [droppedTable]);
|
|
1490
1532
|
}
|
|
1491
|
-
await
|
|
1533
|
+
await refreshTriggersAfterDdl(engine);
|
|
1492
1534
|
}
|
|
1493
|
-
const writeConcern =
|
|
1535
|
+
const writeConcern = resolveWriteConcern(engine, options?.writeConcern);
|
|
1494
1536
|
if (writeConcern) {
|
|
1495
|
-
await
|
|
1537
|
+
await waitForWriteConcern(engine, newSeq, writeConcern);
|
|
1496
1538
|
}
|
|
1497
1539
|
return result;
|
|
1498
1540
|
}
|
|
@@ -1556,7 +1598,7 @@ var LocalExecutor = class {
|
|
|
1556
1598
|
if (droppedTables.length > 0 && engine.tracker) {
|
|
1557
1599
|
await engine.tracker.pruneDroppedTables(engine.writerConn, droppedTables);
|
|
1558
1600
|
}
|
|
1559
|
-
await
|
|
1601
|
+
await refreshTriggersAfterDdl(engine);
|
|
1560
1602
|
}
|
|
1561
1603
|
return { results, requestId };
|
|
1562
1604
|
}
|
|
@@ -1609,11 +1651,11 @@ var LocalExecutor = class {
|
|
|
1609
1651
|
if (hooks.droppedTables.length > 0 && engine.tracker) {
|
|
1610
1652
|
await engine.tracker.pruneDroppedTables(engine.writerConn, hooks.droppedTables);
|
|
1611
1653
|
}
|
|
1612
|
-
await
|
|
1654
|
+
await refreshTriggersAfterDdl(engine);
|
|
1613
1655
|
}
|
|
1614
|
-
const writeConcern =
|
|
1656
|
+
const writeConcern = resolveWriteConcern(engine, options?.writeConcern);
|
|
1615
1657
|
if (writeConcern) {
|
|
1616
|
-
await
|
|
1658
|
+
await waitForWriteConcern(engine, newSeq, writeConcern);
|
|
1617
1659
|
}
|
|
1618
1660
|
return userResult;
|
|
1619
1661
|
}
|
|
@@ -1725,7 +1767,7 @@ var SenderLoop = class {
|
|
|
1725
1767
|
const fromSeq = peerState?.lastSentSeq ?? engine.lastSentSeq;
|
|
1726
1768
|
const rawBatch = await engine.log.readBatch(fromSeq, engine.batchSize);
|
|
1727
1769
|
if (!rawBatch) continue;
|
|
1728
|
-
const batch = engine.
|
|
1770
|
+
const batch = engine.decorate(rawBatch);
|
|
1729
1771
|
const previousSeq = peerState?.lastSentSeq ?? 0n;
|
|
1730
1772
|
if (peerState) {
|
|
1731
1773
|
peerState.pendingBatches += 1;
|
|
@@ -1796,6 +1838,9 @@ async function delayAckIfConfigured(engine) {
|
|
|
1796
1838
|
}
|
|
1797
1839
|
|
|
1798
1840
|
// src/replication/engine/transport-wiring.ts
|
|
1841
|
+
function clockDriftMs(remoteHlc) {
|
|
1842
|
+
return Math.abs(Date.now() - HLC.decode(remoteHlc).wallMs);
|
|
1843
|
+
}
|
|
1799
1844
|
function replicatesFrom(engine, peer) {
|
|
1800
1845
|
if (engine.isCoordinatorMode()) {
|
|
1801
1846
|
return engine.coordinatorState?.currentPrimary?.nodeId === peer.id;
|
|
@@ -1811,11 +1856,11 @@ async function reportAppliedPosition(engine, peer, appliedSeq) {
|
|
|
1811
1856
|
if (!servesItsData((await engine.log.getSyncState()).phase)) return;
|
|
1812
1857
|
await engine.config.transport.sendAck(
|
|
1813
1858
|
peer.id,
|
|
1814
|
-
engine.
|
|
1859
|
+
engine.decorate({ batchId: "", ackedSeq: appliedSeq, nodeId: engine.nodeId })
|
|
1815
1860
|
);
|
|
1816
1861
|
}
|
|
1817
1862
|
function persistAckProgress(engine, nodeId, ackedSeq) {
|
|
1818
|
-
engine.log.setLastAppliedSeq(nodeId, ackedSeq).then(() =>
|
|
1863
|
+
engine.log.setLastAppliedSeq(nodeId, ackedSeq).then(() => handleCoordinatorAckProgress(engine, nodeId, ackedSeq)).catch((err) => {
|
|
1819
1864
|
const wrappedErr = err instanceof Error ? err : new Error(String(err));
|
|
1820
1865
|
engine.emitError({ error: wrappedErr, operation: "ack-state-persist", peerId: nodeId, recoverable: true });
|
|
1821
1866
|
});
|
|
@@ -1840,23 +1885,23 @@ function wireTransportHandlers(engine) {
|
|
|
1840
1885
|
throw new ReplicationError(`Rejected batch from unauthorized peer: ${fromPeerId}`);
|
|
1841
1886
|
}
|
|
1842
1887
|
}
|
|
1843
|
-
await
|
|
1888
|
+
await assertInboundCoordinatorMessage(engine, batch, fromPeerId, "batch");
|
|
1844
1889
|
if (batch.changes.length > engine.maxBatchChanges) {
|
|
1845
1890
|
throw new BatchValidationError(
|
|
1846
1891
|
`Batch too large: ${batch.changes.length} changes exceeds max ${engine.maxBatchChanges}`
|
|
1847
1892
|
);
|
|
1848
1893
|
}
|
|
1849
|
-
const drift =
|
|
1894
|
+
const drift = clockDriftMs(batch.hlcRange.max);
|
|
1850
1895
|
if (drift > engine.maxClockDriftMs) {
|
|
1851
1896
|
throw new BatchValidationError(`Clock drift too high: ${drift}ms exceeds max ${engine.maxClockDriftMs}ms`);
|
|
1852
1897
|
}
|
|
1853
|
-
const applyResult = await engine.log.applyBatch(batch, (table) => engine
|
|
1898
|
+
const applyResult = await engine.log.applyBatch(batch, (table) => resolverFor(engine, table));
|
|
1854
1899
|
const batchContainedDdl = batch.changes.some((c) => c.operation === "ddl");
|
|
1855
1900
|
if (batchContainedDdl) {
|
|
1856
1901
|
if (applyResult.droppedTables.length > 0 && engine.tracker) {
|
|
1857
1902
|
await engine.tracker.pruneDroppedTables(engine.writerConn, applyResult.droppedTables);
|
|
1858
1903
|
}
|
|
1859
|
-
await
|
|
1904
|
+
await refreshTriggersAfterDdl(engine);
|
|
1860
1905
|
}
|
|
1861
1906
|
await engine.log.setLastAppliedSeq(fromPeerId, batch.toSeq);
|
|
1862
1907
|
const previousApplied = engine.appliedSeqByPeer.get(fromPeerId) ?? 0n;
|
|
@@ -1869,7 +1914,7 @@ function wireTransportHandlers(engine) {
|
|
|
1869
1914
|
await delayAckIfConfigured(engine);
|
|
1870
1915
|
await engine.config.transport.sendAck(
|
|
1871
1916
|
fromPeerId,
|
|
1872
|
-
engine.
|
|
1917
|
+
engine.decorate({
|
|
1873
1918
|
batchId: batch.batchId,
|
|
1874
1919
|
ackedSeq: batch.toSeq,
|
|
1875
1920
|
nodeId: engine.nodeId
|
|
@@ -1900,7 +1945,7 @@ function wireTransportHandlers(engine) {
|
|
|
1900
1945
|
persistAckProgress(engine, ack.nodeId, ack.ackedSeq);
|
|
1901
1946
|
return;
|
|
1902
1947
|
}
|
|
1903
|
-
|
|
1948
|
+
assertInboundCoordinatorMessage(engine, ack, fromPeerId, "ack").then(() => {
|
|
1904
1949
|
engine.peerTracker.onAckReceived(ack.nodeId, ack.ackedSeq);
|
|
1905
1950
|
persistAckProgress(engine, ack.nodeId, ack.ackedSeq);
|
|
1906
1951
|
}).catch((err) => {
|
|
@@ -1913,14 +1958,14 @@ function wireTransportHandlers(engine) {
|
|
|
1913
1958
|
if (engine.syncState.phase !== "ready" && engine.syncState.phase !== "catching-up") {
|
|
1914
1959
|
throw new ReplicationError("Node is not ready to handle forwarded requests");
|
|
1915
1960
|
}
|
|
1916
|
-
await
|
|
1961
|
+
await assertInboundCoordinatorMessage(engine, request, fromPeerId, "forward");
|
|
1917
1962
|
const knownPeers = engine.config.transport.peers();
|
|
1918
1963
|
if (!knownPeers.has(fromPeerId)) {
|
|
1919
1964
|
throw new ReplicationError(`Rejected forward from unknown peer: ${fromPeerId}`);
|
|
1920
1965
|
}
|
|
1921
1966
|
try {
|
|
1922
1967
|
const result = await engine.localExecutor.executeForwardedLocally(request.statements);
|
|
1923
|
-
return engine.
|
|
1968
|
+
return engine.decorate(result);
|
|
1924
1969
|
} catch (err) {
|
|
1925
1970
|
const wrappedErr = err instanceof Error ? err : new Error(String(err));
|
|
1926
1971
|
engine.emitError({ error: wrappedErr, operation: "forward-execution", peerId: fromPeerId, recoverable: true });
|
|
@@ -1934,7 +1979,7 @@ function wireTransportHandlers(engine) {
|
|
|
1934
1979
|
if (!engine.running) return;
|
|
1935
1980
|
engine.peerTracker.onAckReceived(peer.id, ackedSeq);
|
|
1936
1981
|
await engine.log.setLastAppliedSeq(peer.id, ackedSeq);
|
|
1937
|
-
await
|
|
1982
|
+
await handleCoordinatorAckProgress(engine, peer.id, ackedSeq);
|
|
1938
1983
|
await reportAppliedPosition(engine, peer, ackedSeq);
|
|
1939
1984
|
}).catch((err) => {
|
|
1940
1985
|
const wrappedErr = err instanceof Error ? err : new Error(String(err));
|
|
@@ -1967,17 +2012,17 @@ function wireTransportHandlers(engine) {
|
|
|
1967
2012
|
});
|
|
1968
2013
|
engine.config.transport.onSyncRequested(async (request, fromPeerId) => {
|
|
1969
2014
|
if (!engine.running) return;
|
|
1970
|
-
await
|
|
2015
|
+
await assertInboundCoordinatorMessage(engine, request, fromPeerId, "sync-request");
|
|
1971
2016
|
await engine.syncServer.handleSyncRequest(request, fromPeerId);
|
|
1972
2017
|
});
|
|
1973
2018
|
engine.config.transport.onSyncBatchReceived(async (batch, fromPeerId) => {
|
|
1974
2019
|
if (!engine.running) return;
|
|
1975
|
-
await
|
|
2020
|
+
await assertInboundCoordinatorMessage(engine, batch, fromPeerId, "sync-data");
|
|
1976
2021
|
await engine.syncJoiner.handleSyncBatchReceived(batch, fromPeerId);
|
|
1977
2022
|
});
|
|
1978
2023
|
engine.config.transport.onSyncCompleteReceived(async (complete, fromPeerId) => {
|
|
1979
2024
|
if (!engine.running) return;
|
|
1980
|
-
await
|
|
2025
|
+
await assertInboundCoordinatorMessage(engine, complete, fromPeerId, "sync-data");
|
|
1981
2026
|
await engine.syncJoiner.handleSyncCompleteReceived(complete, fromPeerId);
|
|
1982
2027
|
});
|
|
1983
2028
|
engine.config.transport.onSyncAckReceived((ack, fromPeerId) => {
|
|
@@ -2002,7 +2047,7 @@ function wireTransportHandlers(engine) {
|
|
|
2002
2047
|
engine.syncServer.handleSyncAckReceived(ack);
|
|
2003
2048
|
return;
|
|
2004
2049
|
}
|
|
2005
|
-
|
|
2050
|
+
assertInboundCoordinatorMessage(engine, ack, fromPeerId, "ack").then(() => {
|
|
2006
2051
|
engine.syncServer.handleSyncAckReceived(ack);
|
|
2007
2052
|
}).catch((err) => {
|
|
2008
2053
|
const wrappedErr = err instanceof Error ? err : new Error(String(err));
|
|
@@ -2012,6 +2057,11 @@ function wireTransportHandlers(engine) {
|
|
|
2012
2057
|
}
|
|
2013
2058
|
|
|
2014
2059
|
// src/replication/engine/startup.ts
|
|
2060
|
+
async function loadAppliedSeqs(engine) {
|
|
2061
|
+
for (const [nodeId, seq] of await selectMaxAppliedSourceSeqByNode(engine.writerConn)) {
|
|
2062
|
+
engine.appliedSeqByPeer.set(nodeId, seq);
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2015
2065
|
async function recoverHlcFromDurableState(engine) {
|
|
2016
2066
|
const persisted = await loadPersistedHlc(engine.writerConn);
|
|
2017
2067
|
if (persisted !== null) {
|
|
@@ -2028,9 +2078,9 @@ async function startEngine(engine) {
|
|
|
2028
2078
|
await recoverHlcFromDurableState(engine);
|
|
2029
2079
|
engine.lastSentSeq = await engine.log.getLocalSeq();
|
|
2030
2080
|
engine.lastLocalSeq = engine.lastSentSeq;
|
|
2031
|
-
await
|
|
2032
|
-
await
|
|
2033
|
-
await
|
|
2081
|
+
await loadAppliedSeqs(engine);
|
|
2082
|
+
await startCoordinatorMode(engine);
|
|
2083
|
+
await prepareCoordinatorRejoinIfNeeded(engine);
|
|
2034
2084
|
wireTransportHandlers(engine);
|
|
2035
2085
|
const transportConfig = {
|
|
2036
2086
|
...engine.config.transportConfig,
|
|
@@ -2040,10 +2090,10 @@ async function startEngine(engine) {
|
|
|
2040
2090
|
protocolVersion: engine.config.coordinator?.compatibility?.protocolVersion ?? engine.config.transportConfig?.protocolVersion
|
|
2041
2091
|
};
|
|
2042
2092
|
await engine.config.transport.connect(engine.nodeId, transportConfig);
|
|
2043
|
-
const isPrimary = engine.isCoordinatorMode() ? engine.
|
|
2093
|
+
const isPrimary = engine.isCoordinatorMode() ? engine.coordinatorAuthority : engine.config.topology.role === "primary";
|
|
2044
2094
|
const syncCompleted = await engine.log.isSyncCompleted();
|
|
2045
|
-
const
|
|
2046
|
-
if (engine.initialSync && !isPrimary && (!syncCompleted ||
|
|
2095
|
+
const rejoinSyncRequired = requiresCoordinatorRejoinSync(engine, engine.coordinatorState);
|
|
2096
|
+
if (engine.initialSync && !isPrimary && (!syncCompleted || rejoinSyncRequired)) {
|
|
2047
2097
|
const savedState = await engine.log.getSyncState();
|
|
2048
2098
|
if (savedState.phase === "syncing") {
|
|
2049
2099
|
if (!engine.tracker) {
|
|
@@ -2080,7 +2130,7 @@ async function startEngine(engine) {
|
|
|
2080
2130
|
startedAt: null,
|
|
2081
2131
|
error: null
|
|
2082
2132
|
};
|
|
2083
|
-
engine.
|
|
2133
|
+
engine.senderLoop.start();
|
|
2084
2134
|
engine.syncJoiner.startCatchUpCheck();
|
|
2085
2135
|
return;
|
|
2086
2136
|
}
|
|
@@ -2099,7 +2149,7 @@ async function startEngine(engine) {
|
|
|
2099
2149
|
} else {
|
|
2100
2150
|
engine.syncState.phase = "ready";
|
|
2101
2151
|
}
|
|
2102
|
-
engine.
|
|
2152
|
+
engine.senderLoop.start();
|
|
2103
2153
|
}
|
|
2104
2154
|
|
|
2105
2155
|
// src/replication/engine/sync-recovery.ts
|
|
@@ -2270,7 +2320,7 @@ var SyncJoiner = class {
|
|
|
2270
2320
|
await engine.log.setSyncMeta("syncing", void 0, sourcePeerId, requestId);
|
|
2271
2321
|
await engine.config.transport.requestSync(
|
|
2272
2322
|
sourcePeerId,
|
|
2273
|
-
engine.
|
|
2323
|
+
engine.decorate({
|
|
2274
2324
|
requestId,
|
|
2275
2325
|
joinerNodeId: engine.nodeId,
|
|
2276
2326
|
completedTables,
|
|
@@ -2463,7 +2513,7 @@ var SyncJoiner = class {
|
|
|
2463
2513
|
engine.syncState.phase = "catching-up";
|
|
2464
2514
|
engine.syncState.snapshotSeq = complete.snapshotSeq;
|
|
2465
2515
|
await engine.log.setSyncMeta("catching-up", complete.snapshotSeq);
|
|
2466
|
-
engine.
|
|
2516
|
+
engine.senderLoop.start();
|
|
2467
2517
|
this.startCatchUpCheck();
|
|
2468
2518
|
} catch {
|
|
2469
2519
|
try {
|
|
@@ -2539,7 +2589,7 @@ var SyncJoiner = class {
|
|
|
2539
2589
|
}
|
|
2540
2590
|
async sendSyncAck(peerId, ack) {
|
|
2541
2591
|
await delayAckIfConfigured(this.engine);
|
|
2542
|
-
await this.engine.config.transport.sendSyncAck(peerId, this.engine.
|
|
2592
|
+
await this.engine.config.transport.sendSyncAck(peerId, this.engine.decorate(ack));
|
|
2543
2593
|
}
|
|
2544
2594
|
async finishCatchUpAsReady() {
|
|
2545
2595
|
const engine = this.engine;
|
|
@@ -2559,7 +2609,7 @@ var SyncServer = class {
|
|
|
2559
2609
|
async rejectSyncRequest(fromPeerId, request, error) {
|
|
2560
2610
|
await this.engine.config.transport.sendSyncAck(
|
|
2561
2611
|
fromPeerId,
|
|
2562
|
-
this.engine.
|
|
2612
|
+
this.engine.decorate({
|
|
2563
2613
|
requestId: request.requestId,
|
|
2564
2614
|
joinerNodeId: request.joinerNodeId,
|
|
2565
2615
|
table: "__schema__",
|
|
@@ -2729,7 +2779,7 @@ var SyncServer = class {
|
|
|
2729
2779
|
async sendSyncBatchAndWaitForAck(peerId, batch) {
|
|
2730
2780
|
const ackPromise = this.waitForSyncAck(batch.requestId, batch.table, batch.batchIndex);
|
|
2731
2781
|
try {
|
|
2732
|
-
await this.engine.config.transport.sendSyncBatch(peerId, this.engine.
|
|
2782
|
+
await this.engine.config.transport.sendSyncBatch(peerId, this.engine.decorate(batch));
|
|
2733
2783
|
} catch (err) {
|
|
2734
2784
|
const key = `${batch.requestId}:${batch.table}:${batch.batchIndex}`;
|
|
2735
2785
|
const waiter = this.syncAckWaiters.get(key);
|
|
@@ -2837,7 +2887,7 @@ var SyncServer = class {
|
|
|
2837
2887
|
}
|
|
2838
2888
|
await engine.config.transport.sendSyncComplete(
|
|
2839
2889
|
session.joinerNodeId,
|
|
2840
|
-
engine.
|
|
2890
|
+
engine.decorate({
|
|
2841
2891
|
requestId: session.requestId,
|
|
2842
2892
|
snapshotSeq: session.snapshotSeq,
|
|
2843
2893
|
manifests
|
|
@@ -2873,62 +2923,105 @@ var SyncServer = class {
|
|
|
2873
2923
|
|
|
2874
2924
|
// src/replication/engine/engine.ts
|
|
2875
2925
|
var ReplicationEngine = class extends EventEmitter {
|
|
2926
|
+
/** @internal */
|
|
2876
2927
|
database;
|
|
2928
|
+
/** @internal */
|
|
2877
2929
|
writerConn;
|
|
2930
|
+
/** @internal */
|
|
2878
2931
|
config;
|
|
2932
|
+
/**
|
|
2933
|
+
* Identifier of this node, which every change it authors carries.
|
|
2934
|
+
*/
|
|
2879
2935
|
nodeId;
|
|
2936
|
+
/** @internal */
|
|
2880
2937
|
hlc;
|
|
2938
|
+
/** @internal */
|
|
2881
2939
|
log;
|
|
2940
|
+
/** @internal */
|
|
2882
2941
|
peerTracker = new PeerTracker();
|
|
2942
|
+
/** @internal */
|
|
2883
2943
|
defaultResolver;
|
|
2944
|
+
/** @internal */
|
|
2884
2945
|
tracker;
|
|
2946
|
+
/** @internal */
|
|
2885
2947
|
snapshotConnectionFactory;
|
|
2948
|
+
/** @internal */
|
|
2886
2949
|
batchSize;
|
|
2950
|
+
/** @internal */
|
|
2887
2951
|
batchIntervalMs;
|
|
2952
|
+
/** @internal */
|
|
2888
2953
|
maxClockDriftMs;
|
|
2954
|
+
/** @internal */
|
|
2889
2955
|
maxPendingBatches;
|
|
2956
|
+
/** @internal */
|
|
2890
2957
|
maxBatchChanges;
|
|
2958
|
+
/** @internal */
|
|
2891
2959
|
ackTimeoutMs;
|
|
2960
|
+
/** @internal */
|
|
2892
2961
|
initialSync;
|
|
2962
|
+
/** @internal */
|
|
2893
2963
|
syncBatchSize;
|
|
2964
|
+
/** @internal */
|
|
2894
2965
|
maxConcurrentSyncs;
|
|
2966
|
+
/** @internal */
|
|
2895
2967
|
maxSyncDurationMs;
|
|
2968
|
+
/** @internal */
|
|
2896
2969
|
maxSyncLagBeforeReady;
|
|
2970
|
+
/** @internal */
|
|
2897
2971
|
syncAckTimeoutMs;
|
|
2972
|
+
/** @internal */
|
|
2898
2973
|
catchUpDeadlineMs;
|
|
2974
|
+
/** @internal */
|
|
2899
2975
|
resumeFromSeq;
|
|
2976
|
+
/** @internal */
|
|
2900
2977
|
running = false;
|
|
2978
|
+
/** @internal */
|
|
2901
2979
|
coordinatorState = null;
|
|
2980
|
+
/** @internal */
|
|
2902
2981
|
coordinatorAuthority = false;
|
|
2982
|
+
/** @internal */
|
|
2903
2983
|
controllerState = "disabled";
|
|
2984
|
+
/** @internal */
|
|
2904
2985
|
nodeSessionLeaseId = null;
|
|
2986
|
+
/** @internal */
|
|
2905
2987
|
controllerLeaseId = null;
|
|
2988
|
+
/** @internal */
|
|
2906
2989
|
coordinatorWatchDisposer = null;
|
|
2990
|
+
/** @internal */
|
|
2907
2991
|
coordinatorLeaseTimer = null;
|
|
2992
|
+
/** @internal */
|
|
2908
2993
|
controllerTimer = null;
|
|
2994
|
+
/** @internal */
|
|
2909
2995
|
coordinatorRejoinSyncStarting = false;
|
|
2996
|
+
/** @internal */
|
|
2910
2997
|
coordinatorSessionRestoring = false;
|
|
2998
|
+
/** @internal */
|
|
2911
2999
|
coordinatorLastContactMs = 0;
|
|
3000
|
+
/** @internal */
|
|
2912
3001
|
inSyncReconcileTimer = null;
|
|
3002
|
+
/** @internal */
|
|
2913
3003
|
inSyncReconciling = false;
|
|
3004
|
+
/** @internal */
|
|
2914
3005
|
lastSentSeq = 0n;
|
|
3006
|
+
/** @internal */
|
|
2915
3007
|
lastLocalSeq = 0n;
|
|
3008
|
+
/** @internal */
|
|
2916
3009
|
highestSourceSeqSeen = 0n;
|
|
3010
|
+
/** @internal */
|
|
2917
3011
|
appliedSeqByPeer = /* @__PURE__ */ new Map();
|
|
3012
|
+
/** @internal */
|
|
2918
3013
|
expectedBatchIndex = /* @__PURE__ */ new Map();
|
|
3014
|
+
/** @internal */
|
|
2919
3015
|
syncTableDigests = /* @__PURE__ */ new Map();
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
snapshotSeq: null,
|
|
2924
|
-
completedTables: [],
|
|
2925
|
-
totalTables: 0,
|
|
2926
|
-
startedAt: null,
|
|
2927
|
-
error: null
|
|
2928
|
-
};
|
|
3016
|
+
/** @internal */
|
|
3017
|
+
syncState = initialSyncState();
|
|
3018
|
+
/** @internal */
|
|
2929
3019
|
localExecutor;
|
|
3020
|
+
/** @internal */
|
|
2930
3021
|
syncServer;
|
|
3022
|
+
/** @internal */
|
|
2931
3023
|
syncJoiner;
|
|
3024
|
+
/** @internal */
|
|
2932
3025
|
senderLoop;
|
|
2933
3026
|
constructor(database, writerConn, config) {
|
|
2934
3027
|
super();
|
|
@@ -2964,9 +3057,15 @@ var ReplicationEngine = class extends EventEmitter {
|
|
|
2964
3057
|
this.senderLoop = new SenderLoop(this);
|
|
2965
3058
|
installTestHooks(this);
|
|
2966
3059
|
}
|
|
3060
|
+
/**
|
|
3061
|
+
* Connects the transport, pulls a full copy when this node needs one, and starts replicating.
|
|
3062
|
+
*/
|
|
2967
3063
|
start() {
|
|
2968
3064
|
return startEngine(this);
|
|
2969
3065
|
}
|
|
3066
|
+
/**
|
|
3067
|
+
* Stops replicating, abandons any sync in flight, and disconnects the transport.
|
|
3068
|
+
*/
|
|
2970
3069
|
async stop() {
|
|
2971
3070
|
if (!this.running) return;
|
|
2972
3071
|
this.running = false;
|
|
@@ -2988,6 +3087,11 @@ var ReplicationEngine = class extends EventEmitter {
|
|
|
2988
3087
|
await stopCoordinatorMode(this);
|
|
2989
3088
|
await this.config.transport.disconnect();
|
|
2990
3089
|
}
|
|
3090
|
+
/**
|
|
3091
|
+
* Reports where this node stands.
|
|
3092
|
+
*
|
|
3093
|
+
* @returns The node's role, its peers, its progress, its health, and its group state.
|
|
3094
|
+
*/
|
|
2991
3095
|
status() {
|
|
2992
3096
|
return {
|
|
2993
3097
|
nodeId: this.nodeId,
|
|
@@ -3000,30 +3104,77 @@ var ReplicationEngine = class extends EventEmitter {
|
|
|
3000
3104
|
coordinator: getCoordinatorRuntimeStatus(this)
|
|
3001
3105
|
};
|
|
3002
3106
|
}
|
|
3107
|
+
/**
|
|
3108
|
+
* Returns the highest change-log position this node has recorded locally.
|
|
3109
|
+
*
|
|
3110
|
+
* @returns That position, which a caller waits for a replica to reach.
|
|
3111
|
+
*/
|
|
3003
3112
|
getCurrentSeq() {
|
|
3004
3113
|
return this.lastLocalSeq;
|
|
3005
3114
|
}
|
|
3115
|
+
/**
|
|
3116
|
+
* Returns how far this node has applied one peer's changes.
|
|
3117
|
+
*
|
|
3118
|
+
* @param peerId - Identifier of the peer.
|
|
3119
|
+
* @returns The highest position from that peer this node has applied.
|
|
3120
|
+
*/
|
|
3006
3121
|
getAppliedSeq(peerId) {
|
|
3007
3122
|
return this.appliedSeqByPeer.get(peerId) ?? 0n;
|
|
3008
3123
|
}
|
|
3124
|
+
/**
|
|
3125
|
+
* Runs a read, refusing it when the node cannot meet the read concern.
|
|
3126
|
+
*
|
|
3127
|
+
* @param sql - The statement to run.
|
|
3128
|
+
* @param params - Values bound to the statement, named or positional.
|
|
3129
|
+
* @param options - Read concern for this statement.
|
|
3130
|
+
* @returns The rows the statement produced.
|
|
3131
|
+
*/
|
|
3009
3132
|
query(sql, params, options) {
|
|
3010
3133
|
return query(this, sql, params, options);
|
|
3011
3134
|
}
|
|
3135
|
+
/**
|
|
3136
|
+
* Runs one write, forwarding it to the primary when this node accepts no writes and forwarding is on.
|
|
3137
|
+
*
|
|
3138
|
+
* @param sql - The statement to run.
|
|
3139
|
+
* @param params - Values bound to the statement, named or positional.
|
|
3140
|
+
* @param options - Write concern for this statement.
|
|
3141
|
+
* @returns How many rows changed, and the last inserted row id.
|
|
3142
|
+
*/
|
|
3012
3143
|
execute(sql, params, options) {
|
|
3013
3144
|
return execute(this, sql, params, options);
|
|
3014
3145
|
}
|
|
3146
|
+
/**
|
|
3147
|
+
* Runs one statement over many parameter sets in a single transaction.
|
|
3148
|
+
*
|
|
3149
|
+
* @param sql - The statement to run for each parameter set.
|
|
3150
|
+
* @param paramsBatch - One parameter set per run.
|
|
3151
|
+
* @param options - Write concern for the transaction.
|
|
3152
|
+
* @returns One result per parameter set, in order.
|
|
3153
|
+
*/
|
|
3015
3154
|
executeBatch(sql, paramsBatch, options) {
|
|
3016
3155
|
return executeBatch(this, sql, paramsBatch, options);
|
|
3017
3156
|
}
|
|
3157
|
+
/**
|
|
3158
|
+
* Runs a function inside one transaction on this node.
|
|
3159
|
+
*
|
|
3160
|
+
* @param fn - Receives the transaction and runs statements on it.
|
|
3161
|
+
* @param options - Write concern for the transaction.
|
|
3162
|
+
* @returns Whatever the function returned.
|
|
3163
|
+
*/
|
|
3018
3164
|
transaction(fn, options) {
|
|
3019
3165
|
return transaction(this, fn, options);
|
|
3020
3166
|
}
|
|
3167
|
+
/**
|
|
3168
|
+
* Sends a write to the primary and waits for its result.
|
|
3169
|
+
*
|
|
3170
|
+
* @param statements - The statements to run, in order, each with its own parameters.
|
|
3171
|
+
* @param options - Write concern the primary applies.
|
|
3172
|
+
* @returns What the primary reported for each statement.
|
|
3173
|
+
*/
|
|
3021
3174
|
forwardStatements(statements, options) {
|
|
3022
3175
|
return forwardStatements(this, statements);
|
|
3023
3176
|
}
|
|
3024
|
-
|
|
3025
|
-
this.senderLoop.start();
|
|
3026
|
-
}
|
|
3177
|
+
/** @internal */
|
|
3027
3178
|
emitError(event) {
|
|
3028
3179
|
if (this.listenerCount("replication-error") > 0) {
|
|
3029
3180
|
try {
|
|
@@ -3032,110 +3183,74 @@ var ReplicationEngine = class extends EventEmitter {
|
|
|
3032
3183
|
}
|
|
3033
3184
|
}
|
|
3034
3185
|
}
|
|
3035
|
-
|
|
3036
|
-
if (table && this.config.conflictResolvers) {
|
|
3037
|
-
const specific = this.config.conflictResolvers[table];
|
|
3038
|
-
if (specific) return specific;
|
|
3039
|
-
}
|
|
3040
|
-
return this.defaultResolver;
|
|
3041
|
-
}
|
|
3042
|
-
checkClockDrift(remoteHlc) {
|
|
3043
|
-
const decoded = HLC.decode(remoteHlc);
|
|
3044
|
-
return Math.abs(Date.now() - decoded.wallMs);
|
|
3045
|
-
}
|
|
3046
|
-
async refreshTriggersAfterDdl() {
|
|
3047
|
-
if (!this.tracker) return;
|
|
3048
|
-
const tables = Array.from(this.tracker.watchedTables);
|
|
3049
|
-
for (const table of tables) {
|
|
3050
|
-
try {
|
|
3051
|
-
await this.tracker.watch(this.writerConn, table);
|
|
3052
|
-
} catch {
|
|
3053
|
-
}
|
|
3054
|
-
}
|
|
3055
|
-
}
|
|
3056
|
-
waitForWriteConcern(seq, wc) {
|
|
3057
|
-
return waitForWriteConcern(this, seq, wc);
|
|
3058
|
-
}
|
|
3059
|
-
async loadAppliedSeqs() {
|
|
3060
|
-
for (const [nodeId, seq] of await selectMaxAppliedSourceSeqByNode(this.writerConn)) {
|
|
3061
|
-
this.appliedSeqByPeer.set(nodeId, seq);
|
|
3062
|
-
}
|
|
3063
|
-
}
|
|
3186
|
+
/** @internal */
|
|
3064
3187
|
isCoordinatorMode() {
|
|
3065
3188
|
return this.config.coordinator !== void 0;
|
|
3066
3189
|
}
|
|
3067
|
-
|
|
3068
|
-
return startCoordinatorMode(this);
|
|
3069
|
-
}
|
|
3070
|
-
prepareCoordinatorRejoinIfNeeded() {
|
|
3071
|
-
return prepareCoordinatorRejoinIfNeeded(this);
|
|
3072
|
-
}
|
|
3073
|
-
hasCoordinatorWriteAuthority() {
|
|
3074
|
-
return this.coordinatorAuthority;
|
|
3075
|
-
}
|
|
3076
|
-
requiresCoordinatorRejoinSync(state = this.coordinatorState) {
|
|
3077
|
-
return requiresCoordinatorRejoinSync(this, state);
|
|
3078
|
-
}
|
|
3079
|
-
markCoordinatorSyncReady() {
|
|
3080
|
-
return markCoordinatorSyncReady(this);
|
|
3081
|
-
}
|
|
3082
|
-
handleCoordinatorAckProgress(nodeId, ackedSeq) {
|
|
3083
|
-
return handleCoordinatorAckProgress(this, nodeId, ackedSeq);
|
|
3084
|
-
}
|
|
3190
|
+
/** @internal */
|
|
3085
3191
|
verifyPrimaryAuthority() {
|
|
3086
3192
|
return verifyPrimaryAuthority(this);
|
|
3087
3193
|
}
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
decorateBatch(batch) {
|
|
3092
|
-
return { ...batch, ...getCoordinatorMessageFields(this) };
|
|
3093
|
-
}
|
|
3094
|
-
decorateAck(ack) {
|
|
3095
|
-
return { ...ack, ...getCoordinatorMessageFields(this) };
|
|
3096
|
-
}
|
|
3097
|
-
decorateForwardResult(result) {
|
|
3098
|
-
return { ...result, ...getCoordinatorMessageFields(this) };
|
|
3099
|
-
}
|
|
3100
|
-
decorateSyncRequest(request) {
|
|
3101
|
-
return { ...request, ...getCoordinatorMessageFields(this) };
|
|
3102
|
-
}
|
|
3103
|
-
decorateSyncBatch(batch) {
|
|
3104
|
-
return { ...batch, ...getCoordinatorMessageFields(this) };
|
|
3105
|
-
}
|
|
3106
|
-
decorateSyncComplete(complete) {
|
|
3107
|
-
return { ...complete, ...getCoordinatorMessageFields(this) };
|
|
3108
|
-
}
|
|
3109
|
-
decorateSyncAck(ack) {
|
|
3110
|
-
return { ...ack, ...getCoordinatorMessageFields(this) };
|
|
3111
|
-
}
|
|
3112
|
-
resolveWriteConcern(wc) {
|
|
3113
|
-
if (wc) return wc;
|
|
3114
|
-
if (this.isCoordinatorMode()) {
|
|
3115
|
-
return { level: "majority" };
|
|
3116
|
-
}
|
|
3117
|
-
return void 0;
|
|
3194
|
+
/** @internal */
|
|
3195
|
+
markCoordinatorSyncReady() {
|
|
3196
|
+
return markCoordinatorSyncReady(this);
|
|
3118
3197
|
}
|
|
3198
|
+
/** @internal */
|
|
3119
3199
|
getCurrentPrimaryPeerId() {
|
|
3120
3200
|
return getForwardingPrimaryPeerId(this);
|
|
3121
3201
|
}
|
|
3202
|
+
/**
|
|
3203
|
+
* Stamps an outgoing replication message with this node's group and primary term.
|
|
3204
|
+
*
|
|
3205
|
+
* @internal
|
|
3206
|
+
*/
|
|
3207
|
+
decorate(message) {
|
|
3208
|
+
return { ...message, ...getCoordinatorMessageFields(this) };
|
|
3209
|
+
}
|
|
3122
3210
|
};
|
|
3123
3211
|
|
|
3124
3212
|
// src/replication/topology/primary-replica.ts
|
|
3125
3213
|
var PrimaryReplicaTopology = class {
|
|
3214
|
+
/**
|
|
3215
|
+
* Whether this node accepts writes or serves reads.
|
|
3216
|
+
*/
|
|
3126
3217
|
role;
|
|
3127
3218
|
constructor(role) {
|
|
3128
3219
|
this.role = role;
|
|
3129
3220
|
}
|
|
3221
|
+
/**
|
|
3222
|
+
* Reports whether this node accepts writes, which only the primary does.
|
|
3223
|
+
*
|
|
3224
|
+
* @returns True on the primary.
|
|
3225
|
+
*/
|
|
3130
3226
|
canWrite() {
|
|
3131
3227
|
return this.role === "primary";
|
|
3132
3228
|
}
|
|
3229
|
+
/**
|
|
3230
|
+
* Reports whether this node sends its changes to a given peer.
|
|
3231
|
+
*
|
|
3232
|
+
* @param _peerId - Identifier of the peer, which this topology ignores.
|
|
3233
|
+
* @param peerRole - Role of the peer.
|
|
3234
|
+
* @returns True when this node is the primary and the peer is a replica.
|
|
3235
|
+
*/
|
|
3133
3236
|
shouldReplicateTo(_peerId, peerRole) {
|
|
3134
3237
|
return this.role === "primary" && peerRole === "replica";
|
|
3135
3238
|
}
|
|
3239
|
+
/**
|
|
3240
|
+
* Reports whether this node applies changes arriving from a given peer.
|
|
3241
|
+
*
|
|
3242
|
+
* @param _peerId - Identifier of the peer, which this topology ignores.
|
|
3243
|
+
* @param peerRole - Role of the peer.
|
|
3244
|
+
* @returns True when this node is a replica and the peer is the primary.
|
|
3245
|
+
*/
|
|
3136
3246
|
shouldAcceptFrom(_peerId, peerRole) {
|
|
3137
3247
|
return this.role === "replica" && peerRole === "primary";
|
|
3138
3248
|
}
|
|
3249
|
+
/**
|
|
3250
|
+
* Reports whether incoming changes need a resolver, which a single writer removes the need for.
|
|
3251
|
+
*
|
|
3252
|
+
* @returns False.
|
|
3253
|
+
*/
|
|
3139
3254
|
requiresConflictResolution() {
|
|
3140
3255
|
return false;
|
|
3141
3256
|
}
|