@g9tm/syncdata 1.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/LICENSE.md +21 -0
- package/PARITY.md +71 -0
- package/README.md +71 -0
- package/THIRD-PARTY-NOTICES.md +31 -0
- package/dist/abstractions/digest.d.ts +27 -0
- package/dist/abstractions/digest.js +78 -0
- package/dist/abstractions/digest.js.map +1 -0
- package/dist/abstractions/enums.d.ts +42 -0
- package/dist/abstractions/enums.js +60 -0
- package/dist/abstractions/enums.js.map +1 -0
- package/dist/abstractions/errors.d.ts +72 -0
- package/dist/abstractions/errors.js +90 -0
- package/dist/abstractions/errors.js.map +1 -0
- package/dist/abstractions/guid.d.ts +24 -0
- package/dist/abstractions/guid.js +93 -0
- package/dist/abstractions/guid.js.map +1 -0
- package/dist/abstractions/key.d.ts +16 -0
- package/dist/abstractions/key.js +47 -0
- package/dist/abstractions/key.js.map +1 -0
- package/dist/abstractions/long.d.ts +103 -0
- package/dist/abstractions/long.js +351 -0
- package/dist/abstractions/long.js.map +1 -0
- package/dist/abstractions/parameters.d.ts +49 -0
- package/dist/abstractions/parameters.js +153 -0
- package/dist/abstractions/parameters.js.map +1 -0
- package/dist/brotli-js/decode.d.ts +8 -0
- package/dist/brotli-js/decode.js +2004 -0
- package/dist/brotli-js/decode.js.map +1 -0
- package/dist/brotli-js/index.d.ts +5 -0
- package/dist/brotli-js/index.js +19 -0
- package/dist/brotli-js/index.js.map +1 -0
- package/dist/client/engine.d.ts +110 -0
- package/dist/client/engine.js +1158 -0
- package/dist/client/engine.js.map +1 -0
- package/dist/client/model.d.ts +61 -0
- package/dist/client/model.js +154 -0
- package/dist/client/model.js.map +1 -0
- package/dist/client/progress.d.ts +101 -0
- package/dist/client/progress.js +251 -0
- package/dist/client/progress.js.map +1 -0
- package/dist/client/runs.d.ts +219 -0
- package/dist/client/runs.js +239 -0
- package/dist/client/runs.js.map +1 -0
- package/dist/client/storeTypes.d.ts +428 -0
- package/dist/client/storeTypes.js +256 -0
- package/dist/client/storeTypes.js.map +1 -0
- package/dist/client/transport.d.ts +19 -0
- package/dist/client/transport.js +2 -0
- package/dist/client/transport.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/abort.d.ts +21 -0
- package/dist/internal/abort.js +125 -0
- package/dist/internal/abort.js.map +1 -0
- package/dist/internal/base64.d.ts +8 -0
- package/dist/internal/base64.js +85 -0
- package/dist/internal/base64.js.map +1 -0
- package/dist/internal/dotnetFormat.d.ts +32 -0
- package/dist/internal/dotnetFormat.js +207 -0
- package/dist/internal/dotnetFormat.js.map +1 -0
- package/dist/internal/md5.d.ts +2 -0
- package/dist/internal/md5.js +97 -0
- package/dist/internal/md5.js.map +1 -0
- package/dist/internal/sha256.d.ts +20 -0
- package/dist/internal/sha256.js +153 -0
- package/dist/internal/sha256.js.map +1 -0
- package/dist/internal/utf8.d.ts +8 -0
- package/dist/internal/utf8.js +143 -0
- package/dist/internal/utf8.js.map +1 -0
- package/dist/node/index.d.ts +9 -0
- package/dist/node/index.js +35 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/sqliteDriver.d.ts +11 -0
- package/dist/node/sqliteDriver.js +252 -0
- package/dist/node/sqliteDriver.js.map +1 -0
- package/dist/protocol/dtos.d.ts +280 -0
- package/dist/protocol/dtos.js +33 -0
- package/dist/protocol/dtos.js.map +1 -0
- package/dist/protocol/protocol.d.ts +36 -0
- package/dist/protocol/protocol.js +63 -0
- package/dist/protocol/protocol.js.map +1 -0
- package/dist/protocol/rowBatch/columnLayout.d.ts +4 -0
- package/dist/protocol/rowBatch/columnLayout.js +309 -0
- package/dist/protocol/rowBatch/columnLayout.js.map +1 -0
- package/dist/protocol/rowBatch/format.d.ts +74 -0
- package/dist/protocol/rowBatch/format.js +163 -0
- package/dist/protocol/rowBatch/format.js.map +1 -0
- package/dist/protocol/rowBatch/reader.d.ts +119 -0
- package/dist/protocol/rowBatch/reader.js +459 -0
- package/dist/protocol/rowBatch/reader.js.map +1 -0
- package/dist/protocol/rowBatch/writer.d.ts +101 -0
- package/dist/protocol/rowBatch/writer.js +412 -0
- package/dist/protocol/rowBatch/writer.js.map +1 -0
- package/dist/protocol/wire.d.ts +32 -0
- package/dist/protocol/wire.js +329 -0
- package/dist/protocol/wire.js.map +1 -0
- package/dist/signalr/index.d.ts +2 -0
- package/dist/signalr/index.js +5 -0
- package/dist/signalr/index.js.map +1 -0
- package/dist/signalr/protocols.d.ts +17 -0
- package/dist/signalr/protocols.js +109 -0
- package/dist/signalr/protocols.js.map +1 -0
- package/dist/signalr/transport.d.ts +67 -0
- package/dist/signalr/transport.js +286 -0
- package/dist/signalr/transport.js.map +1 -0
- package/dist/sqlite/apply.d.ts +91 -0
- package/dist/sqlite/apply.js +657 -0
- package/dist/sqlite/apply.js.map +1 -0
- package/dist/sqlite/commands.d.ts +24 -0
- package/dist/sqlite/commands.js +167 -0
- package/dist/sqlite/commands.js.map +1 -0
- package/dist/sqlite/connection.d.ts +81 -0
- package/dist/sqlite/connection.js +322 -0
- package/dist/sqlite/connection.js.map +1 -0
- package/dist/sqlite/driver.d.ts +63 -0
- package/dist/sqlite/driver.js +9 -0
- package/dist/sqlite/driver.js.map +1 -0
- package/dist/sqlite/index.d.ts +6 -0
- package/dist/sqlite/index.js +7 -0
- package/dist/sqlite/index.js.map +1 -0
- package/dist/sqlite/internals.d.ts +63 -0
- package/dist/sqlite/internals.js +194 -0
- package/dist/sqlite/internals.js.map +1 -0
- package/dist/sqlite/options.d.ts +78 -0
- package/dist/sqlite/options.js +78 -0
- package/dist/sqlite/options.js.map +1 -0
- package/dist/sqlite/push.d.ts +9 -0
- package/dist/sqlite/push.js +272 -0
- package/dist/sqlite/push.js.map +1 -0
- package/dist/sqlite/query.d.ts +38 -0
- package/dist/sqlite/query.js +476 -0
- package/dist/sqlite/query.js.map +1 -0
- package/dist/sqlite/revocation.d.ts +28 -0
- package/dist/sqlite/revocation.js +337 -0
- package/dist/sqlite/revocation.js.map +1 -0
- package/dist/sqlite/sql.d.ts +60 -0
- package/dist/sqlite/sql.js +324 -0
- package/dist/sqlite/sql.js.map +1 -0
- package/dist/sqlite/store.d.ts +116 -0
- package/dist/sqlite/store.js +664 -0
- package/dist/sqlite/store.js.map +1 -0
- package/dist/sqlite/values.d.ts +29 -0
- package/dist/sqlite/values.js +288 -0
- package/dist/sqlite/values.js.map +1 -0
- package/dist/testing/index.d.ts +61 -0
- package/dist/testing/index.js +175 -0
- package/dist/testing/index.js.map +1 -0
- package/package.json +104 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
// Twin of G9SyncData.Client.Sqlite/G9CSqliteSyncStore.Push.cs: dirty rows in local change order become push frames; the
|
|
2
|
+
// result clears exactly the rows that were sent (rows edited meanwhile stay dirty), flags rejections and applies canonical
|
|
3
|
+
// server rows.
|
|
4
|
+
import { G9ESyncDirection } from '../abstractions/enums.js';
|
|
5
|
+
import { G9SyncErrorCodes } from '../abstractions/errors.js';
|
|
6
|
+
import { G9Long } from '../abstractions/long.js';
|
|
7
|
+
import { utf8Encode } from '../internal/utf8.js';
|
|
8
|
+
import { G9ApplyReport } from '../client/storeTypes.js';
|
|
9
|
+
import { G9EPushRowOp, G9ERowBatchOp } from '../protocol/rowBatch/format.js';
|
|
10
|
+
import { G9RowBatchWriter } from '../protocol/rowBatch/writer.js';
|
|
11
|
+
import { applyFrame, beginApply, endApply } from './apply.js';
|
|
12
|
+
import { completeCommands, readPendingCommands } from './commands.js';
|
|
13
|
+
import { asLong, asNumber, asString } from './connection.js';
|
|
14
|
+
import { findByServerName, getLocal, keyValue, meta, nowMs, setMeta } from './internals.js';
|
|
15
|
+
import { quote } from './sql.js';
|
|
16
|
+
import { estimateEncodedBytes, pushSelectExpression, writeLocalValue } from './values.js';
|
|
17
|
+
const ROW_OVERHEAD = 48;
|
|
18
|
+
/** `BuildPushBatchAsync`: the next batch of pending changes, oldest first, within the row and byte budgets. */
|
|
19
|
+
export async function buildPushBatch(store, tables, maxRows, maxBytes) {
|
|
20
|
+
store.ensureInitialized();
|
|
21
|
+
const uploadable = new Map();
|
|
22
|
+
for (const negotiated of tables) {
|
|
23
|
+
if (negotiated.direction === G9ESyncDirection.DownloadOnly)
|
|
24
|
+
continue;
|
|
25
|
+
const local = findByServerName(store, negotiated.name);
|
|
26
|
+
if (!local)
|
|
27
|
+
continue;
|
|
28
|
+
const readOnly = new Set();
|
|
29
|
+
for (const c of negotiated.readOnlyColumns)
|
|
30
|
+
readOnly.add(c.toLowerCase());
|
|
31
|
+
for (const c of negotiated.computedColumns)
|
|
32
|
+
readOnly.add(c.toLowerCase());
|
|
33
|
+
const columns = negotiated.columns
|
|
34
|
+
.filter((c) => !readOnly.has(c.name.toLowerCase()) && local.model.indexOf(c.name) >= 0)
|
|
35
|
+
.map((c) => ({ name: c.name, type: c.type, local: local.model.indexOf(c.name) }));
|
|
36
|
+
uploadable.set(local.tid, { table: local, negotiated, columns });
|
|
37
|
+
}
|
|
38
|
+
return store.writeLock.run(() => store.writer().transaction('immediate', async () => {
|
|
39
|
+
var _a;
|
|
40
|
+
const connection = store.writer();
|
|
41
|
+
const inflightBatch = await meta(connection, 'inflight_batch_id');
|
|
42
|
+
const inflightMax = await meta(connection, 'inflight_max_seq');
|
|
43
|
+
const offsetText = await meta(connection, 'server_offset_ms');
|
|
44
|
+
const offsetMs = offsetText !== null && /^[+-]?\d+$/.test(offsetText) ? Number(offsetText) : 0;
|
|
45
|
+
const tids = Array.from(uploadable.keys());
|
|
46
|
+
const sql = 'SELECT tid, pk, dirty, dirty_mask, server_ver, local_seq, coalesce(hlc_ms, 0) FROM g9_rows WHERE dirty <> 0 AND rejected = 0 AND tid IN (' +
|
|
47
|
+
tids.join(',') +
|
|
48
|
+
')' +
|
|
49
|
+
(inflightMax === null ? '' : ' AND local_seq <= $imax') +
|
|
50
|
+
' ORDER BY local_seq LIMIT $max';
|
|
51
|
+
const commands = await readPendingCommands(store, connection, inflightBatch, Math.max(1, maxRows));
|
|
52
|
+
const pending = uploadable.size === 0
|
|
53
|
+
? []
|
|
54
|
+
: await connection.query(sql, inflightMax === null ? { $max: Math.max(1, maxRows) } : { $max: Math.max(1, maxRows), $imax: Number(inflightMax) });
|
|
55
|
+
const rows = pending.map((r) => ({
|
|
56
|
+
tid: asNumber(r[0]),
|
|
57
|
+
key: asString(r[1]),
|
|
58
|
+
dirty: asNumber(r[2]),
|
|
59
|
+
mask: asLong(r[3]),
|
|
60
|
+
base: asNumber(r[4]),
|
|
61
|
+
seq: asNumber(r[5]),
|
|
62
|
+
hlc: asNumber(r[6]),
|
|
63
|
+
}));
|
|
64
|
+
if (rows.length === 0 && commands.length === 0) {
|
|
65
|
+
if (inflightBatch !== null) {
|
|
66
|
+
// Everything the in-flight batch carried has been resolved another way; forget it.
|
|
67
|
+
await connection.exec("DELETE FROM g9_meta WHERE key IN ('inflight_batch_id', 'inflight_max_seq')");
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
// Rows are encoded in local_seq order and the batch ends at the first row past the byte budget, so a batch is always
|
|
72
|
+
// a PREFIX of the pending changes. One section (frame) per table.
|
|
73
|
+
const sections = new Map();
|
|
74
|
+
let included = 0;
|
|
75
|
+
let includedMaxSequence = 0;
|
|
76
|
+
let budgetUsed = 0;
|
|
77
|
+
let truncated = false;
|
|
78
|
+
const tooLarge = [];
|
|
79
|
+
for (const row of rows) {
|
|
80
|
+
const entry = uploadable.get(row.tid);
|
|
81
|
+
if (!entry)
|
|
82
|
+
continue;
|
|
83
|
+
let section = sections.get(row.tid);
|
|
84
|
+
if (!section) {
|
|
85
|
+
const select = entry.columns.length === 0
|
|
86
|
+
? `SELECT 1 FROM ${entry.table.quoted} WHERE ${entry.table.keyQuoted} = $k`
|
|
87
|
+
: `SELECT ${entry.columns.map((c) => pushSelectExpression(quote(c.name), c.type)).join(', ')} FROM ${entry.table.quoted} WHERE ${entry.table.keyQuoted} = $k`;
|
|
88
|
+
const writer = new G9RowBatchWriter();
|
|
89
|
+
writer.beginSection(G9ERowBatchOp.Push, entry.negotiated.tableId, entry.table.model.keyType, entry.columns.map((c) => c.type));
|
|
90
|
+
section = { table: entry.table, negotiated: entry.negotiated, columns: entry.columns, writer, select, rows: 0 };
|
|
91
|
+
sections.set(row.tid, section);
|
|
92
|
+
}
|
|
93
|
+
const hlc = row.hlc === 0 ? 0 : row.hlc + offsetMs;
|
|
94
|
+
const keyBytes = utf8Encode(row.key).length;
|
|
95
|
+
if (row.dirty === 2) {
|
|
96
|
+
const deleteBytes = ROW_OVERHEAD + keyBytes;
|
|
97
|
+
if (included > 0 && budgetUsed + deleteBytes > maxBytes) {
|
|
98
|
+
truncated = true;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
section.writer.beginPushRow(row.key, G9EPushRowOp.Delete, row.seq, row.base, G9Long.ZERO, hlc);
|
|
102
|
+
section.writer.endRow();
|
|
103
|
+
section.rows++;
|
|
104
|
+
included++;
|
|
105
|
+
includedMaxSequence = row.seq;
|
|
106
|
+
budgetUsed += deleteBytes;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const found = await connection.query(section.select, { $k: keyValue(section.table, row.key) });
|
|
110
|
+
const values = found[0];
|
|
111
|
+
if (!values) {
|
|
112
|
+
section.writer.beginPushRow(row.key, G9EPushRowOp.Delete, row.seq, row.base, G9Long.ZERO, hlc);
|
|
113
|
+
section.writer.endRow();
|
|
114
|
+
section.rows++;
|
|
115
|
+
included++;
|
|
116
|
+
includedMaxSequence = row.seq;
|
|
117
|
+
budgetUsed += ROW_OVERHEAD + keyBytes;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
let rowBytes = ROW_OVERHEAD + keyBytes;
|
|
121
|
+
for (let j = 0; j < section.columns.length; j++)
|
|
122
|
+
rowBytes += estimateEncodedBytes(values[j], section.columns[j].type);
|
|
123
|
+
if (rowBytes > maxBytes) {
|
|
124
|
+
// Can never fit, whatever else the batch carries: reject it here so it does not block every later push.
|
|
125
|
+
tooLarge.push({ tid: row.tid, key: row.key, bytes: rowBytes });
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (included > 0 && budgetUsed + rowBytes > maxBytes) {
|
|
129
|
+
truncated = true;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
let pushMask = G9Long.ZERO;
|
|
133
|
+
for (let j = 0; j < section.columns.length; j++) {
|
|
134
|
+
if (row.mask.testBit(section.columns[j].local))
|
|
135
|
+
pushMask = pushMask.or(G9Long.ONE.shiftLeft(j));
|
|
136
|
+
}
|
|
137
|
+
section.writer.beginPushRow(row.key, G9EPushRowOp.Upsert, row.seq, row.base, pushMask, hlc);
|
|
138
|
+
for (let j = 0; j < section.columns.length; j++)
|
|
139
|
+
writeLocalValue(section.writer, values[j], section.columns[j].type, store.options);
|
|
140
|
+
section.writer.endRow();
|
|
141
|
+
section.rows++;
|
|
142
|
+
included++;
|
|
143
|
+
includedMaxSequence = row.seq;
|
|
144
|
+
budgetUsed += rowBytes;
|
|
145
|
+
}
|
|
146
|
+
for (const { tid, key, bytes } of tooLarge) {
|
|
147
|
+
await connection.exec('UPDATE g9_rows SET rejected = 1 WHERE tid = $t AND pk = $pk', { $t: tid, $pk: key });
|
|
148
|
+
await connection.exec('INSERT OR REPLACE INTO g9_rejections (tid, pk, code, message, sql_state, constraint_name, column_name, details, at_utc) ' +
|
|
149
|
+
'VALUES ($t, $pk, $code, $message, NULL, NULL, NULL, NULL, $at)', {
|
|
150
|
+
$t: tid,
|
|
151
|
+
$pk: key,
|
|
152
|
+
$code: G9SyncErrorCodes.RejectTooLarge,
|
|
153
|
+
$message: `The row needs about ${bytes} bytes; the server accepts at most ${maxBytes} bytes per push.`,
|
|
154
|
+
$at: nowMs(),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
if (included === 0 && commands.length === 0) {
|
|
158
|
+
if (tooLarge.length === 0)
|
|
159
|
+
return null;
|
|
160
|
+
return { batchId: 0, maxSequence: 0, frames: [], tables: [], rowCount: 0, truncated: false, rejectedLocally: tooLarge.length, commands: [] };
|
|
161
|
+
}
|
|
162
|
+
const frames = [];
|
|
163
|
+
const usedTables = [];
|
|
164
|
+
sections.forEach((section) => {
|
|
165
|
+
if (section.rows === 0)
|
|
166
|
+
return;
|
|
167
|
+
section.writer.endSection();
|
|
168
|
+
frames.push(section.writer.toFrame({ compressor: store.options.frameCompressor }));
|
|
169
|
+
usedTables.push({ tableId: section.negotiated.tableId, columns: section.columns.map((c) => c.name) });
|
|
170
|
+
});
|
|
171
|
+
let batchId;
|
|
172
|
+
let maxSequence;
|
|
173
|
+
if (inflightBatch !== null) {
|
|
174
|
+
batchId = Number(inflightBatch);
|
|
175
|
+
maxSequence = Number(inflightMax);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
batchId = Number((_a = (await meta(connection, 'next_batch_id'))) !== null && _a !== void 0 ? _a : '1');
|
|
179
|
+
maxSequence = includedMaxSequence;
|
|
180
|
+
await setMeta(connection, 'next_batch_id', String(batchId + 1));
|
|
181
|
+
await setMeta(connection, 'inflight_batch_id', String(batchId));
|
|
182
|
+
await setMeta(connection, 'inflight_max_seq', String(maxSequence));
|
|
183
|
+
}
|
|
184
|
+
for (const item of commands) {
|
|
185
|
+
await connection.exec('UPDATE g9_commands SET batch_id = $b WHERE command_id = $id', { $b: batchId, $id: item.commandId });
|
|
186
|
+
}
|
|
187
|
+
return { batchId, maxSequence, frames, tables: usedTables, rowCount: included, truncated, rejectedLocally: tooLarge.length, commands };
|
|
188
|
+
}));
|
|
189
|
+
}
|
|
190
|
+
/** `ForgetUploadedRowsAsync`: removes uploaded rows of an UPLOAD-ONLY table without uploading a delete. */
|
|
191
|
+
export async function forgetUploadedRows(store, localTable, keys) {
|
|
192
|
+
store.ensureInitialized();
|
|
193
|
+
const table = getLocal(store, localTable);
|
|
194
|
+
if (!table.negotiated || table.negotiated.direction !== G9ESyncDirection.UploadOnly) {
|
|
195
|
+
throw new Error(`'${localTable}' is not upload-only: deleting its rows locally is a change the server must receive.`);
|
|
196
|
+
}
|
|
197
|
+
return store.writeLock.run(() => store.writer().transaction('immediate', async () => {
|
|
198
|
+
const connection = store.writer();
|
|
199
|
+
await connection.exec('UPDATE g9_ctx SET applying = 1 WHERE id = 1'); // a library write: not captured
|
|
200
|
+
await connection.exec('CREATE TEMP TABLE IF NOT EXISTS g9_forget (pk TEXT PRIMARY KEY); DELETE FROM g9_forget');
|
|
201
|
+
if (keys === null) {
|
|
202
|
+
await connection.exec('INSERT INTO g9_forget (pk) SELECT pk FROM g9_rows WHERE tid = $t AND dirty = 0', { $t: table.tid });
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
for (const key of keys) {
|
|
206
|
+
await connection.exec('INSERT OR IGNORE INTO g9_forget (pk) SELECT pk FROM g9_rows WHERE tid = $t AND pk = $pk AND dirty = 0', { $t: table.tid, $pk: key });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const forgotten = asNumber(await connection.scalar('SELECT count(*) FROM g9_forget'));
|
|
210
|
+
await connection.exec(`DELETE FROM ${table.quoted} WHERE CAST(${table.keyQuoted} AS TEXT) IN (SELECT pk FROM g9_forget)`);
|
|
211
|
+
await connection.exec('DELETE FROM g9_rows WHERE tid = $t AND pk IN (SELECT pk FROM g9_forget); DELETE FROM g9_forget', { $t: table.tid });
|
|
212
|
+
await connection.exec('UPDATE g9_ctx SET applying = 0 WHERE id = 1');
|
|
213
|
+
return forgotten;
|
|
214
|
+
}));
|
|
215
|
+
}
|
|
216
|
+
/** `ApplyPushResultAsync`. */
|
|
217
|
+
export async function applyPushResult(store, batch, result, signal) {
|
|
218
|
+
store.ensureInitialized();
|
|
219
|
+
return store.writeLock.run(() => store.writer().transaction('immediate', async () => {
|
|
220
|
+
var _a, _b, _c, _d, _e;
|
|
221
|
+
const connection = store.writer();
|
|
222
|
+
// Before BeginApply: finished commands release their rows, so the canonical rows below replace the optimistic ones.
|
|
223
|
+
const completed = await completeCommands(connection, result.commandResults);
|
|
224
|
+
const context = await beginApply(store, connection, { dirtyAfterSequence: batch.maxSequence, backfills: [], signal });
|
|
225
|
+
context.report.commandsCompleted = completed;
|
|
226
|
+
const acceptSql = 'UPDATE g9_rows SET server_ver = $v, ' +
|
|
227
|
+
'dirty = CASE WHEN local_seq <= $m THEN 0 ELSE dirty END, ' +
|
|
228
|
+
'dirty_mask = CASE WHEN local_seq <= $m THEN 0 ELSE dirty_mask END, ' +
|
|
229
|
+
'deleted = CASE WHEN local_seq <= $m AND dirty = 2 THEN 1 ELSE deleted END, ' +
|
|
230
|
+
'rejected = 0 WHERE tid = $t AND pk = $pk; ' +
|
|
231
|
+
'DELETE FROM g9_rejections WHERE tid = $t AND pk = $pk';
|
|
232
|
+
for (const accepted of result.accepted) {
|
|
233
|
+
const table = store.byServerId.get(accepted.tableId);
|
|
234
|
+
if (!table)
|
|
235
|
+
continue;
|
|
236
|
+
for (let i = 0; i < accepted.keys.length; i++) {
|
|
237
|
+
await context.queue(acceptSql, { $v: i < accepted.versions.length ? accepted.versions[i] : 0, $m: batch.maxSequence, $t: table.tid, $pk: accepted.keys[i] });
|
|
238
|
+
context.report.accepted++;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const rejectSql = 'UPDATE g9_rows SET rejected = 1 WHERE tid = $t AND pk = $pk AND local_seq <= $m; ' +
|
|
242
|
+
'INSERT OR REPLACE INTO g9_rejections (tid, pk, code, message, sql_state, constraint_name, column_name, details, at_utc) ' +
|
|
243
|
+
'VALUES ($t, $pk, $code, $message, $state, $constraint, $column, $details, $now)';
|
|
244
|
+
const now = nowMs();
|
|
245
|
+
for (const rejection of result.rejections) {
|
|
246
|
+
const table = store.byServerId.get(rejection.tableId);
|
|
247
|
+
if (!table)
|
|
248
|
+
continue;
|
|
249
|
+
await context.queue(rejectSql, {
|
|
250
|
+
$t: table.tid,
|
|
251
|
+
$pk: rejection.key,
|
|
252
|
+
$m: batch.maxSequence,
|
|
253
|
+
$code: rejection.code,
|
|
254
|
+
$message: (_a = rejection.message) !== null && _a !== void 0 ? _a : null,
|
|
255
|
+
$state: (_b = rejection.sqlState) !== null && _b !== void 0 ? _b : null,
|
|
256
|
+
$constraint: (_c = rejection.constraint) !== null && _c !== void 0 ? _c : null,
|
|
257
|
+
$column: (_d = rejection.column) !== null && _d !== void 0 ? _d : null,
|
|
258
|
+
$details: (_e = rejection.details) !== null && _e !== void 0 ? _e : null,
|
|
259
|
+
$now: now,
|
|
260
|
+
});
|
|
261
|
+
context.report.rejected++;
|
|
262
|
+
}
|
|
263
|
+
context.report.conflicts = result.conflicts.length;
|
|
264
|
+
await context.flush();
|
|
265
|
+
for (const frame of result.canonicalFrames)
|
|
266
|
+
await applyFrame(store, context, frame, null);
|
|
267
|
+
await endApply(context);
|
|
268
|
+
await connection.exec("DELETE FROM g9_meta WHERE key IN ('inflight_batch_id', 'inflight_max_seq')");
|
|
269
|
+
return context.report;
|
|
270
|
+
}));
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=push.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/sqlite/push.ts"],"names":[],"mappings":"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","sourcesContent":["// Twin of G9SyncData.Client.Sqlite/G9CSqliteSyncStore.Push.cs: dirty rows in local change order become push frames; the\n// result clears exactly the rows that were sent (rows edited meanwhile stay dirty), flags rejections and applies canonical\n// server rows.\nimport { G9ESyncDirection } from '../abstractions/enums.js';\nimport { G9SyncErrorCodes } from '../abstractions/errors.js';\nimport { G9Long } from '../abstractions/long.js';\nimport { utf8Encode } from '../internal/utf8.js';\nimport { G9ApplyReport, type G9PushBatch } from '../client/storeTypes.js';\nimport type { G9DtNegotiatedTable, G9DtPushResult, G9DtTableColumns } from '../protocol/dtos.js';\nimport { G9EPushRowOp, G9ERowBatchOp } from '../protocol/rowBatch/format.js';\nimport { G9RowBatchWriter } from '../protocol/rowBatch/writer.js';\nimport { applyFrame, beginApply, endApply } from './apply.js';\nimport { completeCommands, readPendingCommands } from './commands.js';\nimport { asLong, asNumber, asString } from './connection.js';\nimport type { G9SqlValue } from './driver.js';\nimport { findByServerName, getLocal, keyValue, meta, nowMs, setMeta, type LocalTable, type StoreInternals } from './internals.js';\nimport { quote } from './sql.js';\nimport { estimateEncodedBytes, pushSelectExpression, writeLocalValue } from './values.js';\n\ninterface PushColumn {\n name: string;\n type: number;\n local: number;\n}\n\ninterface PushSection {\n table: LocalTable;\n negotiated: G9DtNegotiatedTable;\n columns: PushColumn[];\n writer: G9RowBatchWriter;\n select: string;\n rows: number;\n}\n\nconst ROW_OVERHEAD = 48;\n\n/** `BuildPushBatchAsync`: the next batch of pending changes, oldest first, within the row and byte budgets. */\nexport async function buildPushBatch(store: StoreInternals, tables: readonly G9DtNegotiatedTable[], maxRows: number, maxBytes: number): Promise<G9PushBatch | null> {\n store.ensureInitialized();\n const uploadable = new Map<number, { table: LocalTable; negotiated: G9DtNegotiatedTable; columns: PushColumn[] }>();\n for (const negotiated of tables) {\n if (negotiated.direction === G9ESyncDirection.DownloadOnly) continue;\n const local = findByServerName(store, negotiated.name);\n if (!local) continue;\n const readOnly = new Set<string>();\n for (const c of negotiated.readOnlyColumns) readOnly.add(c.toLowerCase());\n for (const c of negotiated.computedColumns) readOnly.add(c.toLowerCase());\n const columns = negotiated.columns\n .filter((c) => !readOnly.has(c.name.toLowerCase()) && local.model.indexOf(c.name) >= 0)\n .map((c) => ({ name: c.name, type: c.type, local: local.model.indexOf(c.name) }));\n uploadable.set(local.tid, { table: local, negotiated, columns });\n }\n\n return store.writeLock.run(() =>\n store.writer().transaction('immediate', async () => {\n const connection = store.writer();\n const inflightBatch = await meta(connection, 'inflight_batch_id');\n const inflightMax = await meta(connection, 'inflight_max_seq');\n const offsetText = await meta(connection, 'server_offset_ms');\n const offsetMs = offsetText !== null && /^[+-]?\\d+$/.test(offsetText) ? Number(offsetText) : 0;\n\n const tids = Array.from(uploadable.keys());\n const sql =\n 'SELECT tid, pk, dirty, dirty_mask, server_ver, local_seq, coalesce(hlc_ms, 0) FROM g9_rows WHERE dirty <> 0 AND rejected = 0 AND tid IN (' +\n tids.join(',') +\n ')' +\n (inflightMax === null ? '' : ' AND local_seq <= $imax') +\n ' ORDER BY local_seq LIMIT $max';\n const commands = await readPendingCommands(store, connection, inflightBatch, Math.max(1, maxRows));\n const pending =\n uploadable.size === 0\n ? []\n : await connection.query(sql, inflightMax === null ? { $max: Math.max(1, maxRows) } : { $max: Math.max(1, maxRows), $imax: Number(inflightMax) });\n const rows = pending.map((r) => ({\n tid: asNumber(r[0]),\n key: asString(r[1]),\n dirty: asNumber(r[2]),\n mask: asLong(r[3]),\n base: asNumber(r[4]),\n seq: asNumber(r[5]),\n hlc: asNumber(r[6]),\n }));\n\n if (rows.length === 0 && commands.length === 0) {\n if (inflightBatch !== null) {\n // Everything the in-flight batch carried has been resolved another way; forget it.\n await connection.exec(\"DELETE FROM g9_meta WHERE key IN ('inflight_batch_id', 'inflight_max_seq')\");\n }\n return null;\n }\n\n // Rows are encoded in local_seq order and the batch ends at the first row past the byte budget, so a batch is always\n // a PREFIX of the pending changes. One section (frame) per table.\n const sections = new Map<number, PushSection>();\n let included = 0;\n let includedMaxSequence = 0;\n let budgetUsed = 0;\n let truncated = false;\n const tooLarge: { tid: number; key: string; bytes: number }[] = [];\n for (const row of rows) {\n const entry = uploadable.get(row.tid);\n if (!entry) continue;\n let section = sections.get(row.tid);\n if (!section) {\n const select =\n entry.columns.length === 0\n ? `SELECT 1 FROM ${entry.table.quoted} WHERE ${entry.table.keyQuoted} = $k`\n : `SELECT ${entry.columns.map((c) => pushSelectExpression(quote(c.name), c.type)).join(', ')} FROM ${entry.table.quoted} WHERE ${entry.table.keyQuoted} = $k`;\n const writer = new G9RowBatchWriter();\n writer.beginSection(\n G9ERowBatchOp.Push,\n entry.negotiated.tableId,\n entry.table.model.keyType,\n entry.columns.map((c) => c.type),\n );\n section = { table: entry.table, negotiated: entry.negotiated, columns: entry.columns, writer, select, rows: 0 };\n sections.set(row.tid, section);\n }\n\n const hlc = row.hlc === 0 ? 0 : row.hlc + offsetMs;\n const keyBytes = utf8Encode(row.key).length;\n if (row.dirty === 2) {\n const deleteBytes = ROW_OVERHEAD + keyBytes;\n if (included > 0 && budgetUsed + deleteBytes > maxBytes) {\n truncated = true;\n break;\n }\n section.writer.beginPushRow(row.key, G9EPushRowOp.Delete, row.seq, row.base, G9Long.ZERO, hlc);\n section.writer.endRow();\n section.rows++;\n included++;\n includedMaxSequence = row.seq;\n budgetUsed += deleteBytes;\n continue;\n }\n\n const found = await connection.query(section.select, { $k: keyValue(section.table, row.key) });\n const values = found[0];\n if (!values) {\n section.writer.beginPushRow(row.key, G9EPushRowOp.Delete, row.seq, row.base, G9Long.ZERO, hlc);\n section.writer.endRow();\n section.rows++;\n included++;\n includedMaxSequence = row.seq;\n budgetUsed += ROW_OVERHEAD + keyBytes;\n continue;\n }\n\n let rowBytes = ROW_OVERHEAD + keyBytes;\n for (let j = 0; j < section.columns.length; j++) rowBytes += estimateEncodedBytes(values[j] as G9SqlValue, (section.columns[j] as PushColumn).type);\n if (rowBytes > maxBytes) {\n // Can never fit, whatever else the batch carries: reject it here so it does not block every later push.\n tooLarge.push({ tid: row.tid, key: row.key, bytes: rowBytes });\n continue;\n }\n if (included > 0 && budgetUsed + rowBytes > maxBytes) {\n truncated = true;\n break;\n }\n\n let pushMask = G9Long.ZERO;\n for (let j = 0; j < section.columns.length; j++) {\n if (row.mask.testBit((section.columns[j] as PushColumn).local)) pushMask = pushMask.or(G9Long.ONE.shiftLeft(j));\n }\n section.writer.beginPushRow(row.key, G9EPushRowOp.Upsert, row.seq, row.base, pushMask, hlc);\n for (let j = 0; j < section.columns.length; j++) writeLocalValue(section.writer, values[j] as G9SqlValue, (section.columns[j] as PushColumn).type, store.options);\n section.writer.endRow();\n section.rows++;\n included++;\n includedMaxSequence = row.seq;\n budgetUsed += rowBytes;\n }\n\n for (const { tid, key, bytes } of tooLarge) {\n await connection.exec('UPDATE g9_rows SET rejected = 1 WHERE tid = $t AND pk = $pk', { $t: tid, $pk: key });\n await connection.exec(\n 'INSERT OR REPLACE INTO g9_rejections (tid, pk, code, message, sql_state, constraint_name, column_name, details, at_utc) ' +\n 'VALUES ($t, $pk, $code, $message, NULL, NULL, NULL, NULL, $at)',\n {\n $t: tid,\n $pk: key,\n $code: G9SyncErrorCodes.RejectTooLarge,\n $message: `The row needs about ${bytes} bytes; the server accepts at most ${maxBytes} bytes per push.`,\n $at: nowMs(),\n },\n );\n }\n\n if (included === 0 && commands.length === 0) {\n if (tooLarge.length === 0) return null;\n return { batchId: 0, maxSequence: 0, frames: [], tables: [], rowCount: 0, truncated: false, rejectedLocally: tooLarge.length, commands: [] };\n }\n\n const frames: Uint8Array[] = [];\n const usedTables: G9DtTableColumns[] = [];\n sections.forEach((section) => {\n if (section.rows === 0) return;\n section.writer.endSection();\n frames.push(section.writer.toFrame({ compressor: store.options.frameCompressor }));\n usedTables.push({ tableId: section.negotiated.tableId, columns: section.columns.map((c) => c.name) });\n });\n\n let batchId: number;\n let maxSequence: number;\n if (inflightBatch !== null) {\n batchId = Number(inflightBatch);\n maxSequence = Number(inflightMax);\n } else {\n batchId = Number((await meta(connection, 'next_batch_id')) ?? '1');\n maxSequence = includedMaxSequence;\n await setMeta(connection, 'next_batch_id', String(batchId + 1));\n await setMeta(connection, 'inflight_batch_id', String(batchId));\n await setMeta(connection, 'inflight_max_seq', String(maxSequence));\n }\n for (const item of commands) {\n await connection.exec('UPDATE g9_commands SET batch_id = $b WHERE command_id = $id', { $b: batchId, $id: item.commandId });\n }\n return { batchId, maxSequence, frames, tables: usedTables, rowCount: included, truncated, rejectedLocally: tooLarge.length, commands };\n }),\n );\n}\n\n/** `ForgetUploadedRowsAsync`: removes uploaded rows of an UPLOAD-ONLY table without uploading a delete. */\nexport async function forgetUploadedRows(store: StoreInternals, localTable: string, keys: readonly string[] | null): Promise<number> {\n store.ensureInitialized();\n const table = getLocal(store, localTable);\n if (!table.negotiated || table.negotiated.direction !== G9ESyncDirection.UploadOnly) {\n throw new Error(`'${localTable}' is not upload-only: deleting its rows locally is a change the server must receive.`);\n }\n return store.writeLock.run(() =>\n store.writer().transaction('immediate', async () => {\n const connection = store.writer();\n await connection.exec('UPDATE g9_ctx SET applying = 1 WHERE id = 1'); // a library write: not captured\n await connection.exec('CREATE TEMP TABLE IF NOT EXISTS g9_forget (pk TEXT PRIMARY KEY); DELETE FROM g9_forget');\n if (keys === null) {\n await connection.exec('INSERT INTO g9_forget (pk) SELECT pk FROM g9_rows WHERE tid = $t AND dirty = 0', { $t: table.tid });\n } else {\n for (const key of keys) {\n await connection.exec('INSERT OR IGNORE INTO g9_forget (pk) SELECT pk FROM g9_rows WHERE tid = $t AND pk = $pk AND dirty = 0', { $t: table.tid, $pk: key });\n }\n }\n const forgotten = asNumber(await connection.scalar('SELECT count(*) FROM g9_forget'));\n await connection.exec(`DELETE FROM ${table.quoted} WHERE CAST(${table.keyQuoted} AS TEXT) IN (SELECT pk FROM g9_forget)`);\n await connection.exec('DELETE FROM g9_rows WHERE tid = $t AND pk IN (SELECT pk FROM g9_forget); DELETE FROM g9_forget', { $t: table.tid });\n await connection.exec('UPDATE g9_ctx SET applying = 0 WHERE id = 1');\n return forgotten;\n }),\n );\n}\n\n/** `ApplyPushResultAsync`. */\nexport async function applyPushResult(store: StoreInternals, batch: G9PushBatch, result: G9DtPushResult, signal?: AbortSignal): Promise<G9ApplyReport> {\n store.ensureInitialized();\n return store.writeLock.run(() =>\n store.writer().transaction('immediate', async () => {\n const connection = store.writer();\n // Before BeginApply: finished commands release their rows, so the canonical rows below replace the optimistic ones.\n const completed = await completeCommands(connection, result.commandResults);\n const context = await beginApply(store, connection, { dirtyAfterSequence: batch.maxSequence, backfills: [], signal });\n context.report.commandsCompleted = completed;\n\n const acceptSql =\n 'UPDATE g9_rows SET server_ver = $v, ' +\n 'dirty = CASE WHEN local_seq <= $m THEN 0 ELSE dirty END, ' +\n 'dirty_mask = CASE WHEN local_seq <= $m THEN 0 ELSE dirty_mask END, ' +\n 'deleted = CASE WHEN local_seq <= $m AND dirty = 2 THEN 1 ELSE deleted END, ' +\n 'rejected = 0 WHERE tid = $t AND pk = $pk; ' +\n 'DELETE FROM g9_rejections WHERE tid = $t AND pk = $pk';\n for (const accepted of result.accepted) {\n const table = store.byServerId.get(accepted.tableId);\n if (!table) continue;\n for (let i = 0; i < accepted.keys.length; i++) {\n await context.queue(acceptSql, { $v: i < accepted.versions.length ? (accepted.versions[i] as number) : 0, $m: batch.maxSequence, $t: table.tid, $pk: accepted.keys[i] as string });\n context.report.accepted++;\n }\n }\n\n const rejectSql =\n 'UPDATE g9_rows SET rejected = 1 WHERE tid = $t AND pk = $pk AND local_seq <= $m; ' +\n 'INSERT OR REPLACE INTO g9_rejections (tid, pk, code, message, sql_state, constraint_name, column_name, details, at_utc) ' +\n 'VALUES ($t, $pk, $code, $message, $state, $constraint, $column, $details, $now)';\n const now = nowMs();\n for (const rejection of result.rejections) {\n const table = store.byServerId.get(rejection.tableId);\n if (!table) continue;\n await context.queue(rejectSql, {\n $t: table.tid,\n $pk: rejection.key,\n $m: batch.maxSequence,\n $code: rejection.code,\n $message: rejection.message ?? null,\n $state: rejection.sqlState ?? null,\n $constraint: rejection.constraint ?? null,\n $column: rejection.column ?? null,\n $details: rejection.details ?? null,\n $now: now,\n });\n context.report.rejected++;\n }\n\n context.report.conflicts = result.conflicts.length;\n await context.flush();\n for (const frame of result.canonicalFrames) await applyFrame(store, context, frame, null);\n\n await endApply(context);\n await connection.exec(\"DELETE FROM g9_meta WHERE key IN ('inflight_batch_id', 'inflight_max_seq')\");\n return context.report;\n }),\n );\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { G9ChangeFeedEntry, G9LocalRowEntry, G9PendingSummary, G9ResumePoint, G9StagedPull, G9SyncRejection, G9SyncRunReport } from '../client/storeTypes.js';
|
|
2
|
+
import type { G9DtNegotiatedTable, G9DtSyncChunk, G9DtTableDigest } from '../protocol/dtos.js';
|
|
3
|
+
import { type G9SqliteConnection } from './connection.js';
|
|
4
|
+
import type { G9SqlValue } from './driver.js';
|
|
5
|
+
import { type LocalTable, type StoreInternals } from './internals.js';
|
|
6
|
+
import { ticksToUnixMs } from './values.js';
|
|
7
|
+
export declare function readStagedRun(connection: G9SqliteConnection, runId: string): Promise<G9StagedPull | null>;
|
|
8
|
+
export declare function getStagedPull(store: StoreInternals, generation: string | null): Promise<G9StagedPull | null>;
|
|
9
|
+
export declare function pruneStagedPulls(store: StoreInternals, olderThanUtcMs: number, maxBytes: number, keepGeneration: string | null): Promise<number>;
|
|
10
|
+
export declare function discardAllStagedPulls(store: StoreInternals): Promise<number>;
|
|
11
|
+
export declare function beginStagedPull(store: StoreInternals, pull: G9StagedPull): Promise<void>;
|
|
12
|
+
export declare function stageChunk(store: StoreInternals, runId: string, chunk: G9DtSyncChunk, durability: (connection: G9SqliteConnection) => Promise<(() => Promise<void>) | null>): Promise<void>;
|
|
13
|
+
export declare function getResumePoint(store: StoreInternals, runId: string): Promise<G9ResumePoint>;
|
|
14
|
+
export declare function discardStagedPull(store: StoreInternals, runId: string): Promise<void>;
|
|
15
|
+
export declare function computeDigest(store: StoreInternals, subscriptionKey: string, table: G9DtNegotiatedTable): Promise<G9DtTableDigest>;
|
|
16
|
+
export declare function readLocalEntries(store: StoreInternals, subscriptionKey: string, table: G9DtNegotiatedTable, buckets: ReadonlySet<number>, serverKeys: ReadonlySet<string>): Promise<G9LocalRowEntry[]>;
|
|
17
|
+
export declare function getPendingSummary(store: StoreInternals): Promise<G9PendingSummary>;
|
|
18
|
+
export declare function readChangeFeed(store: StoreInternals, localTable: string, afterSequence: number, limit: number): Promise<G9ChangeFeedEntry[]>;
|
|
19
|
+
export declare function getChangeSequence(store: StoreInternals): Promise<number>;
|
|
20
|
+
export declare function getRejections(store: StoreInternals): Promise<G9SyncRejection[]>;
|
|
21
|
+
export declare function retryRejected(store: StoreInternals, localTable: string | null, key: string | null): Promise<number>;
|
|
22
|
+
export declare function markDirty(store: StoreInternals, localTable: string, keys: readonly string[]): Promise<number>;
|
|
23
|
+
/** Marks the rows `keysSql` selects as pending, giving EACH its own local sequence (LES-0039). */
|
|
24
|
+
export declare function markPending(connection: G9SqliteConnection, table: LocalTable, keysSql: string, params: {
|
|
25
|
+
[name: string]: G9SqlValue;
|
|
26
|
+
}, set: string): Promise<number>;
|
|
27
|
+
export declare function adoptExistingRows(store: StoreInternals): Promise<number>;
|
|
28
|
+
export declare function saveRunReport(store: StoreInternals, report: G9SyncRunReport, keep: number): Promise<void>;
|
|
29
|
+
export declare function getRecentRunReports(store: StoreInternals, count: number): Promise<G9SyncRunReport[]>;
|
|
30
|
+
export declare function getPhaseStatistics(store: StoreInternals): Promise<{
|
|
31
|
+
[key: string]: number;
|
|
32
|
+
}>;
|
|
33
|
+
export declare function recordPhaseStatistics(store: StoreInternals, samples: {
|
|
34
|
+
[key: string]: number;
|
|
35
|
+
}): Promise<void>;
|
|
36
|
+
export declare function purgeDeletedFeed(store: StoreInternals, keep: number): Promise<number>;
|
|
37
|
+
export declare function getChangeFeedFloor(store: StoreInternals): Promise<number>;
|
|
38
|
+
export { ticksToUnixMs };
|