@quereus/sync 4.3.2 → 4.4.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/README.md +19 -1
- package/dist/src/create-sync-module.d.ts +11 -1
- package/dist/src/create-sync-module.d.ts.map +1 -1
- package/dist/src/create-sync-module.js +2 -2
- package/dist/src/create-sync-module.js.map +1 -1
- package/dist/src/index.d.ts +4 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +9 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/metadata/change-log.d.ts +39 -3
- package/dist/src/metadata/change-log.d.ts.map +1 -1
- package/dist/src/metadata/change-log.js +43 -8
- package/dist/src/metadata/change-log.js.map +1 -1
- package/dist/src/metadata/column-version.d.ts +62 -9
- package/dist/src/metadata/column-version.d.ts.map +1 -1
- package/dist/src/metadata/column-version.js +79 -25
- package/dist/src/metadata/column-version.js.map +1 -1
- package/dist/src/metadata/index.d.ts +1 -0
- package/dist/src/metadata/index.d.ts.map +1 -1
- package/dist/src/metadata/index.js +1 -0
- package/dist/src/metadata/index.js.map +1 -1
- package/dist/src/metadata/keys.d.ts +88 -22
- package/dist/src/metadata/keys.d.ts.map +1 -1
- package/dist/src/metadata/keys.js +134 -77
- package/dist/src/metadata/keys.js.map +1 -1
- package/dist/src/metadata/pk-identity.d.ts +60 -0
- package/dist/src/metadata/pk-identity.d.ts.map +1 -0
- package/dist/src/metadata/pk-identity.js +94 -0
- package/dist/src/metadata/pk-identity.js.map +1 -0
- package/dist/src/metadata/tombstones.d.ts +41 -11
- package/dist/src/metadata/tombstones.d.ts.map +1 -1
- package/dist/src/metadata/tombstones.js +58 -33
- package/dist/src/metadata/tombstones.js.map +1 -1
- package/dist/src/sync/change-applicator.d.ts +30 -1
- package/dist/src/sync/change-applicator.d.ts.map +1 -1
- package/dist/src/sync/change-applicator.js +161 -40
- package/dist/src/sync/change-applicator.js.map +1 -1
- package/dist/src/sync/maintenance.d.ts +70 -0
- package/dist/src/sync/maintenance.d.ts.map +1 -0
- package/dist/src/sync/maintenance.js +84 -0
- package/dist/src/sync/maintenance.js.map +1 -0
- package/dist/src/sync/protocol.d.ts +61 -10
- package/dist/src/sync/protocol.d.ts.map +1 -1
- package/dist/src/sync/protocol.js +15 -0
- package/dist/src/sync/protocol.js.map +1 -1
- package/dist/src/sync/snapshot-identity.d.ts +62 -0
- package/dist/src/sync/snapshot-identity.d.ts.map +1 -0
- package/dist/src/sync/snapshot-identity.js +88 -0
- package/dist/src/sync/snapshot-identity.js.map +1 -0
- package/dist/src/sync/snapshot-stream.d.ts +8 -1
- package/dist/src/sync/snapshot-stream.d.ts.map +1 -1
- package/dist/src/sync/snapshot-stream.js +166 -67
- package/dist/src/sync/snapshot-stream.js.map +1 -1
- package/dist/src/sync/snapshot.d.ts +1 -1
- package/dist/src/sync/snapshot.d.ts.map +1 -1
- package/dist/src/sync/snapshot.js +75 -44
- package/dist/src/sync/snapshot.js.map +1 -1
- package/dist/src/sync/staged-transaction-metadata.d.ts +75 -0
- package/dist/src/sync/staged-transaction-metadata.d.ts.map +1 -0
- package/dist/src/sync/staged-transaction-metadata.js +100 -0
- package/dist/src/sync/staged-transaction-metadata.js.map +1 -0
- package/dist/src/sync/store-adapter.d.ts +10 -4
- package/dist/src/sync/store-adapter.d.ts.map +1 -1
- package/dist/src/sync/store-adapter.js +244 -21
- package/dist/src/sync/store-adapter.js.map +1 -1
- package/dist/src/sync/sync-context.d.ts +54 -2
- package/dist/src/sync/sync-context.d.ts.map +1 -1
- package/dist/src/sync/sync-context.js +66 -0
- package/dist/src/sync/sync-context.js.map +1 -1
- package/dist/src/sync/sync-manager-impl.d.ts +70 -4
- package/dist/src/sync/sync-manager-impl.d.ts.map +1 -1
- package/dist/src/sync/sync-manager-impl.js +206 -36
- package/dist/src/sync/sync-manager-impl.js.map +1 -1
- package/dist/src/sync/wire.d.ts +47 -6
- package/dist/src/sync/wire.d.ts.map +1 -1
- package/dist/src/sync/wire.js +62 -11
- package/dist/src/sync/wire.js.map +1 -1
- package/package.json +6 -6
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
* Full in-memory snapshot get/apply for small databases
|
|
5
5
|
* or when streaming is not needed.
|
|
6
6
|
*/
|
|
7
|
-
import { assertWithinDrift } from '../clock/hlc.js';
|
|
7
|
+
import { assertWithinDrift, compareHLC } from '../clock/hlc.js';
|
|
8
8
|
import { deserializeColumnVersion } from '../metadata/column-version.js';
|
|
9
9
|
import { deserializeMigration } from '../metadata/schema-migration.js';
|
|
10
10
|
import { deserializeTombstone } from '../metadata/tombstones.js';
|
|
11
|
-
import { buildAllColumnVersionsScanBounds, buildAllTombstonesScanBounds, buildAllSchemaMigrationsScanBounds, buildAllChangeLogScanBounds, parseColumnVersionKey, parseTombstoneKey, parseSchemaMigrationKey,
|
|
11
|
+
import { buildAllColumnVersionsScanBounds, buildAllTombstonesScanBounds, buildAllSchemaMigrationsScanBounds, buildAllChangeLogScanBounds, parseColumnVersionKey, parseTombstoneKey, parseSchemaMigrationKey, encodePkIdentity, } from '../metadata/keys.js';
|
|
12
|
+
import { SNAPSHOT_WIRE_FORMAT_VERSION, } from './protocol.js';
|
|
12
13
|
import { toError } from './sync-context.js';
|
|
14
|
+
import { createSnapshotKeyingResolver, keepMaxHLC, reconcileCell, tableScopedRowKey, } from './snapshot-identity.js';
|
|
13
15
|
import { admitGroup } from './admission.js';
|
|
14
16
|
/**
|
|
15
17
|
* Get a full snapshot of all data and schema state.
|
|
@@ -23,7 +25,9 @@ export async function getSnapshot(ctx) {
|
|
|
23
25
|
continue;
|
|
24
26
|
const cv = deserializeColumnVersion(entry.value);
|
|
25
27
|
const tableKey = `${parsed.schema}.${parsed.table}`;
|
|
26
|
-
|
|
28
|
+
// The parsed pk IDENTITY groups one row's cells; the raw pk (needed to
|
|
29
|
+
// address the row on the receiver) rides in each cell's record value.
|
|
30
|
+
const rowKey = parsed.identity;
|
|
27
31
|
if (!tableData.has(tableKey)) {
|
|
28
32
|
tableData.set(tableKey, { schema: parsed.schema, table: parsed.table, rows: new Map() });
|
|
29
33
|
}
|
|
@@ -37,22 +41,16 @@ export async function getSnapshot(ctx) {
|
|
|
37
41
|
// Build table snapshots
|
|
38
42
|
const tables = [];
|
|
39
43
|
for (const { schema, table, rows } of tableData.values()) {
|
|
40
|
-
const columnVersions =
|
|
41
|
-
const
|
|
42
|
-
for (const [rowKey, rowVersionsMap] of rows) {
|
|
43
|
-
const row = Array.from(rowVersionsMap.values()).map(cv => cv.value);
|
|
44
|
-
rowsArray.push(row);
|
|
44
|
+
const columnVersions = [];
|
|
45
|
+
for (const rowVersionsMap of rows.values()) {
|
|
45
46
|
for (const [column, cv] of rowVersionsMap) {
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
// Flat cell records: only the raw pk travels. The sender's own key
|
|
48
|
+
// identity (the `rows` grouping key) stays local — the receiver derives
|
|
49
|
+
// its own identity from each entry's pk.
|
|
50
|
+
columnVersions.push({ column, hlc: cv.hlc, value: cv.value, pk: cv.pk });
|
|
48
51
|
}
|
|
49
52
|
}
|
|
50
|
-
tables.push({
|
|
51
|
-
schema,
|
|
52
|
-
table,
|
|
53
|
-
rows: rowsArray,
|
|
54
|
-
columnVersions,
|
|
55
|
-
});
|
|
53
|
+
tables.push({ schema, table, columnVersions });
|
|
56
54
|
}
|
|
57
55
|
// Collect all schema migrations
|
|
58
56
|
const schemaMigrations = [];
|
|
@@ -84,7 +82,7 @@ export async function getSnapshot(ctx) {
|
|
|
84
82
|
tombstones.push({
|
|
85
83
|
schema: parsed.schema,
|
|
86
84
|
table: parsed.table,
|
|
87
|
-
pk:
|
|
85
|
+
pk: ts.pk,
|
|
88
86
|
hlc: ts.hlc,
|
|
89
87
|
createdAt: ts.createdAt,
|
|
90
88
|
...(ts.priorRow !== undefined ? { priorRow: ts.priorRow } : {}),
|
|
@@ -93,6 +91,7 @@ export async function getSnapshot(ctx) {
|
|
|
93
91
|
return {
|
|
94
92
|
siteId: ctx.getSiteId(),
|
|
95
93
|
hlc: ctx.getCurrentHLC(),
|
|
94
|
+
snapshotFormat: SNAPSHOT_WIRE_FORMAT_VERSION,
|
|
96
95
|
tables,
|
|
97
96
|
schemaMigrations,
|
|
98
97
|
tombstones,
|
|
@@ -102,6 +101,16 @@ export async function getSnapshot(ctx) {
|
|
|
102
101
|
* Apply a full snapshot, replacing all local data.
|
|
103
102
|
*/
|
|
104
103
|
export async function applySnapshot(ctx, snapshot) {
|
|
104
|
+
// Wire-format gate: refuse a snapshot whose stamp is missing or different
|
|
105
|
+
// BEFORE touching any local state — same posture as the `fv:` sync-metadata
|
|
106
|
+
// gate in `SyncManagerImpl.create` and the streaming header gate. Recovery:
|
|
107
|
+
// regenerate the snapshot from a live peer.
|
|
108
|
+
if (snapshot.snapshotFormat !== SNAPSHOT_WIRE_FORMAT_VERSION) {
|
|
109
|
+
const error = new Error(`Snapshot wire format ${snapshot.snapshotFormat ?? '(missing)'} does not match this build's `
|
|
110
|
+
+ `${SNAPSHOT_WIRE_FORMAT_VERSION} — regenerate the snapshot from a live peer`);
|
|
111
|
+
ctx.syncEvents.emitSyncStateChange({ status: 'error', error });
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
105
114
|
// Pre-commit drift validation (mirrors applyChanges / applySnapshotStream): reject a
|
|
106
115
|
// snapshot whose HLC is beyond the drift bound BEFORE clearing or writing anything,
|
|
107
116
|
// so a far-future peer cannot land poison LWW winners. Emit status:'error' first for
|
|
@@ -125,26 +134,32 @@ export async function applySnapshot(ctx, snapshot) {
|
|
|
125
134
|
});
|
|
126
135
|
}
|
|
127
136
|
for (const tableSnapshot of snapshot.tables) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
// No local keying is resolvable yet — the table may only exist once this
|
|
138
|
+
// same unit's `create table` migration runs, and both go to the store in
|
|
139
|
+
// ONE `applyToStore` call. So no receiver identity can group cells into
|
|
140
|
+
// rows here. Instead: one single-column update per cell, ordered by HLC
|
|
141
|
+
// ascending across the table. The store collapses pk spellings with the
|
|
142
|
+
// same rules the identity uses, so a later write lands last and the data
|
|
143
|
+
// converges to per-cell last-writer-wins — matching the reconciled
|
|
144
|
+
// metadata `commitMetadata` files below.
|
|
145
|
+
// NOTE: this multiplies data changes by column count (O(cells), not
|
|
146
|
+
// O(rows)). Fine for the non-streaming path's small-database contract; if
|
|
147
|
+
// it ever shows up as slow, use the streaming path (which groups rows
|
|
148
|
+
// after DDL lands) rather than optimizing here.
|
|
149
|
+
// NOTE: on a COLLAPSE the stored row keeps the EARLIEST spelling of the pk
|
|
150
|
+
// (the store adapter's row group takes `changes[0].pk`), while the metadata
|
|
151
|
+
// below files the newest — both are valid addresses for the same identity,
|
|
152
|
+
// so lookups and relays agree, but two receivers of one snapshot can show
|
|
153
|
+
// different pk text. If that ever needs to be uniform, resolve the winning
|
|
154
|
+
// spelling here the way `commitMetadata` does.
|
|
155
|
+
const sorted = [...tableSnapshot.columnVersions].sort((a, b) => compareHLC(a.hlc, b.hlc));
|
|
156
|
+
for (const cvEntry of sorted) {
|
|
142
157
|
dataChangesToApply.push({
|
|
143
158
|
type: 'update',
|
|
144
159
|
schema: tableSnapshot.schema,
|
|
145
160
|
table: tableSnapshot.table,
|
|
146
|
-
pk,
|
|
147
|
-
columns,
|
|
161
|
+
pk: cvEntry.pk,
|
|
162
|
+
columns: { [cvEntry.column]: cvEntry.value },
|
|
148
163
|
});
|
|
149
164
|
}
|
|
150
165
|
}
|
|
@@ -159,6 +174,10 @@ export async function applySnapshot(ctx, snapshot) {
|
|
|
159
174
|
schemaChanges: schemaChangesToApply,
|
|
160
175
|
applyOptions: { remote: true, bootstrap: true },
|
|
161
176
|
commitMetadata: async () => {
|
|
177
|
+
// Runs AFTER the data apply, so every snapshot table (including ones the
|
|
178
|
+
// snapshot's own `create table` migration installed) has a resolvable
|
|
179
|
+
// schema — local keying is derivable here.
|
|
180
|
+
const resolveKeying = createSnapshotKeyingResolver(ctx);
|
|
162
181
|
// Clear existing CRDT metadata and apply new
|
|
163
182
|
const clearBatch = ctx.kv.batch();
|
|
164
183
|
for await (const entry of ctx.kv.iterate(buildAllColumnVersionsScanBounds())) {
|
|
@@ -171,18 +190,24 @@ export async function applySnapshot(ctx, snapshot) {
|
|
|
171
190
|
clearBatch.delete(entry.key);
|
|
172
191
|
}
|
|
173
192
|
await clearBatch.write();
|
|
174
|
-
// Apply snapshot's column versions and rebuild change log
|
|
193
|
+
// Apply snapshot's column versions and rebuild change log, filed under the
|
|
194
|
+
// RECEIVER's derived identity. A sender with different keying (a raw-keyed
|
|
195
|
+
// relay) can carry several records for what this receiver considers ONE
|
|
196
|
+
// row; reconcile per (identity, column) by greatest HLC so exactly one
|
|
197
|
+
// cell record + one change-log entry survive per cell — last-writer-wins,
|
|
198
|
+
// not entry order.
|
|
175
199
|
const applyBatch = ctx.kv.batch();
|
|
176
200
|
for (const tableSnapshot of snapshot.tables) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
201
|
+
const keying = resolveKeying(tableSnapshot.schema, tableSnapshot.table);
|
|
202
|
+
const rows = new Map();
|
|
203
|
+
for (const cvEntry of tableSnapshot.columnVersions) {
|
|
204
|
+
reconcileCell(rows, cvEntry, keying);
|
|
205
|
+
}
|
|
206
|
+
for (const row of rows.values()) {
|
|
207
|
+
for (const [column, cell] of row.cells) {
|
|
208
|
+
ctx.columnVersions.setColumnVersionBatch(applyBatch, tableSnapshot.schema, tableSnapshot.table, row.pk, column, { hlc: cell.hlc, value: cell.value });
|
|
209
|
+
ctx.changeLog.recordColumnChangeBatch(applyBatch, cell.hlc, tableSnapshot.schema, tableSnapshot.table, row.pk, column);
|
|
210
|
+
}
|
|
186
211
|
}
|
|
187
212
|
}
|
|
188
213
|
// Record schema migrations
|
|
@@ -197,12 +222,18 @@ export async function applySnapshot(ctx, snapshot) {
|
|
|
197
222
|
});
|
|
198
223
|
}
|
|
199
224
|
// Re-write the snapshot's tombstones (the clearBatch above wiped the
|
|
200
|
-
// receiver's existing ones)
|
|
225
|
+
// receiver's existing ones), one per receiver-derived identity, greatest
|
|
226
|
+
// HLC winning — mirrors the streaming consumer's reconciliation.
|
|
201
227
|
// NOTE: `setTombstoneBatch` stamps `createdAt = Date.now()` internally and
|
|
202
228
|
// ignores the sender's `ts.createdAt`, so a bootstrapped tombstone's TTL
|
|
203
229
|
// horizon re-bases to bootstrap time rather than preserved. Accepted phase-1
|
|
204
230
|
// behavior (the tombstone lives a full horizon from bootstrap).
|
|
231
|
+
const tombstoneWinners = new Map();
|
|
205
232
|
for (const ts of snapshot.tombstones) {
|
|
233
|
+
const keying = resolveKeying(ts.schema, ts.table);
|
|
234
|
+
keepMaxHLC(tombstoneWinners, tableScopedRowKey(ts.schema, ts.table, encodePkIdentity(ts.pk, keying)), ts);
|
|
235
|
+
}
|
|
236
|
+
for (const ts of tombstoneWinners.values()) {
|
|
206
237
|
ctx.tombstones.setTombstoneBatch(applyBatch, ts.schema, ts.table, ts.pk, ts.hlc, ts.priorRow);
|
|
207
238
|
}
|
|
208
239
|
await applyBatch.write();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../../src/sync/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../../src/sync/snapshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-transaction staged-metadata overlay for local change capture.
|
|
3
|
+
*
|
|
4
|
+
* `handleTransactionCommit` records one committed engine transaction into a
|
|
5
|
+
* single `WriteBatch`, so every read it performs against committed storage is
|
|
6
|
+
* blind to what the same transaction has already staged. This overlay is the
|
|
7
|
+
* read-your-own-writes companion to that batch: each cell version, tombstone,
|
|
8
|
+
* and row cleanup the transaction stages is noted here, and the capture path
|
|
9
|
+
* consults the overlay BEFORE committed storage. The transaction's event order
|
|
10
|
+
* — which is commit order, the engine's own authority on the row's final state
|
|
11
|
+
* — then decides the recorded metadata, matching what the same statements
|
|
12
|
+
* produce as separate transactions.
|
|
13
|
+
*
|
|
14
|
+
* Rows are keyed by pk IDENTITY (the same {@link encodePkIdentity} the
|
|
15
|
+
* `cv:`/`tb:`/`cl:` storage keys use), so two pk spellings collapse here iff
|
|
16
|
+
* they collide on disk. One instance lives for exactly one
|
|
17
|
+
* `handleTransactionCommit` call.
|
|
18
|
+
*/
|
|
19
|
+
import type { SqlValue } from '@quereus/quereus';
|
|
20
|
+
import type { HLC } from '../clock/hlc.js';
|
|
21
|
+
import type { ColumnVersionData } from '../metadata/column-version.js';
|
|
22
|
+
import { type PkKeying } from '../metadata/keys.js';
|
|
23
|
+
/**
|
|
24
|
+
* A row's staged state, as the delete cleanup consumes it (see
|
|
25
|
+
* `deleteRowVersionsAndLogEntries` in `sync-context.ts`).
|
|
26
|
+
*/
|
|
27
|
+
export interface StagedRowState {
|
|
28
|
+
/** Cell records this transaction has staged live (column → staged HLC). */
|
|
29
|
+
readonly stagedColumns: ReadonlyMap<string, HLC>;
|
|
30
|
+
/**
|
|
31
|
+
* True when this transaction already staged removal of every cell record of
|
|
32
|
+
* the row (a prior same-transaction delete). Cells staged after that removal
|
|
33
|
+
* appear in {@link stagedColumns}; every other column of the row is staged
|
|
34
|
+
* as deleted.
|
|
35
|
+
*/
|
|
36
|
+
readonly rowCleared: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare class StagedTransactionMetadata {
|
|
39
|
+
private readonly getPkKeying;
|
|
40
|
+
private readonly rows;
|
|
41
|
+
constructor(getPkKeying: (schema: string, table: string) => PkKeying);
|
|
42
|
+
/**
|
|
43
|
+
* `\0`-separated so a dotted identifier cannot collide two tables into one
|
|
44
|
+
* map slot (the identity itself may contain any character; it comes last).
|
|
45
|
+
*/
|
|
46
|
+
private rowKey;
|
|
47
|
+
private row;
|
|
48
|
+
/**
|
|
49
|
+
* The cell version this transaction has staged for `(pk, column)`:
|
|
50
|
+
* the staged version when it staged one; `null` when it staged the cell's
|
|
51
|
+
* removal (a row delete not followed by a re-stage of this column);
|
|
52
|
+
* `undefined` when it staged nothing — fall back to committed storage.
|
|
53
|
+
*/
|
|
54
|
+
columnVersion(schema: string, table: string, pk: SqlValue[], column: string): ColumnVersionData | null | undefined;
|
|
55
|
+
noteColumnVersion(schema: string, table: string, pk: SqlValue[], column: string, version: ColumnVersionData): void;
|
|
56
|
+
/**
|
|
57
|
+
* The HLC of the tombstone this transaction has staged for the row, or
|
|
58
|
+
* `undefined` when it staged none — fall back to committed storage. (Local
|
|
59
|
+
* capture never removes a tombstone, so there is no staged-as-deleted state.)
|
|
60
|
+
*/
|
|
61
|
+
tombstoneHlc(schema: string, table: string, pk: SqlValue[]): HLC | undefined;
|
|
62
|
+
noteTombstone(schema: string, table: string, pk: SqlValue[], hlc: HLC): void;
|
|
63
|
+
/**
|
|
64
|
+
* The row's staged state for the delete cleanup, or `undefined` when this
|
|
65
|
+
* transaction has staged nothing for the row.
|
|
66
|
+
*/
|
|
67
|
+
rowState(schema: string, table: string, pk: SqlValue[]): StagedRowState | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Record that removal of ALL the row's cell records — committed and staged —
|
|
70
|
+
* has been staged (a row delete's cleanup). Later staged cells re-populate
|
|
71
|
+
* the live set; until then every column of the row reads as deleted.
|
|
72
|
+
*/
|
|
73
|
+
noteRowCleared(schema: string, table: string, pk: SqlValue[]): void;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=staged-transaction-metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staged-transaction-metadata.d.ts","sourceRoot":"","sources":["../../../src/sync/staged-transaction-metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAoB,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEtE;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjD;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC7B;AAQD,qBAAa,yBAAyB;IAQzB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAFxC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgC;gBAExB,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,QAAQ;IAErF;;;OAGG;IACH,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,GAAG;IAUX;;;;;OAKG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,GAAG,SAAS;IAQlH,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAIlH;;;;OAIG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,GAAG,GAAG,SAAS;IAI5E,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI;IAI5E;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,cAAc,GAAG,SAAS;IAQnF;;;;OAIG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,IAAI;CAKnE"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-transaction staged-metadata overlay for local change capture.
|
|
3
|
+
*
|
|
4
|
+
* `handleTransactionCommit` records one committed engine transaction into a
|
|
5
|
+
* single `WriteBatch`, so every read it performs against committed storage is
|
|
6
|
+
* blind to what the same transaction has already staged. This overlay is the
|
|
7
|
+
* read-your-own-writes companion to that batch: each cell version, tombstone,
|
|
8
|
+
* and row cleanup the transaction stages is noted here, and the capture path
|
|
9
|
+
* consults the overlay BEFORE committed storage. The transaction's event order
|
|
10
|
+
* — which is commit order, the engine's own authority on the row's final state
|
|
11
|
+
* — then decides the recorded metadata, matching what the same statements
|
|
12
|
+
* produce as separate transactions.
|
|
13
|
+
*
|
|
14
|
+
* Rows are keyed by pk IDENTITY (the same {@link encodePkIdentity} the
|
|
15
|
+
* `cv:`/`tb:`/`cl:` storage keys use), so two pk spellings collapse here iff
|
|
16
|
+
* they collide on disk. One instance lives for exactly one
|
|
17
|
+
* `handleTransactionCommit` call.
|
|
18
|
+
*/
|
|
19
|
+
import { encodePkIdentity } from '../metadata/keys.js';
|
|
20
|
+
export class StagedTransactionMetadata {
|
|
21
|
+
getPkKeying;
|
|
22
|
+
// NOTE: retains one ColumnVersionData — value AND before-image — per staged cell
|
|
23
|
+
// for the life of the transaction, roughly doubling capture's peak footprint (the
|
|
24
|
+
// `changes[]` array already holds one entry per fact). If very large transactions
|
|
25
|
+
// ever pressure memory, note that only `columnVersion()`'s before-image chaining
|
|
26
|
+
// needs the values; the delete cleanup needs nothing but the HLC.
|
|
27
|
+
rows = new Map();
|
|
28
|
+
constructor(getPkKeying) {
|
|
29
|
+
this.getPkKeying = getPkKeying;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* `\0`-separated so a dotted identifier cannot collide two tables into one
|
|
33
|
+
* map slot (the identity itself may contain any character; it comes last).
|
|
34
|
+
*/
|
|
35
|
+
rowKey(schema, table, pk) {
|
|
36
|
+
return `${schema}\0${table}\0${encodePkIdentity(pk, this.getPkKeying(schema, table))}`;
|
|
37
|
+
}
|
|
38
|
+
row(schema, table, pk) {
|
|
39
|
+
const key = this.rowKey(schema, table, pk);
|
|
40
|
+
let row = this.rows.get(key);
|
|
41
|
+
if (!row) {
|
|
42
|
+
row = { cleared: false, columns: new Map() };
|
|
43
|
+
this.rows.set(key, row);
|
|
44
|
+
}
|
|
45
|
+
return row;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The cell version this transaction has staged for `(pk, column)`:
|
|
49
|
+
* the staged version when it staged one; `null` when it staged the cell's
|
|
50
|
+
* removal (a row delete not followed by a re-stage of this column);
|
|
51
|
+
* `undefined` when it staged nothing — fall back to committed storage.
|
|
52
|
+
*/
|
|
53
|
+
columnVersion(schema, table, pk, column) {
|
|
54
|
+
const row = this.rows.get(this.rowKey(schema, table, pk));
|
|
55
|
+
if (!row)
|
|
56
|
+
return undefined;
|
|
57
|
+
const staged = row.columns.get(column);
|
|
58
|
+
if (staged)
|
|
59
|
+
return staged;
|
|
60
|
+
return row.cleared ? null : undefined;
|
|
61
|
+
}
|
|
62
|
+
noteColumnVersion(schema, table, pk, column, version) {
|
|
63
|
+
this.row(schema, table, pk).columns.set(column, version);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The HLC of the tombstone this transaction has staged for the row, or
|
|
67
|
+
* `undefined` when it staged none — fall back to committed storage. (Local
|
|
68
|
+
* capture never removes a tombstone, so there is no staged-as-deleted state.)
|
|
69
|
+
*/
|
|
70
|
+
tombstoneHlc(schema, table, pk) {
|
|
71
|
+
return this.rows.get(this.rowKey(schema, table, pk))?.tombstoneHlc;
|
|
72
|
+
}
|
|
73
|
+
noteTombstone(schema, table, pk, hlc) {
|
|
74
|
+
this.row(schema, table, pk).tombstoneHlc = hlc;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The row's staged state for the delete cleanup, or `undefined` when this
|
|
78
|
+
* transaction has staged nothing for the row.
|
|
79
|
+
*/
|
|
80
|
+
rowState(schema, table, pk) {
|
|
81
|
+
const row = this.rows.get(this.rowKey(schema, table, pk));
|
|
82
|
+
if (!row)
|
|
83
|
+
return undefined;
|
|
84
|
+
const stagedColumns = new Map();
|
|
85
|
+
for (const [column, version] of row.columns)
|
|
86
|
+
stagedColumns.set(column, version.hlc);
|
|
87
|
+
return { stagedColumns, rowCleared: row.cleared };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Record that removal of ALL the row's cell records — committed and staged —
|
|
91
|
+
* has been staged (a row delete's cleanup). Later staged cells re-populate
|
|
92
|
+
* the live set; until then every column of the row reads as deleted.
|
|
93
|
+
*/
|
|
94
|
+
noteRowCleared(schema, table, pk) {
|
|
95
|
+
const row = this.row(schema, table, pk);
|
|
96
|
+
row.cleared = true;
|
|
97
|
+
row.columns.clear();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=staged-transaction-metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staged-transaction-metadata.js","sourceRoot":"","sources":["../../../src/sync/staged-transaction-metadata.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,11 +10,17 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Per `applyToStore(dataChanges, schemaChanges, options)` invocation:
|
|
12
12
|
* 1. Schema changes execute first via `db.exec` (DDL before DML), with the
|
|
13
|
-
* resulting module schema events pre-marked remote.
|
|
13
|
+
* resulting module schema events pre-marked remote. Replicated DDL is
|
|
14
|
+
* applied IDEMPOTENTLY: an object already in the migration's wanted state
|
|
15
|
+
* with a matching definition is counted applied without re-exec'ing, so two
|
|
16
|
+
* peers that independently created the same table converge instead of the
|
|
17
|
+
* receiver failing "already exists" on every sync (see
|
|
18
|
+
* {@link decideSchemaChange}).
|
|
14
19
|
* 2. Data changes are grouped per table, then per row; each row group
|
|
15
|
-
* collapses to ONE `ExternalRowOp`
|
|
16
|
-
*
|
|
17
|
-
*
|
|
20
|
+
* collapses to ONE `ExternalRowOp` — the group's NET effect in arrival
|
|
21
|
+
* order (a delete resets the row to absent and later column updates
|
|
22
|
+
* rebuild it from PK+nulls; updates with no later delete merge onto the
|
|
23
|
+
* pre-read existing row — UPSERT semantics; see {@link buildRowOp}).
|
|
18
24
|
* 3. `StoreTable.applyExternalRowChanges(ops)` applies the table's ops to
|
|
19
25
|
* committed storage and returns the EFFECTIVE changes (no-ops — absent
|
|
20
26
|
* delete, value-identical upsert — are suppressed: no storage write, no
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-adapter.d.ts","sourceRoot":"","sources":["../../../src/sync/store-adapter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"store-adapter.d.ts","sourceRoot":"","sources":["../../../src/sync/store-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyGG;AAEH,OAAO,KAAK,EAAoB,QAAQ,EAAmE,MAAM,kBAAkB,CAAC;AAEpI,OAAO,KAAK,EAAoC,iBAAiB,EAAE,WAAW,EAAc,MAAM,gBAAgB,CAAC;AAEnH,OAAO,KAAK,EACV,oBAAoB,EAKrB,MAAM,eAAe,CAAC;AAGvB;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,kFAAkF;IAClF,EAAE,EAAE,QAAQ,CAAC;IACb;;;;;OAKG;IACH,WAAW,EAAE,WAAW,CAAC;IACzB,mFAAmF;IACnF,MAAM,EAAE,iBAAiB,CAAC;IAC1B;;;;;;;;OAQG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,uBAAuB,GAAG,oBAAoB,CA+GzF"}
|