@delali/sirannon-db 0.3.4-next.62 → 0.3.4
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 +79 -56
- package/dist/backup/index.d.ts +44 -45
- package/dist/backup/index.mjs +7 -7
- package/dist/backup-scheduler/index.d.ts +42 -41
- package/dist/backup-scheduler/index.mjs +4 -4
- package/dist/baseline-Co3iIsN_.d.ts +19 -0
- package/dist/change-tracker-DqCdeEyx.d.ts +128 -0
- package/dist/{chunk-VOSJBZ6Q.mjs → chunk-2ES3DT4A.mjs} +71 -33
- package/dist/{chunk-6CMCO7S4.mjs → chunk-2O3PP4CF.mjs} +1 -1
- package/dist/{chunk-E7Q7HE46.mjs → chunk-3BZLZ47R.mjs} +26 -26
- package/dist/chunk-4KASJKKK.mjs +2501 -0
- package/dist/{chunk-MYNI2WFS.mjs → chunk-52XW3Z76.mjs} +40 -8
- package/dist/chunk-55YRLSM2.mjs +57 -0
- package/dist/{chunk-KDAHYFW7.mjs → chunk-6AOJCNRU.mjs} +5 -5
- package/dist/{chunk-KXTYR2OQ.mjs → chunk-A3PHCO6W.mjs} +39 -111
- package/dist/chunk-ADNWEHGZ.mjs +299 -0
- package/dist/{chunk-2QLXDHAP.mjs → chunk-BLMRNSUG.mjs} +11 -2
- package/dist/chunk-BVMZJOVM.mjs +5 -0
- package/dist/{chunk-IWGIYDMZ.mjs → chunk-BZWDPF4G.mjs} +1 -1
- package/dist/{chunk-YVF2ZJTE.mjs → chunk-CZL4D3BM.mjs} +1 -1
- package/dist/{chunk-7SC2TZAS.mjs → chunk-DX4WGJL7.mjs} +2 -2
- package/dist/{chunk-45MKELYA.mjs → chunk-ESTZZPT5.mjs} +132 -13
- package/dist/{chunk-QP5L5DFJ.mjs → chunk-FJPR2CJX.mjs} +3 -3
- package/dist/chunk-HR7K3GZW.mjs +36 -0
- package/dist/{chunk-LXCGPZPA.mjs → chunk-IEKIYO4V.mjs} +1 -1
- package/dist/{chunk-TUD5CJ76.mjs → chunk-KB4OLJAD.mjs} +26 -19
- package/dist/{chunk-PBRXXISQ.mjs → chunk-KINDFBQP.mjs} +3 -2
- package/dist/{chunk-E6IMX7RS.mjs → chunk-KJLNYOOL.mjs} +2 -2
- package/dist/{chunk-BTTFW4Z4.mjs → chunk-KU32OT4H.mjs} +2 -10
- package/dist/{chunk-JQWGHSEH.mjs → chunk-NVSQ2T5Q.mjs} +71 -70
- package/dist/{chunk-W4EUTJNT.mjs → chunk-NZGSDALX.mjs} +94 -96
- package/dist/{chunk-U445A3C4.mjs → chunk-QGVRRKB2.mjs} +1 -1
- package/dist/{chunk-GYCEJNU4.mjs → chunk-R7GBXNTO.mjs} +1 -1
- package/dist/{chunk-7C36BCSN.mjs → chunk-VDN6FHU4.mjs} +1 -1
- package/dist/{chunk-6IYS4WLH.mjs → chunk-VO3ZWA2G.mjs} +142 -115
- package/dist/chunk-WOMADPM4.mjs +12 -0
- package/dist/{chunk-OUSWVNWT.mjs → chunk-YCMB6GR4.mjs} +1 -1
- package/dist/{chunk-YNTDOFTN.mjs → chunk-YW7IKQ7T.mjs} +1 -1
- package/dist/{chunk-M2BEAH5I.mjs → chunk-ZN4HI5FK.mjs} +126 -72
- package/dist/{chunk-SBL6GN43.mjs → chunk-ZYWI45EN.mjs} +1 -1
- package/dist/client/index.d.ts +135 -137
- package/dist/client/index.mjs +177 -82
- package/dist/client/topology.d.ts +31 -31
- package/dist/client/topology.mjs +42 -23
- package/dist/client-base-BxTocU1x.d.ts +398 -0
- package/dist/client-streams-YKANXMWO.mjs +112 -0
- package/dist/codegen/cli.mjs +4 -4
- package/dist/codegen/index.d.ts +36 -29
- package/dist/codegen/index.mjs +4 -4
- package/dist/core/index.d.ts +130 -115
- package/dist/core/index.mjs +387 -297
- package/dist/core/writer-worker.mjs +3 -3
- package/dist/database-CGDsXW1K.d.ts +959 -0
- package/dist/database-backup-DcLhxoa1.d.ts +143 -0
- package/dist/driver/better-sqlite3.d.ts +11 -10
- package/dist/driver/better-sqlite3.mjs +11 -11
- package/dist/driver/bun.d.ts +9 -8
- package/dist/driver/expo.d.ts +6 -7
- package/dist/driver/node.d.ts +11 -10
- package/dist/driver/node.mjs +13 -11
- package/dist/driver/wa-sqlite.d.ts +6 -6
- package/dist/errors-CJUkQ-KL.d.ts +199 -0
- package/dist/file-migrations/index.d.ts +20 -18
- package/dist/file-migrations/index.mjs +3 -3
- package/dist/forward-rpc-VRWFZXHN.mjs +44 -0
- package/dist/operation-registry-CGhNT-Sn.d.ts +96 -0
- package/dist/primary-wins-CHNOYuN7.d.ts +81 -0
- package/dist/protocol-DFpNNsIT.d.ts +153 -0
- package/dist/query-types-5B02ixH0.d.ts +129 -0
- package/dist/react/index.d.ts +22 -22
- package/dist/replication/coordinator/etcd.d.ts +37 -31
- package/dist/replication/coordinator/etcd.mjs +296 -131
- package/dist/replication/index.d.ts +201 -161
- package/dist/replication/index.mjs +630 -529
- package/dist/restore-options-CH8mE-Tb.d.ts +84 -0
- package/dist/server/index.d.ts +201 -191
- package/dist/server/index.mjs +736 -619
- package/dist/server-options-aYtCkefX.d.ts +317 -0
- package/dist/server-streams-24XL4T43.mjs +254 -0
- package/dist/sirannon-J3EgPVD7.d.ts +179 -0
- package/dist/transport/grpc.d.ts +37 -24
- package/dist/transport/grpc.mjs +83 -3199
- package/dist/transport/memory.d.ts +25 -26
- package/dist/transport/memory.mjs +18 -9
- package/dist/types-BrAiDGOb.d.ts +505 -0
- package/dist/types-CEJG0eFS.d.ts +88 -0
- package/dist/types-Dc8oWlxj.d.ts +1580 -0
- package/dist/types-DqODKeOq.d.ts +123 -0
- package/dist/types-guEKSzMr.d.ts +367 -0
- package/package.json +7 -7
- package/dist/baseline-D93hcIEE.d.ts +0 -17
- package/dist/change-tracker-CjRFuZdB.d.ts +0 -144
- package/dist/client-base-Cmd9EVme.d.ts +0 -397
- package/dist/database-BLQvqjsw.d.ts +0 -917
- package/dist/database-backup-B7oszQ2n.d.ts +0 -141
- package/dist/errors-Dei4GdBb.d.ts +0 -196
- package/dist/operation-registry-oiPuSYI6.d.ts +0 -94
- package/dist/primary-wins-B0np8JS3.d.ts +0 -85
- package/dist/protocol-HI-nY8DD.d.ts +0 -152
- package/dist/query-types-p1liOP-b.d.ts +0 -127
- package/dist/restore-options-CksYsH1l.d.ts +0 -83
- package/dist/server-options-Bj5iJnjx.d.ts +0 -292
- package/dist/sirannon-BPxC5tC2.d.ts +0 -178
- package/dist/types-B0EhgDbr.d.ts +0 -340
- package/dist/types-CbjfRwrK.d.ts +0 -499
- package/dist/types-CjhxcjhA.d.ts +0 -123
- package/dist/types-DPSmjU54.d.ts +0 -1518
- package/dist/types-DTO4CYhW.d.ts +0 -88
|
@@ -1,5 +1,25 @@
|
|
|
1
|
-
import { assertSqlAllowed, encodeWireRowsInPlace } from './chunk-
|
|
2
|
-
import { SirannonError, QueryError } from './chunk-
|
|
1
|
+
import { assertSqlAllowed, encodeWireRowsInPlace } from './chunk-2O3PP4CF.mjs';
|
|
2
|
+
import { SirannonError, TransactionError, QueryError } from './chunk-KINDFBQP.mjs';
|
|
3
|
+
|
|
4
|
+
// src/core/writer-transaction.ts
|
|
5
|
+
function commitFailure(err) {
|
|
6
|
+
if (err instanceof SirannonError) return err;
|
|
7
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
8
|
+
return new TransactionError(`SQLite could not commit the transaction: ${reason}`, err);
|
|
9
|
+
}
|
|
10
|
+
async function runWriterTransaction(conn, body) {
|
|
11
|
+
let bodyFinished = false;
|
|
12
|
+
try {
|
|
13
|
+
return await conn.transaction(async (txConn) => {
|
|
14
|
+
const value = await body(txConn);
|
|
15
|
+
bodyFinished = true;
|
|
16
|
+
return value;
|
|
17
|
+
});
|
|
18
|
+
} catch (err) {
|
|
19
|
+
if (!bodyFinished) throw err;
|
|
20
|
+
throw commitFailure(err);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
3
23
|
|
|
4
24
|
// src/core/query-executor.ts
|
|
5
25
|
var STATEMENT_CACHE_CAPACITY = 128;
|
|
@@ -97,8 +117,7 @@ async function executeGroup(conn, units) {
|
|
|
97
117
|
}
|
|
98
118
|
const failure = await execControl(conn, "COMMIT");
|
|
99
119
|
if (!failure) return outcomes;
|
|
100
|
-
|
|
101
|
-
return outcomes.map(() => ({ ok: false, error: failure }));
|
|
120
|
+
return settleRefusedCommit(conn, units, outcomes, failure);
|
|
102
121
|
}
|
|
103
122
|
async function runUnit(conn, unit) {
|
|
104
123
|
const values = new Array(unit.statements.length);
|
|
@@ -109,7 +128,7 @@ async function runUnit(conn, unit) {
|
|
|
109
128
|
return values;
|
|
110
129
|
}
|
|
111
130
|
function controlError(err, sql) {
|
|
112
|
-
return asQueryError(err, sql);
|
|
131
|
+
return sql === "COMMIT" ? commitFailure(err) : asQueryError(err, sql);
|
|
113
132
|
}
|
|
114
133
|
async function execControl(conn, sql) {
|
|
115
134
|
try {
|
|
@@ -135,8 +154,21 @@ async function executeGroupIsolated(conn, units) {
|
|
|
135
154
|
}
|
|
136
155
|
const failure = await execControl(conn, "COMMIT");
|
|
137
156
|
if (!failure) return outcomes;
|
|
138
|
-
|
|
139
|
-
|
|
157
|
+
return settleRefusedCommit(conn, units, outcomes, failure);
|
|
158
|
+
}
|
|
159
|
+
async function settleRefusedCommit(conn, units, outcomes, failure) {
|
|
160
|
+
const transactionStillOpen = await execControl(conn, "ROLLBACK") === null;
|
|
161
|
+
const rerunEachAlone = transactionStillOpen && succeededUnitCount(outcomes) > 1;
|
|
162
|
+
for (let i = 0; i < units.length; i++) {
|
|
163
|
+
if (!outcomes[i].ok) continue;
|
|
164
|
+
outcomes[i] = rerunEachAlone ? await runUnitAlone(conn, units[i]) : { ok: false, error: failure };
|
|
165
|
+
}
|
|
166
|
+
return outcomes;
|
|
167
|
+
}
|
|
168
|
+
function succeededUnitCount(outcomes) {
|
|
169
|
+
let count = 0;
|
|
170
|
+
for (const outcome of outcomes) if (outcome.ok) count++;
|
|
171
|
+
return count;
|
|
140
172
|
}
|
|
141
173
|
async function runIsolatedUnit(conn, unit, savepoint) {
|
|
142
174
|
const notOpened = await execControl(conn, `SAVEPOINT ${savepoint}`);
|
|
@@ -221,4 +253,4 @@ async function executeBatchSummary(conn, sql, paramsBatch) {
|
|
|
221
253
|
return { rowsLoaded: paramsBatch.length, changes };
|
|
222
254
|
}
|
|
223
255
|
|
|
224
|
-
export { bindParams, execute, executeBatch, executeBatchSummary, executeGroup, query, queryForWire, queryOne };
|
|
256
|
+
export { bindParams, execute, executeBatch, executeBatchSummary, executeGroup, query, queryForWire, queryOne, runWriterTransaction };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// src/transport/grpc/client-reconnect.ts
|
|
2
|
+
var RECONNECT_MIN_DELAY_MS = 250;
|
|
3
|
+
var RECONNECT_MAX_DELAY_MS = 5e3;
|
|
4
|
+
var stateByTransport = /* @__PURE__ */ new WeakMap();
|
|
5
|
+
function stateFor(transport) {
|
|
6
|
+
const existing = stateByTransport.get(transport);
|
|
7
|
+
if (existing) return existing;
|
|
8
|
+
const created = { timers: /* @__PURE__ */ new Map(), delays: /* @__PURE__ */ new Map(), dialled: /* @__PURE__ */ new Map() };
|
|
9
|
+
stateByTransport.set(transport, created);
|
|
10
|
+
return created;
|
|
11
|
+
}
|
|
12
|
+
function claimEndpoint(transport, endpoint) {
|
|
13
|
+
const state = stateFor(transport);
|
|
14
|
+
if (state.dialled.has(endpoint)) return false;
|
|
15
|
+
state.dialled.set(endpoint, () => void 0);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
function registerEndpointAbort(transport, endpoint, abort) {
|
|
19
|
+
const state = stateFor(transport);
|
|
20
|
+
if (!state.dialled.has(endpoint)) return;
|
|
21
|
+
state.dialled.set(endpoint, abort);
|
|
22
|
+
}
|
|
23
|
+
function releaseEndpoint(transport, endpoint) {
|
|
24
|
+
stateFor(transport).dialled.delete(endpoint);
|
|
25
|
+
}
|
|
26
|
+
function noteEndpointReachable(transport, endpoint) {
|
|
27
|
+
stateFor(transport).delays.delete(endpoint);
|
|
28
|
+
}
|
|
29
|
+
function scheduleEndpointRedial(transport, endpoint, redial) {
|
|
30
|
+
if (!transport.connected) return;
|
|
31
|
+
const state = stateFor(transport);
|
|
32
|
+
if (state.timers.has(endpoint)) return;
|
|
33
|
+
const delayMs = state.delays.get(endpoint) ?? RECONNECT_MIN_DELAY_MS;
|
|
34
|
+
state.delays.set(endpoint, Math.min(delayMs * 2, RECONNECT_MAX_DELAY_MS));
|
|
35
|
+
const timer = setTimeout(() => {
|
|
36
|
+
state.timers.delete(endpoint);
|
|
37
|
+
if (!transport.connected) return;
|
|
38
|
+
redial();
|
|
39
|
+
}, delayMs);
|
|
40
|
+
state.timers.set(endpoint, timer);
|
|
41
|
+
}
|
|
42
|
+
function stopEndpointDialling(transport) {
|
|
43
|
+
const state = stateByTransport.get(transport);
|
|
44
|
+
if (!state) return;
|
|
45
|
+
for (const timer of state.timers.values()) {
|
|
46
|
+
clearTimeout(timer);
|
|
47
|
+
}
|
|
48
|
+
state.timers.clear();
|
|
49
|
+
state.delays.clear();
|
|
50
|
+
const aborts = [...state.dialled.values()];
|
|
51
|
+
state.dialled.clear();
|
|
52
|
+
for (const abort of aborts) {
|
|
53
|
+
abort();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { claimEndpoint, noteEndpointReachable, registerEndpointAbort, releaseEndpoint, scheduleEndpointRedial, stopEndpointDialling };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { checkpointLog } from './chunk-
|
|
2
|
-
import { readLogFileHeader, sameLog, cursorChecksum, scanLogFrames, copyLogRange } from './chunk-
|
|
3
|
-
import { logFrameOffset } from './chunk-
|
|
4
|
-
import { isBackupChainPosition } from './chunk-
|
|
1
|
+
import { checkpointLog } from './chunk-QGVRRKB2.mjs';
|
|
2
|
+
import { readLogFileHeader, sameLog, cursorChecksum, scanLogFrames, copyLogRange } from './chunk-KJLNYOOL.mjs';
|
|
3
|
+
import { logFrameOffset } from './chunk-R7GBXNTO.mjs';
|
|
4
|
+
import { isBackupChainPosition } from './chunk-CZL4D3BM.mjs';
|
|
5
5
|
import { randomHex } from './chunk-QBBLSQHQ.mjs';
|
|
6
|
-
import { SirannonError } from './chunk-
|
|
6
|
+
import { SirannonError } from './chunk-KINDFBQP.mjs';
|
|
7
7
|
import { readFile, mkdir, writeFile, rename, rm, readdir } from 'fs/promises';
|
|
8
8
|
import { dirname, join } from 'path';
|
|
9
9
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { query, execute, executeBatch } from './chunk-
|
|
2
|
-
import { ensureMetaTable, selectMetaValue, IDENTIFIER_RE, selectAppliedSourceSeqsInRange, prepareInsertAppliedChange, validateDdlSafety, extractDroppedTable, selectMaxChangeSeq, updateChangeStampsAfterSeqSql, selectOutboundChangesSql, selectNodeChangesAfterSeqSql, prepareDeleteRowColumnVersionsUpToHlc, canonicaliseForChecksum, prepareUpsertNewerColumnVersion, upsertMetaValue, selectMaxColumnVersionHlcForRow, selectMaxRowChangeHlc, validateIdentifier, prepareDeleteRowColumnVersions, prepareUpsertColumnVersion } from './chunk-
|
|
3
|
-
import {
|
|
4
|
-
import { CHANGES_TABLE, isReservedIdentifier, decodeTaggedValues } from './chunk-
|
|
1
|
+
import { query, execute, executeBatch, runWriterTransaction } from './chunk-52XW3Z76.mjs';
|
|
2
|
+
import { HLC, ensureMetaTable, selectMetaValue, IDENTIFIER_RE, selectAppliedSourceSeqsInRange, prepareInsertAppliedChange, validateDdlSafety, extractDroppedTable, selectMaxChangeSeq, updateChangeStampsAfterSeqSql, selectOutboundChangesSql, selectNodeChangesAfterSeqSql, prepareDeleteRowColumnVersionsUpToHlc, canonicaliseForChecksum, prepareUpsertNewerColumnVersion, upsertMetaValue, selectMaxColumnVersionHlcForRow, selectMaxRowChangeHlc, validateIdentifier, prepareDeleteRowColumnVersions, prepareUpsertColumnVersion } from './chunk-ESTZZPT5.mjs';
|
|
3
|
+
import { BatchValidationError, ConflictError } from './chunk-BLMRNSUG.mjs';
|
|
4
|
+
import { CHANGES_TABLE, isReservedIdentifier, decodeTaggedValues } from './chunk-2O3PP4CF.mjs';
|
|
5
5
|
import { sha256Hex } from './chunk-GVCNMPOS.mjs';
|
|
6
6
|
|
|
7
7
|
// src/core/transaction.ts
|
|
@@ -11,21 +11,21 @@ var Transaction = class _Transaction {
|
|
|
11
11
|
}
|
|
12
12
|
_lastInsertRowId = 0;
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Executes a read inside this transaction.
|
|
15
15
|
*
|
|
16
|
-
* @param sql - The statement to
|
|
17
|
-
* @param params -
|
|
18
|
-
* @returns The rows the statement
|
|
16
|
+
* @param sql - The statement to execute.
|
|
17
|
+
* @param params - The values to bind to the statement, named or positional.
|
|
18
|
+
* @returns The rows that the statement returns.
|
|
19
19
|
*/
|
|
20
20
|
async query(sql, params) {
|
|
21
21
|
return query(this.conn, sql, params);
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* Executes one write inside this transaction.
|
|
25
25
|
*
|
|
26
|
-
* @param sql - The statement to
|
|
27
|
-
* @param params -
|
|
28
|
-
* @returns
|
|
26
|
+
* @param sql - The statement to execute.
|
|
27
|
+
* @param params - The values to bind to the statement, named or positional.
|
|
28
|
+
* @returns The number of rows that the statement changed, and the row id of the last inserted row.
|
|
29
29
|
*/
|
|
30
30
|
async execute(sql, params) {
|
|
31
31
|
const result = await execute(this.conn, sql, params);
|
|
@@ -33,10 +33,10 @@ var Transaction = class _Transaction {
|
|
|
33
33
|
return result;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* Executes one statement once for each parameter set, inside this transaction.
|
|
37
37
|
*
|
|
38
|
-
* @param sql - The statement to
|
|
39
|
-
* @param paramsBatch - One parameter set
|
|
38
|
+
* @param sql - The statement to execute for each parameter set.
|
|
39
|
+
* @param paramsBatch - One parameter set for each execution.
|
|
40
40
|
* @returns One result per parameter set, in order.
|
|
41
41
|
*/
|
|
42
42
|
async executeBatch(sql, paramsBatch) {
|
|
@@ -47,110 +47,24 @@ var Transaction = class _Transaction {
|
|
|
47
47
|
return results;
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
|
-
*
|
|
50
|
+
* The row id that SQLite assigned to the last row that this transaction inserted.
|
|
51
51
|
*/
|
|
52
52
|
get lastInsertRowId() {
|
|
53
53
|
return this._lastInsertRowId;
|
|
54
54
|
}
|
|
55
55
|
/** @internal */
|
|
56
56
|
static async run(conn, fn) {
|
|
57
|
-
return conn
|
|
57
|
+
return runWriterTransaction(conn, async (txConn) => {
|
|
58
58
|
const tx = new _Transaction(txConn);
|
|
59
59
|
return fn(tx);
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
-
// src/core/sync/hlc.ts
|
|
65
|
-
var MAX_LOGICAL = 65535;
|
|
66
|
-
var HLC = class _HLC {
|
|
67
|
-
wallMs;
|
|
68
|
-
logical;
|
|
69
|
-
nodeId;
|
|
70
|
-
constructor(nodeId) {
|
|
71
|
-
this.nodeId = nodeId;
|
|
72
|
-
this.wallMs = 0;
|
|
73
|
-
this.logical = 0;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Generate a new HLC timestamp, advancing the clock.
|
|
77
|
-
*
|
|
78
|
-
* @internal
|
|
79
|
-
*/
|
|
80
|
-
now() {
|
|
81
|
-
const physicalMs = Date.now();
|
|
82
|
-
if (physicalMs > this.wallMs) {
|
|
83
|
-
this.wallMs = physicalMs;
|
|
84
|
-
this.logical = 0;
|
|
85
|
-
} else {
|
|
86
|
-
this.logical += 1;
|
|
87
|
-
if (this.logical > MAX_LOGICAL) {
|
|
88
|
-
throw new ReplicationError("HLC logical counter overflow");
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return _HLC.encode(this.wallMs, this.logical, this.nodeId);
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Merge a remote HLC timestamp into the local clock and return the updated value.
|
|
95
|
-
*
|
|
96
|
-
* @internal
|
|
97
|
-
*/
|
|
98
|
-
receive(remote) {
|
|
99
|
-
const r = _HLC.decode(remote);
|
|
100
|
-
const physicalMs = Date.now();
|
|
101
|
-
if (physicalMs > this.wallMs && physicalMs > r.wallMs) {
|
|
102
|
-
this.wallMs = physicalMs;
|
|
103
|
-
this.logical = 0;
|
|
104
|
-
} else if (r.wallMs > this.wallMs) {
|
|
105
|
-
this.wallMs = r.wallMs;
|
|
106
|
-
this.logical = r.logical + 1;
|
|
107
|
-
} else if (this.wallMs > r.wallMs) {
|
|
108
|
-
this.logical += 1;
|
|
109
|
-
} else {
|
|
110
|
-
this.logical = Math.max(this.logical, r.logical) + 1;
|
|
111
|
-
}
|
|
112
|
-
if (this.logical > MAX_LOGICAL) {
|
|
113
|
-
throw new ReplicationError("HLC logical counter overflow");
|
|
114
|
-
}
|
|
115
|
-
return _HLC.encode(this.wallMs, this.logical, this.nodeId);
|
|
116
|
-
}
|
|
117
|
-
/** Lexicographic comparison of two encoded HLC strings. Returns -1, 0, or 1. */
|
|
118
|
-
static compare(a, b) {
|
|
119
|
-
if (a < b) return -1;
|
|
120
|
-
if (a > b) return 1;
|
|
121
|
-
return 0;
|
|
122
|
-
}
|
|
123
|
-
/** Parse an encoded HLC string into its wall-clock, logical, and nodeId components. */
|
|
124
|
-
static decode(hlc) {
|
|
125
|
-
const parts = hlc.split("-");
|
|
126
|
-
if (parts.length < 3) {
|
|
127
|
-
throw new ReplicationError(`Invalid HLC format: ${hlc}`);
|
|
128
|
-
}
|
|
129
|
-
return {
|
|
130
|
-
wallMs: Number.parseInt(parts[0], 16),
|
|
131
|
-
logical: Number.parseInt(parts[1], 16),
|
|
132
|
-
nodeId: parts.slice(2).join("-")
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Builds the string form of a clock reading, which sorts in the same order as the reading itself.
|
|
137
|
-
*
|
|
138
|
-
* @param wallMs - Wall-clock milliseconds since the Unix epoch.
|
|
139
|
-
* @param logical - Counter that orders events sharing one millisecond.
|
|
140
|
-
* @param nodeId - Identifier of the node taking the reading.
|
|
141
|
-
* @returns The encoded stamp.
|
|
142
|
-
*/
|
|
143
|
-
static encode(wallMs, logical, nodeId) {
|
|
144
|
-
const wallHex = wallMs.toString(16).padStart(12, "0");
|
|
145
|
-
const logicalHex = logical.toString(16).padStart(4, "0");
|
|
146
|
-
return `${wallHex}-${logicalHex}-${nodeId}`;
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
|
|
150
64
|
// src/core/sync/conflict/lww.ts
|
|
151
65
|
var LWWResolver = class {
|
|
152
66
|
/**
|
|
153
|
-
*
|
|
67
|
+
* Returns the incoming row when its stamp is higher, and the local row otherwise.
|
|
154
68
|
*
|
|
155
69
|
* @param ctx - The local and incoming versions of one row.
|
|
156
70
|
* @returns Which version to write.
|
|
@@ -344,6 +258,21 @@ var RowWriter = class {
|
|
|
344
258
|
};
|
|
345
259
|
|
|
346
260
|
// src/core/sync/batch-applier.ts
|
|
261
|
+
async function resolveConflict(resolver, ctx) {
|
|
262
|
+
try {
|
|
263
|
+
const changeResolver = typeof resolver === "function" ? resolver(ctx.table) : resolver;
|
|
264
|
+
return await changeResolver.resolve(ctx);
|
|
265
|
+
} catch (err) {
|
|
266
|
+
if (err instanceof ConflictError) throw err;
|
|
267
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
268
|
+
throw new ConflictError(
|
|
269
|
+
`Conflict resolution failed for table '${ctx.table}' row '${ctx.rowId}': ${reason}`,
|
|
270
|
+
ctx.table,
|
|
271
|
+
ctx.rowId,
|
|
272
|
+
err
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
347
276
|
var BatchApplier = class {
|
|
348
277
|
constructor(conn, localNodeId, hlc, pkResolver, getLastAppliedSeq, tracker, changesTable = CHANGES_TABLE, beforeApply) {
|
|
349
278
|
this.conn = conn;
|
|
@@ -455,10 +384,10 @@ var BatchApplier = class {
|
|
|
455
384
|
return { ...result, droppedTables };
|
|
456
385
|
}
|
|
457
386
|
/**
|
|
458
|
-
* Applies one
|
|
459
|
-
*
|
|
460
|
-
* The
|
|
461
|
-
*
|
|
387
|
+
* Applies one staged transaction, reading its changes from `source` one batch
|
|
388
|
+
* at a time so that memory use stays bounded for a transaction of any size.
|
|
389
|
+
* The method writes every batch, the echo stamps, and the `withinTx` work
|
|
390
|
+
* inside one local transaction, so a failure at any step rolls all of it back.
|
|
462
391
|
*/
|
|
463
392
|
async applyStagedGroup(options) {
|
|
464
393
|
const { source, resolver, withinTx } = options;
|
|
@@ -539,8 +468,7 @@ var BatchApplier = class {
|
|
|
539
468
|
newData: existingRow,
|
|
540
469
|
oldData: null
|
|
541
470
|
};
|
|
542
|
-
const
|
|
543
|
-
const resolution = await changeResolver.resolve({
|
|
471
|
+
const resolution = await resolveConflict(resolver, {
|
|
544
472
|
table: change.table,
|
|
545
473
|
rowId: change.rowId,
|
|
546
474
|
localChange,
|
|
@@ -714,4 +642,4 @@ var StampOps = class {
|
|
|
714
642
|
}
|
|
715
643
|
};
|
|
716
644
|
|
|
717
|
-
export { BatchApplier, BatchReader,
|
|
645
|
+
export { BatchApplier, BatchReader, HLC_CLOCK_META_KEY, LWWResolver, REMOTE_ORIGIN_NODE_ID, StampOps, Transaction, isWellFormedHlc, loadPersistedHlc, recordLocalColumnVersions };
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { TransportError } from './chunk-KU32OT4H.mjs';
|
|
2
|
+
|
|
3
|
+
// src/transport/grpc/codec.ts
|
|
4
|
+
var PROTO_UINT32_MAX = 4294967295;
|
|
5
|
+
function toColumnValue(value) {
|
|
6
|
+
if (value === null || value === void 0) {
|
|
7
|
+
return { nullValue: true };
|
|
8
|
+
}
|
|
9
|
+
if (typeof value === "string") {
|
|
10
|
+
return { stringValue: value };
|
|
11
|
+
}
|
|
12
|
+
if (typeof value === "bigint") {
|
|
13
|
+
return { intValue: value };
|
|
14
|
+
}
|
|
15
|
+
if (typeof value === "number") {
|
|
16
|
+
if (Number.isInteger(value)) {
|
|
17
|
+
return { intValue: BigInt(value) };
|
|
18
|
+
}
|
|
19
|
+
return { floatValue: value };
|
|
20
|
+
}
|
|
21
|
+
if (typeof value === "boolean") {
|
|
22
|
+
return { boolValue: value };
|
|
23
|
+
}
|
|
24
|
+
if (value instanceof Uint8Array || Buffer.isBuffer(value)) {
|
|
25
|
+
return { blobValue: Buffer.from(value) };
|
|
26
|
+
}
|
|
27
|
+
throw new TypeError(
|
|
28
|
+
`Unsupported value type for ColumnValue conversion: ${typeof value} (${Object.prototype.toString.call(value)})`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
function fromColumnValue(cv) {
|
|
32
|
+
if (cv.nullValue !== void 0) return null;
|
|
33
|
+
if (cv.stringValue !== void 0) return cv.stringValue;
|
|
34
|
+
if (cv.intValue !== void 0) return cv.intValue;
|
|
35
|
+
if (cv.floatValue !== void 0) return cv.floatValue;
|
|
36
|
+
if (cv.blobValue !== void 0) return cv.blobValue;
|
|
37
|
+
if (cv.boolValue !== void 0) return cv.boolValue;
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
function toRowData(record) {
|
|
41
|
+
if (record === null || record === void 0) return void 0;
|
|
42
|
+
const fields = {};
|
|
43
|
+
for (const [key, value] of Object.entries(record)) {
|
|
44
|
+
fields[key] = toColumnValue(value);
|
|
45
|
+
}
|
|
46
|
+
return { fields };
|
|
47
|
+
}
|
|
48
|
+
function fromRowData(rowData) {
|
|
49
|
+
if (rowData === void 0) return null;
|
|
50
|
+
const result = {};
|
|
51
|
+
for (const [key, cv] of Object.entries(rowData.fields)) {
|
|
52
|
+
result[key] = fromColumnValue(cv);
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
function toProtoChange(change) {
|
|
57
|
+
return {
|
|
58
|
+
table: change.table,
|
|
59
|
+
operation: change.operation,
|
|
60
|
+
rowId: change.rowId,
|
|
61
|
+
primaryKey: toRowData(change.primaryKey),
|
|
62
|
+
hlc: change.hlc,
|
|
63
|
+
txId: change.txId,
|
|
64
|
+
nodeId: change.nodeId,
|
|
65
|
+
newData: toRowData(change.newData),
|
|
66
|
+
oldData: toRowData(change.oldData),
|
|
67
|
+
ddlStatement: change.ddlStatement ?? ""
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function fromProtoChange(proto) {
|
|
71
|
+
return {
|
|
72
|
+
table: proto.table,
|
|
73
|
+
operation: proto.operation,
|
|
74
|
+
rowId: proto.rowId,
|
|
75
|
+
primaryKey: fromRowData(proto.primaryKey) ?? {},
|
|
76
|
+
hlc: proto.hlc,
|
|
77
|
+
txId: proto.txId,
|
|
78
|
+
nodeId: proto.nodeId,
|
|
79
|
+
newData: fromRowData(proto.newData),
|
|
80
|
+
oldData: fromRowData(proto.oldData),
|
|
81
|
+
ddlStatement: proto.ddlStatement || void 0
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function toBatchPayload(batch) {
|
|
85
|
+
return {
|
|
86
|
+
sourceNodeId: batch.sourceNodeId,
|
|
87
|
+
batchId: batch.batchId,
|
|
88
|
+
fromSeq: batch.fromSeq,
|
|
89
|
+
toSeq: batch.toSeq,
|
|
90
|
+
hlcRange: { min: batch.hlcRange.min, max: batch.hlcRange.max },
|
|
91
|
+
changes: batch.changes.map(toProtoChange),
|
|
92
|
+
checksum: batch.checksum,
|
|
93
|
+
groupId: batch.groupId ?? "",
|
|
94
|
+
primaryTerm: batch.primaryTerm ?? 0n
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function fromBatchPayload(payload) {
|
|
98
|
+
return {
|
|
99
|
+
sourceNodeId: payload.sourceNodeId,
|
|
100
|
+
batchId: payload.batchId,
|
|
101
|
+
fromSeq: payload.fromSeq,
|
|
102
|
+
toSeq: payload.toSeq,
|
|
103
|
+
hlcRange: {
|
|
104
|
+
min: payload.hlcRange?.min ?? "",
|
|
105
|
+
max: payload.hlcRange?.max ?? ""
|
|
106
|
+
},
|
|
107
|
+
changes: payload.changes.map(fromProtoChange),
|
|
108
|
+
checksum: payload.checksum,
|
|
109
|
+
groupId: payload.groupId || void 0,
|
|
110
|
+
primaryTerm: payload.primaryTerm === 0n ? void 0 : payload.primaryTerm
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function toAckPayload(ack) {
|
|
114
|
+
return {
|
|
115
|
+
batchId: ack.batchId,
|
|
116
|
+
ackedSeq: ack.ackedSeq,
|
|
117
|
+
nodeId: ack.nodeId,
|
|
118
|
+
groupId: ack.groupId ?? "",
|
|
119
|
+
primaryTerm: ack.primaryTerm ?? 0n
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function fromAckPayload(payload) {
|
|
123
|
+
return {
|
|
124
|
+
batchId: payload.batchId,
|
|
125
|
+
ackedSeq: payload.ackedSeq,
|
|
126
|
+
nodeId: payload.nodeId,
|
|
127
|
+
groupId: payload.groupId || void 0,
|
|
128
|
+
primaryTerm: payload.primaryTerm === 0n ? void 0 : payload.primaryTerm
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function toSyncRequestPayload(req) {
|
|
132
|
+
return {
|
|
133
|
+
requestId: req.requestId,
|
|
134
|
+
joinerNodeId: req.joinerNodeId,
|
|
135
|
+
completedTables: req.completedTables,
|
|
136
|
+
groupId: req.groupId ?? "",
|
|
137
|
+
primaryTerm: req.primaryTerm ?? 0n,
|
|
138
|
+
supportsStreamVerification: req.supportsStreamVerification === true
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function fromSyncRequestPayload(p) {
|
|
142
|
+
return {
|
|
143
|
+
requestId: p.requestId,
|
|
144
|
+
joinerNodeId: p.joinerNodeId,
|
|
145
|
+
completedTables: p.completedTables,
|
|
146
|
+
supportsStreamVerification: p.supportsStreamVerification === true ? true : void 0,
|
|
147
|
+
groupId: p.groupId || void 0,
|
|
148
|
+
primaryTerm: p.primaryTerm === 0n ? void 0 : p.primaryTerm
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function toSyncBatchPayload(batch) {
|
|
152
|
+
return {
|
|
153
|
+
requestId: batch.requestId,
|
|
154
|
+
table: batch.table,
|
|
155
|
+
batchIndex: batch.batchIndex,
|
|
156
|
+
rows: batch.rows.map((row) => toRowData(row) ?? { fields: {} }),
|
|
157
|
+
schema: batch.schema ?? [],
|
|
158
|
+
checksum: batch.checksum,
|
|
159
|
+
isLastBatchForTable: batch.isLastBatchForTable,
|
|
160
|
+
groupId: batch.groupId ?? "",
|
|
161
|
+
primaryTerm: batch.primaryTerm ?? 0n,
|
|
162
|
+
totalTables: batch.totalTables ?? 0
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function fromSyncBatchPayload(p) {
|
|
166
|
+
return {
|
|
167
|
+
requestId: p.requestId,
|
|
168
|
+
table: p.table,
|
|
169
|
+
batchIndex: p.batchIndex,
|
|
170
|
+
rows: p.rows.map((r) => fromRowData(r) ?? {}),
|
|
171
|
+
schema: p.schema.length > 0 ? p.schema : void 0,
|
|
172
|
+
checksum: p.checksum,
|
|
173
|
+
isLastBatchForTable: p.isLastBatchForTable,
|
|
174
|
+
groupId: p.groupId || void 0,
|
|
175
|
+
primaryTerm: p.primaryTerm === 0n ? void 0 : p.primaryTerm,
|
|
176
|
+
totalTables: p.totalTables === 0 ? void 0 : p.totalTables
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function toSyncCompletePayload(complete) {
|
|
180
|
+
return {
|
|
181
|
+
requestId: complete.requestId,
|
|
182
|
+
snapshotSeq: complete.snapshotSeq,
|
|
183
|
+
manifests: complete.manifests.map((m) => ({
|
|
184
|
+
table: m.table,
|
|
185
|
+
rowCount: m.rowCount,
|
|
186
|
+
pkHash: m.pkHash ?? "",
|
|
187
|
+
batchDigest: m.batchDigest ?? ""
|
|
188
|
+
})),
|
|
189
|
+
groupId: complete.groupId ?? "",
|
|
190
|
+
primaryTerm: complete.primaryTerm ?? 0n
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function fromSyncCompletePayload(p) {
|
|
194
|
+
return {
|
|
195
|
+
requestId: p.requestId,
|
|
196
|
+
snapshotSeq: p.snapshotSeq,
|
|
197
|
+
manifests: p.manifests.map(
|
|
198
|
+
(m) => ({
|
|
199
|
+
table: m.table,
|
|
200
|
+
rowCount: m.rowCount,
|
|
201
|
+
pkHash: m.pkHash || void 0,
|
|
202
|
+
batchDigest: m.batchDigest || void 0
|
|
203
|
+
})
|
|
204
|
+
),
|
|
205
|
+
groupId: p.groupId || void 0,
|
|
206
|
+
primaryTerm: p.primaryTerm === 0n ? void 0 : p.primaryTerm
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function toSyncAckPayload(ack) {
|
|
210
|
+
return {
|
|
211
|
+
requestId: ack.requestId,
|
|
212
|
+
joinerNodeId: ack.joinerNodeId,
|
|
213
|
+
table: ack.table,
|
|
214
|
+
batchIndex: ack.batchIndex,
|
|
215
|
+
success: ack.success,
|
|
216
|
+
error: ack.error ?? "",
|
|
217
|
+
groupId: ack.groupId ?? "",
|
|
218
|
+
primaryTerm: ack.primaryTerm ?? 0n
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function fromSyncAckPayload(p) {
|
|
222
|
+
return {
|
|
223
|
+
requestId: p.requestId,
|
|
224
|
+
joinerNodeId: p.joinerNodeId,
|
|
225
|
+
table: p.table,
|
|
226
|
+
batchIndex: p.batchIndex,
|
|
227
|
+
success: p.success,
|
|
228
|
+
error: p.error || void 0,
|
|
229
|
+
groupId: p.groupId || void 0,
|
|
230
|
+
primaryTerm: p.primaryTerm === 0n ? void 0 : p.primaryTerm
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function toForwardRequest(req) {
|
|
234
|
+
const statements = req.statements.map((s) => {
|
|
235
|
+
const namedParams = {};
|
|
236
|
+
const positionalParams = [];
|
|
237
|
+
if (s.params !== void 0 && s.params !== null) {
|
|
238
|
+
if (Array.isArray(s.params)) {
|
|
239
|
+
for (const v of s.params) {
|
|
240
|
+
positionalParams.push(toColumnValue(v));
|
|
241
|
+
}
|
|
242
|
+
} else {
|
|
243
|
+
for (const [k, v] of Object.entries(s.params)) {
|
|
244
|
+
namedParams[k] = toColumnValue(v);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return { sql: s.sql, namedParams, positionalParams };
|
|
249
|
+
});
|
|
250
|
+
const writeConcernTimeoutMs = req.writeConcern?.timeoutMs ?? 0;
|
|
251
|
+
if (writeConcernTimeoutMs > PROTO_UINT32_MAX) {
|
|
252
|
+
throw new TransportError(
|
|
253
|
+
`Write concern timeoutMs ${writeConcernTimeoutMs} exceeds the ${PROTO_UINT32_MAX} ms that a forwarded write can carry`
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
requestId: req.requestId,
|
|
258
|
+
statements,
|
|
259
|
+
groupId: req.groupId ?? "",
|
|
260
|
+
primaryTerm: req.primaryTerm ?? 0n,
|
|
261
|
+
writeConcernLevel: req.writeConcern?.level ?? "",
|
|
262
|
+
writeConcernTimeoutMs
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function fromForwardWriteConcern(proto) {
|
|
266
|
+
const level = proto.writeConcernLevel;
|
|
267
|
+
if (level === "") return void 0;
|
|
268
|
+
if (level !== "local" && level !== "majority" && level !== "all") {
|
|
269
|
+
throw new TransportError(`Forward request states an unknown write concern level '${level}'`);
|
|
270
|
+
}
|
|
271
|
+
return proto.writeConcernTimeoutMs > 0 ? { level, timeoutMs: proto.writeConcernTimeoutMs } : { level };
|
|
272
|
+
}
|
|
273
|
+
function fromForwardRequest(proto) {
|
|
274
|
+
const statements = proto.statements.map((s) => {
|
|
275
|
+
const hasNamed = Object.keys(s.namedParams).length > 0;
|
|
276
|
+
const hasPositional = s.positionalParams.length > 0;
|
|
277
|
+
let params;
|
|
278
|
+
if (hasNamed) {
|
|
279
|
+
const named = {};
|
|
280
|
+
for (const [k, cv] of Object.entries(s.namedParams)) {
|
|
281
|
+
named[k] = fromColumnValue(cv);
|
|
282
|
+
}
|
|
283
|
+
params = named;
|
|
284
|
+
} else if (hasPositional) {
|
|
285
|
+
params = s.positionalParams.map(fromColumnValue);
|
|
286
|
+
}
|
|
287
|
+
return { sql: s.sql, params };
|
|
288
|
+
});
|
|
289
|
+
const writeConcern = fromForwardWriteConcern(proto);
|
|
290
|
+
return {
|
|
291
|
+
requestId: proto.requestId,
|
|
292
|
+
statements,
|
|
293
|
+
groupId: proto.groupId || void 0,
|
|
294
|
+
primaryTerm: proto.primaryTerm === 0n ? void 0 : proto.primaryTerm,
|
|
295
|
+
...writeConcern === void 0 ? {} : { writeConcern }
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export { fromAckPayload, fromBatchPayload, fromColumnValue, fromForwardRequest, fromSyncAckPayload, fromSyncBatchPayload, fromSyncCompletePayload, fromSyncRequestPayload, toAckPayload, toBatchPayload, toColumnValue, toForwardRequest, toSyncAckPayload, toSyncBatchPayload, toSyncCompletePayload, toSyncRequestPayload };
|