@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
|
@@ -7,48 +7,64 @@
|
|
|
7
7
|
import type { Row, SqlValue } from '@quereus/quereus';
|
|
8
8
|
import type { KVStore, WriteBatch } from '@quereus/store';
|
|
9
9
|
import { type HLC } from '../clock/hlc.js';
|
|
10
|
+
import type { PkKeyingResolver } from './pk-identity.js';
|
|
10
11
|
/**
|
|
11
12
|
* Tombstone record.
|
|
12
13
|
*/
|
|
13
14
|
export interface Tombstone {
|
|
14
15
|
hlc: HLC;
|
|
15
16
|
createdAt: number;
|
|
17
|
+
/**
|
|
18
|
+
* The deleted row's ADDRESS — its raw primary-key values, one spelling from
|
|
19
|
+
* the row's equivalence class. UNCONDITIONAL (unlike `priorRow`): the record's
|
|
20
|
+
* key holds only the lossy pk identity, so the value is the only place the
|
|
21
|
+
* wire-transportable pk can come from.
|
|
22
|
+
*/
|
|
23
|
+
pk: SqlValue[];
|
|
16
24
|
/**
|
|
17
25
|
* Optional last-known row image before deletion (the engine's `oldRow` at delete
|
|
18
26
|
* time). Persisted so it reaches a receiver via `getChangesSince`, which
|
|
19
27
|
* re-resolves deletions from the tombstone. Best-effort audit/undo metadata:
|
|
20
28
|
* absent when the delete carried no `oldRow` and on snapshot-reconstructed
|
|
21
|
-
* tombstones.
|
|
29
|
+
* tombstones.
|
|
22
30
|
*/
|
|
23
31
|
priorRow?: Row;
|
|
24
32
|
}
|
|
25
33
|
/**
|
|
26
34
|
* Serialize a tombstone for storage.
|
|
27
35
|
*
|
|
28
|
-
* Format: 30 bytes HLC + 8 bytes createdAt
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* A tombstone with no `priorRow` serializes to the unchanged 38-byte head.
|
|
36
|
+
* Format: 30 bytes HLC + 8 bytes createdAt + JSON payload `{ k, pr? }`
|
|
37
|
+
* (see {@link SerializedTombstonePayload}). The payload is unconditional — the
|
|
38
|
+
* pk must always be recoverable from the value.
|
|
32
39
|
*/
|
|
33
40
|
export declare function serializeTombstone(tombstone: Tombstone): Uint8Array;
|
|
34
41
|
/**
|
|
35
|
-
* Deserialize a tombstone from storage.
|
|
36
|
-
*
|
|
37
|
-
* head-only buffer (no before-image, or a snapshot-reconstructed tombstone)
|
|
38
|
-
* deserializes with `priorRow` absent.
|
|
42
|
+
* Deserialize a tombstone from storage. The payload's `pr` (before-image) is
|
|
43
|
+
* optional; the pk is required (see {@link serializeTombstone}).
|
|
39
44
|
*/
|
|
40
45
|
export declare function deserializeTombstone(buffer: Uint8Array): Tombstone;
|
|
41
46
|
/**
|
|
42
47
|
* Tombstone store operations.
|
|
48
|
+
*
|
|
49
|
+
* Every pk-taking method derives the record key's pk IDENTITY through the
|
|
50
|
+
* per-table {@link PkKeyingResolver} passed at construction, so callers keep
|
|
51
|
+
* addressing rows by raw pk while the storage keys collapse spellings.
|
|
43
52
|
*/
|
|
44
53
|
export declare class TombstoneStore {
|
|
45
54
|
private readonly kv;
|
|
46
55
|
private readonly retentionHorizonMs;
|
|
47
|
-
|
|
56
|
+
private readonly keying;
|
|
57
|
+
constructor(kv: KVStore, retentionHorizonMs: number, keying: PkKeyingResolver);
|
|
58
|
+
private identity;
|
|
48
59
|
/**
|
|
49
60
|
* Get the tombstone for a row, if it exists.
|
|
50
61
|
*/
|
|
51
62
|
getTombstone(schemaName: string, tableName: string, pk: SqlValue[]): Promise<Tombstone | undefined>;
|
|
63
|
+
/**
|
|
64
|
+
* Get the tombstone for a row by pk IDENTITY (as recovered from a parsed
|
|
65
|
+
* `tb:`/`cl:` key — no raw pk needed).
|
|
66
|
+
*/
|
|
67
|
+
getTombstoneByIdentity(schemaName: string, tableName: string, identity: string): Promise<Tombstone | undefined>;
|
|
52
68
|
/**
|
|
53
69
|
* Create a tombstone for a deleted row. `priorRow` (optional) is the row's
|
|
54
70
|
* last-known image, persisted as best-effort audit/undo metadata.
|
|
@@ -56,11 +72,19 @@ export declare class TombstoneStore {
|
|
|
56
72
|
setTombstone(schemaName: string, tableName: string, pk: SqlValue[], hlc: HLC, priorRow?: Row): Promise<void>;
|
|
57
73
|
/**
|
|
58
74
|
* Set tombstone in a batch. `priorRow` (optional) is the row's last-known image,
|
|
59
|
-
* persisted as best-effort audit/undo metadata.
|
|
75
|
+
* persisted as best-effort audit/undo metadata. The key's pk IDENTITY is always
|
|
76
|
+
* derived locally — no caller-supplied-identity variant exists (see
|
|
77
|
+
* `ColumnVersionStore.setColumnVersionBatch`).
|
|
60
78
|
*/
|
|
61
79
|
setTombstoneBatch(batch: WriteBatch, schemaName: string, tableName: string, pk: SqlValue[], hlc: HLC, priorRow?: Row): void;
|
|
62
80
|
/**
|
|
63
81
|
* Delete a tombstone (used when resurrecting a row).
|
|
82
|
+
*
|
|
83
|
+
* NOTE: no production caller. If one is added, it must ALSO delete the paired
|
|
84
|
+
* `cl:` change-log delete entry (`changeLog.deleteEntryBatch(batch, tombstone.hlc,
|
|
85
|
+
* 'delete', schema, table, pk)`) in the same batch — otherwise the entry outlives
|
|
86
|
+
* its target and becomes permanent index garbage, the leak `SyncManagerImpl.
|
|
87
|
+
* pruneTombstones` was fixed to avoid.
|
|
64
88
|
*/
|
|
65
89
|
deleteTombstone(schemaName: string, tableName: string, pk: SqlValue[]): Promise<void>;
|
|
66
90
|
/**
|
|
@@ -71,6 +95,12 @@ export declare class TombstoneStore {
|
|
|
71
95
|
/**
|
|
72
96
|
* Prune expired tombstones.
|
|
73
97
|
* Returns the number of tombstones deleted.
|
|
98
|
+
*
|
|
99
|
+
* NOTE: no production caller — `SyncManagerImpl.pruneTombstones` is the wired
|
|
100
|
+
* all-tables equivalent. If this per-table variant is wired up, it needs the same
|
|
101
|
+
* paired `cl:` change-log delete-entry removal that one does (see
|
|
102
|
+
* {@link deleteTombstone}); this store has no `ChangeLogStore` handle, so that
|
|
103
|
+
* would mean threading one in or moving the sweep up a level.
|
|
74
104
|
*/
|
|
75
105
|
pruneExpired(schemaName: string, tableName: string): Promise<number>;
|
|
76
106
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tombstones.d.ts","sourceRoot":"","sources":["../../../src/metadata/tombstones.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,KAAK,GAAG,EAA4C,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"tombstones.d.ts","sourceRoot":"","sources":["../../../src/metadata/tombstones.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,KAAK,GAAG,EAA4C,MAAM,iBAAiB,CAAC;AAGrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAKzD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,GAAG,CAAC;IACT,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,EAAE,EAAE,QAAQ,EAAE,CAAC;IACf;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,GAAG,CAAC;CAChB;AAYD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,UAAU,CAiBnE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAalE;AAED;;;;;;GAMG;AACH,qBAAa,cAAc;IAEvB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAFN,EAAE,EAAE,OAAO,EACX,kBAAkB,EAAE,MAAM,EAC1B,MAAM,EAAE,gBAAgB;IAG3C,OAAO,CAAC,QAAQ;IAIhB;;OAEG;IACG,YAAY,CAChB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,QAAQ,EAAE,GACb,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAIjC;;;OAGG;IACG,sBAAsB,CAC1B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAOjC;;;OAGG;IACG,YAAY,CAChB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,QAAQ,EAAE,EACd,GAAG,EAAE,GAAG,EACR,QAAQ,CAAC,EAAE,GAAG,GACb,OAAO,CAAC,IAAI,CAAC;IAMhB;;;;;OAKG;IACH,iBAAiB,CACf,KAAK,EAAE,UAAU,EACjB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,QAAQ,EAAE,EACd,GAAG,EAAE,GAAG,EACR,QAAQ,CAAC,EAAE,GAAG,GACb,IAAI;IAMP;;;;;;;;OAQG;IACG,eAAe,CACnB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,QAAQ,EAAE,GACb,OAAO,CAAC,IAAI,CAAC;IAKhB;;;OAGG;IACG,oBAAoB,CACxB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,QAAQ,EAAE,EACd,WAAW,EAAE,GAAG,EAChB,iBAAiB,EAAE,OAAO,GACzB,OAAO,CAAC,OAAO,CAAC;IAanB;;;;;;;;;OASG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkB1E;;OAEG;IACI,gBAAgB,CACrB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,aAAa,CAAC;QAAE,EAAE,EAAE,QAAQ,EAAE,CAAC;QAAC,SAAS,EAAE,SAAS,CAAA;KAAE,CAAC;CAS3D"}
|
|
@@ -6,16 +6,15 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { serializeHLC, deserializeHLC, compareHLC } from '../clock/hlc.js';
|
|
8
8
|
import { encodeSqlValue, decodeSqlValue } from './column-version.js';
|
|
9
|
-
import { buildTombstoneKey, buildTombstoneScanBounds,
|
|
9
|
+
import { buildTombstoneKey, buildTombstoneScanBounds, encodePkIdentity } from './keys.js';
|
|
10
10
|
/** Fixed-size head of a serialized tombstone: 30 bytes HLC + 8 bytes createdAt. */
|
|
11
11
|
const TOMBSTONE_HEAD_BYTES = 38;
|
|
12
12
|
/**
|
|
13
13
|
* Serialize a tombstone for storage.
|
|
14
14
|
*
|
|
15
|
-
* Format: 30 bytes HLC + 8 bytes createdAt
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* A tombstone with no `priorRow` serializes to the unchanged 38-byte head.
|
|
15
|
+
* Format: 30 bytes HLC + 8 bytes createdAt + JSON payload `{ k, pr? }`
|
|
16
|
+
* (see {@link SerializedTombstonePayload}). The payload is unconditional — the
|
|
17
|
+
* pk must always be recoverable from the value.
|
|
19
18
|
*/
|
|
20
19
|
export function serializeTombstone(tombstone) {
|
|
21
20
|
const head = new Uint8Array(TOMBSTONE_HEAD_BYTES);
|
|
@@ -23,46 +22,62 @@ export function serializeTombstone(tombstone) {
|
|
|
23
22
|
head.set(hlcBytes, 0);
|
|
24
23
|
const view = new DataView(head.buffer);
|
|
25
24
|
view.setBigUint64(30, BigInt(tombstone.createdAt), false);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
const payload = {
|
|
26
|
+
k: tombstone.pk.map(encodeSqlValue),
|
|
27
|
+
...(tombstone.priorRow !== undefined ? { pr: tombstone.priorRow.map(encodeSqlValue) } : {}),
|
|
28
|
+
};
|
|
29
|
+
const payloadBytes = new TextEncoder().encode(JSON.stringify(payload));
|
|
30
|
+
const result = new Uint8Array(head.length + payloadBytes.length);
|
|
30
31
|
result.set(head, 0);
|
|
31
|
-
result.set(
|
|
32
|
+
result.set(payloadBytes, head.length);
|
|
32
33
|
return result;
|
|
33
34
|
}
|
|
34
35
|
/**
|
|
35
|
-
* Deserialize a tombstone from storage.
|
|
36
|
-
*
|
|
37
|
-
* head-only buffer (no before-image, or a snapshot-reconstructed tombstone)
|
|
38
|
-
* deserializes with `priorRow` absent.
|
|
36
|
+
* Deserialize a tombstone from storage. The payload's `pr` (before-image) is
|
|
37
|
+
* optional; the pk is required (see {@link serializeTombstone}).
|
|
39
38
|
*/
|
|
40
39
|
export function deserializeTombstone(buffer) {
|
|
41
40
|
const hlc = deserializeHLC(buffer.slice(0, 30));
|
|
42
41
|
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
43
42
|
const createdAt = Number(view.getBigUint64(30, false));
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
tombstone.priorRow =
|
|
43
|
+
const payload = JSON.parse(new TextDecoder().decode(buffer.slice(TOMBSTONE_HEAD_BYTES)));
|
|
44
|
+
const tombstone = { hlc, createdAt, pk: payload.k.map(decodeSqlValue) };
|
|
45
|
+
if (payload.pr !== undefined) {
|
|
46
|
+
tombstone.priorRow = payload.pr.map(decodeSqlValue);
|
|
48
47
|
}
|
|
49
48
|
return tombstone;
|
|
50
49
|
}
|
|
51
50
|
/**
|
|
52
51
|
* Tombstone store operations.
|
|
52
|
+
*
|
|
53
|
+
* Every pk-taking method derives the record key's pk IDENTITY through the
|
|
54
|
+
* per-table {@link PkKeyingResolver} passed at construction, so callers keep
|
|
55
|
+
* addressing rows by raw pk while the storage keys collapse spellings.
|
|
53
56
|
*/
|
|
54
57
|
export class TombstoneStore {
|
|
55
58
|
kv;
|
|
56
59
|
retentionHorizonMs;
|
|
57
|
-
|
|
60
|
+
keying;
|
|
61
|
+
constructor(kv, retentionHorizonMs, keying) {
|
|
58
62
|
this.kv = kv;
|
|
59
63
|
this.retentionHorizonMs = retentionHorizonMs;
|
|
64
|
+
this.keying = keying;
|
|
65
|
+
}
|
|
66
|
+
identity(schemaName, tableName, pk) {
|
|
67
|
+
return encodePkIdentity(pk, this.keying(schemaName, tableName));
|
|
60
68
|
}
|
|
61
69
|
/**
|
|
62
70
|
* Get the tombstone for a row, if it exists.
|
|
63
71
|
*/
|
|
64
72
|
async getTombstone(schemaName, tableName, pk) {
|
|
65
|
-
|
|
73
|
+
return this.getTombstoneByIdentity(schemaName, tableName, this.identity(schemaName, tableName, pk));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get the tombstone for a row by pk IDENTITY (as recovered from a parsed
|
|
77
|
+
* `tb:`/`cl:` key — no raw pk needed).
|
|
78
|
+
*/
|
|
79
|
+
async getTombstoneByIdentity(schemaName, tableName, identity) {
|
|
80
|
+
const key = buildTombstoneKey(schemaName, tableName, identity);
|
|
66
81
|
const data = await this.kv.get(key);
|
|
67
82
|
if (!data)
|
|
68
83
|
return undefined;
|
|
@@ -73,24 +88,32 @@ export class TombstoneStore {
|
|
|
73
88
|
* last-known image, persisted as best-effort audit/undo metadata.
|
|
74
89
|
*/
|
|
75
90
|
async setTombstone(schemaName, tableName, pk, hlc, priorRow) {
|
|
76
|
-
const key = buildTombstoneKey(schemaName, tableName, pk);
|
|
77
|
-
const tombstone = { hlc, createdAt: Date.now(), ...(priorRow !== undefined ? { priorRow } : {}) };
|
|
91
|
+
const key = buildTombstoneKey(schemaName, tableName, this.identity(schemaName, tableName, pk));
|
|
92
|
+
const tombstone = { hlc, createdAt: Date.now(), pk, ...(priorRow !== undefined ? { priorRow } : {}) };
|
|
78
93
|
await this.kv.put(key, serializeTombstone(tombstone));
|
|
79
94
|
}
|
|
80
95
|
/**
|
|
81
96
|
* Set tombstone in a batch. `priorRow` (optional) is the row's last-known image,
|
|
82
|
-
* persisted as best-effort audit/undo metadata.
|
|
97
|
+
* persisted as best-effort audit/undo metadata. The key's pk IDENTITY is always
|
|
98
|
+
* derived locally — no caller-supplied-identity variant exists (see
|
|
99
|
+
* `ColumnVersionStore.setColumnVersionBatch`).
|
|
83
100
|
*/
|
|
84
101
|
setTombstoneBatch(batch, schemaName, tableName, pk, hlc, priorRow) {
|
|
85
|
-
const key = buildTombstoneKey(schemaName, tableName, pk);
|
|
86
|
-
const tombstone = { hlc, createdAt: Date.now(), ...(priorRow !== undefined ? { priorRow } : {}) };
|
|
102
|
+
const key = buildTombstoneKey(schemaName, tableName, this.identity(schemaName, tableName, pk));
|
|
103
|
+
const tombstone = { hlc, createdAt: Date.now(), pk, ...(priorRow !== undefined ? { priorRow } : {}) };
|
|
87
104
|
batch.put(key, serializeTombstone(tombstone));
|
|
88
105
|
}
|
|
89
106
|
/**
|
|
90
107
|
* Delete a tombstone (used when resurrecting a row).
|
|
108
|
+
*
|
|
109
|
+
* NOTE: no production caller. If one is added, it must ALSO delete the paired
|
|
110
|
+
* `cl:` change-log delete entry (`changeLog.deleteEntryBatch(batch, tombstone.hlc,
|
|
111
|
+
* 'delete', schema, table, pk)`) in the same batch — otherwise the entry outlives
|
|
112
|
+
* its target and becomes permanent index garbage, the leak `SyncManagerImpl.
|
|
113
|
+
* pruneTombstones` was fixed to avoid.
|
|
91
114
|
*/
|
|
92
115
|
async deleteTombstone(schemaName, tableName, pk) {
|
|
93
|
-
const key = buildTombstoneKey(schemaName, tableName, pk);
|
|
116
|
+
const key = buildTombstoneKey(schemaName, tableName, this.identity(schemaName, tableName, pk));
|
|
94
117
|
await this.kv.delete(key);
|
|
95
118
|
}
|
|
96
119
|
/**
|
|
@@ -113,6 +136,12 @@ export class TombstoneStore {
|
|
|
113
136
|
/**
|
|
114
137
|
* Prune expired tombstones.
|
|
115
138
|
* Returns the number of tombstones deleted.
|
|
139
|
+
*
|
|
140
|
+
* NOTE: no production caller — `SyncManagerImpl.pruneTombstones` is the wired
|
|
141
|
+
* all-tables equivalent. If this per-table variant is wired up, it needs the same
|
|
142
|
+
* paired `cl:` change-log delete-entry removal that one does (see
|
|
143
|
+
* {@link deleteTombstone}); this store has no `ChangeLogStore` handle, so that
|
|
144
|
+
* would mean threading one in or moving the sweep up a level.
|
|
116
145
|
*/
|
|
117
146
|
async pruneExpired(schemaName, tableName) {
|
|
118
147
|
const bounds = buildTombstoneScanBounds(schemaName, tableName);
|
|
@@ -135,13 +164,9 @@ export class TombstoneStore {
|
|
|
135
164
|
async *getAllTombstones(schemaName, tableName) {
|
|
136
165
|
const bounds = buildTombstoneScanBounds(schemaName, tableName);
|
|
137
166
|
for await (const entry of this.kv.iterate(bounds)) {
|
|
138
|
-
//
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
const secondColon = keyStr.indexOf(':', firstColon + 1);
|
|
142
|
-
const pkJson = keyStr.slice(secondColon + 1);
|
|
143
|
-
const pk = decodePK(pkJson);
|
|
144
|
-
yield { pk, tombstone: deserializeTombstone(entry.value) };
|
|
167
|
+
// The key holds only the lossy pk identity; the raw pk comes from the value.
|
|
168
|
+
const tombstone = deserializeTombstone(entry.value);
|
|
169
|
+
yield { pk: tombstone.pk, tombstone };
|
|
145
170
|
}
|
|
146
171
|
}
|
|
147
172
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tombstones.js","sourceRoot":"","sources":["../../../src/metadata/tombstones.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAY,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,
|
|
1
|
+
{"version":3,"file":"tombstones.js","sourceRoot":"","sources":["../../../src/metadata/tombstones.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,6 +22,25 @@ export interface ResolvedChange {
|
|
|
22
22
|
oldColumnVersion?: ColumnVersion;
|
|
23
23
|
/** For delete changes: the prior tombstone whose stale delete entry to clean up in the change log. */
|
|
24
24
|
oldTombstone?: Tombstone;
|
|
25
|
+
/**
|
|
26
|
+
* For a row's winning in-batch delete under `allowResurrection`: columns of this
|
|
27
|
+
* row whose same-batch write beat the delete, so the post-commit cleanup keeps
|
|
28
|
+
* them. Filled by {@link reconcileInBatchDeletes}.
|
|
29
|
+
*/
|
|
30
|
+
keepColumns?: Set<string>;
|
|
31
|
+
/**
|
|
32
|
+
* For a column change that BEAT a same-batch delete under `allowResurrection`:
|
|
33
|
+
* the delete erased this cell's local lineage before the write landed, so the
|
|
34
|
+
* write records no before-image. {@link oldColumnVersion} still travels with it
|
|
35
|
+
* — its stale `cl:` entry must be dropped, and the delete's cleanup now skips
|
|
36
|
+
* this column. Filled by {@link reconcileInBatchDeletes}.
|
|
37
|
+
*/
|
|
38
|
+
priorErasedByInBatchDelete?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Table exists only via this same batch's create_table — no local schema (and so
|
|
41
|
+
* no resolvable pk-identity keying) until Phase 2's DDL runs.
|
|
42
|
+
*/
|
|
43
|
+
freshLocalTable?: boolean;
|
|
25
44
|
}
|
|
26
45
|
/**
|
|
27
46
|
* Apply change sets from a remote peer.
|
|
@@ -84,8 +103,13 @@ export declare function drainReappearedTables(ctx: SyncContext, tables: Readonly
|
|
|
84
103
|
* Resolve CRDT conflicts for a single change WITHOUT writing metadata.
|
|
85
104
|
*
|
|
86
105
|
* Phase 1 of the 3-phase apply pattern.
|
|
106
|
+
*
|
|
107
|
+
* `freshLocalTable` marks a table that exists only via the SAME batch's
|
|
108
|
+
* create_table: it has no local schema yet (so no pk-identity keying) and no
|
|
109
|
+
* local metadata, so every read is skipped and the change resolves as a first
|
|
110
|
+
* write. Phase 3 commits its metadata after the DDL has executed.
|
|
87
111
|
*/
|
|
88
|
-
export declare function resolveChange(ctx: SyncContext, change: Change): Promise<ResolvedChange>;
|
|
112
|
+
export declare function resolveChange(ctx: SyncContext, change: Change, freshLocalTable?: boolean): Promise<ResolvedChange>;
|
|
89
113
|
/**
|
|
90
114
|
* Commit CRDT metadata for resolved changes.
|
|
91
115
|
*
|
|
@@ -102,6 +126,11 @@ export declare function resolveChange(ctx: SyncContext, change: Change): Promise
|
|
|
102
126
|
* pre-batch prior entry is deleted once. Mirrors the local write-path dedup in
|
|
103
127
|
* `recordDataEvent` / `recordColumnVersions`, keeping the delete and column paths
|
|
104
128
|
* symmetric.
|
|
129
|
+
*
|
|
130
|
+
* Cross-type (delete vs column) same-row collisions were already settled upstream by
|
|
131
|
+
* {@link reconcileInBatchDeletes}: every column change still `'applied'` here is one
|
|
132
|
+
* that must be written, and a winning delete's {@link ResolvedChange.keepColumns}
|
|
133
|
+
* names the columns its cleanup must leave alone.
|
|
105
134
|
*/
|
|
106
135
|
export declare function commitChangeMetadata(ctx: SyncContext, resolvedChanges: ResolvedChange[]): Promise<void>;
|
|
107
136
|
//# sourceMappingURL=change-applicator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-applicator.d.ts","sourceRoot":"","sources":["../../../src/sync/change-applicator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAE3D,OAAO,KAAK,EACX,SAAS,EACT,MAAM,EAGN,WAAW,EACX,iBAAiB,EAGjB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,aAAa,CAAC;IACjC,sGAAsG;IACtG,YAAY,CAAC,EAAE,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"change-applicator.d.ts","sourceRoot":"","sources":["../../../src/sync/change-applicator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAE3D,OAAO,KAAK,EACX,SAAS,EACT,MAAM,EAGN,WAAW,EACX,iBAAiB,EAGjB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,aAAa,CAAC;IACjC,sGAAsG;IACtG,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;;;OAIG;IACH,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B;;;;;;OAMG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B;AA2CD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,YAAY,CACjC,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,SAAS,EAAE,GAClB,OAAO,CAAC,WAAW,CAAC,CAiPtB;AAsCD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,gBAAgB,CACrC,GAAG,EAAE,WAAW,EAChB,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAsBjB;AA6FD;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CAC1C,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,aAAa,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,GACtD,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAClC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,MAAM,EACd,eAAe,UAAQ,GACrB,OAAO,CAAC,cAAc,CAAC,CAkHzB;AAyDD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,oBAAoB,CACzC,GAAG,EAAE,WAAW,EAChB,eAAe,EAAE,cAAc,EAAE,GAC/B,OAAO,CAAC,IAAI,CAAC,CAiDf"}
|