@quereus/sync 4.5.0 → 4.7.0
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 +14 -6
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/metadata/column-version.d.ts.map +1 -1
- package/dist/src/metadata/column-version.js +8 -9
- package/dist/src/metadata/column-version.js.map +1 -1
- package/dist/src/metadata/keys.d.ts +99 -39
- package/dist/src/metadata/keys.d.ts.map +1 -1
- package/dist/src/metadata/keys.js +213 -158
- package/dist/src/metadata/keys.js.map +1 -1
- package/dist/src/metadata/pk-identity.d.ts +19 -14
- package/dist/src/metadata/pk-identity.d.ts.map +1 -1
- package/dist/src/metadata/pk-identity.js +21 -28
- package/dist/src/metadata/pk-identity.js.map +1 -1
- package/dist/src/metadata/schema-migration.d.ts +33 -10
- package/dist/src/metadata/schema-migration.d.ts.map +1 -1
- package/dist/src/metadata/schema-migration.js +77 -18
- package/dist/src/metadata/schema-migration.js.map +1 -1
- package/dist/src/sync/change-applicator.d.ts +19 -3
- package/dist/src/sync/change-applicator.d.ts.map +1 -1
- package/dist/src/sync/change-applicator.js +253 -63
- package/dist/src/sync/change-applicator.js.map +1 -1
- package/dist/src/sync/maintenance.d.ts +8 -5
- package/dist/src/sync/maintenance.d.ts.map +1 -1
- package/dist/src/sync/maintenance.js +7 -4
- package/dist/src/sync/maintenance.js.map +1 -1
- package/dist/src/sync/manager.d.ts +46 -1
- package/dist/src/sync/manager.d.ts.map +1 -1
- package/dist/src/sync/protocol.d.ts +42 -3
- package/dist/src/sync/protocol.d.ts.map +1 -1
- package/dist/src/sync/protocol.js +40 -0
- package/dist/src/sync/protocol.js.map +1 -1
- package/dist/src/sync/snapshot-identity.d.ts.map +1 -1
- package/dist/src/sync/snapshot-identity.js +1 -9
- package/dist/src/sync/snapshot-identity.js.map +1 -1
- package/dist/src/sync/snapshot-stream.d.ts +11 -0
- package/dist/src/sync/snapshot-stream.d.ts.map +1 -1
- package/dist/src/sync/snapshot-stream.js +134 -67
- package/dist/src/sync/snapshot-stream.js.map +1 -1
- package/dist/src/sync/snapshot.d.ts.map +1 -1
- package/dist/src/sync/snapshot.js +15 -30
- package/dist/src/sync/snapshot.js.map +1 -1
- package/dist/src/sync/store-adapter.d.ts +5 -4
- package/dist/src/sync/store-adapter.d.ts.map +1 -1
- package/dist/src/sync/store-adapter.js +328 -108
- package/dist/src/sync/store-adapter.js.map +1 -1
- package/dist/src/sync/sync-manager-impl.d.ts +37 -4
- package/dist/src/sync/sync-manager-impl.d.ts.map +1 -1
- package/dist/src/sync/sync-manager-impl.js +110 -56
- package/dist/src/sync/sync-manager-impl.js.map +1 -1
- package/dist/src/sync/wire.d.ts +6 -0
- package/dist/src/sync/wire.d.ts.map +1 -1
- package/dist/src/sync/wire.js +3 -0
- package/dist/src/sync/wire.js.map +1 -1
- package/package.json +7 -7
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
* The sync layer files every per-row record (`cv:`/`tb:`/`cl:`) under the row's
|
|
5
5
|
* IDENTITY — the same "same row?" answer the rest of the engine gives: each pk
|
|
6
6
|
* column normalized under its KEY COLLATION ('apple' ≡ 'APPLE' under nocase) and
|
|
7
|
-
* its logical type's semantic key transform ('PT1H' ≡ 'PT60M' for TIMESPAN).
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* its logical type's semantic key transform ('PT1H' ≡ 'PT60M' for TIMESPAN). This
|
|
8
|
+
* module resolves that keying from a table's schema by calling into
|
|
9
|
+
* `@quereus/quereus`'s `resolvePkIdentityKeying` — the ONE implementation of that
|
|
10
|
+
* recipe, also used by `makePkKeySerializer` in `@quereus/isolation` (the overlay's
|
|
11
|
+
* row-alignment key) — so the two layers can never disagree on row identity.
|
|
11
12
|
*
|
|
12
13
|
* Identity is derived and lossy; the row's ADDRESS (a real, type-valid
|
|
13
14
|
* `SqlValue[]` — any spelling from the equivalence class) lives in the record
|
|
@@ -22,16 +23,20 @@ import { type PkKeying } from './keys.js';
|
|
|
22
23
|
*/
|
|
23
24
|
export type PkKeyingResolver = (schemaName: string, tableName: string) => PkKeying;
|
|
24
25
|
/**
|
|
25
|
-
* Resolve a table's {@link PkKeying} from its schema
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
26
|
+
* Resolve a table's {@link PkKeying} from its schema. Delegates entirely to
|
|
27
|
+
* `@quereus/quereus`'s {@link resolvePkIdentityKeying} (per pk column: key collation
|
|
28
|
+
* via `pkKeyCollationName` — its own declared collation for a collation-aware column
|
|
29
|
+
* (`text`, `any`; for text the store reconciles an undecorated pk to the table key
|
|
30
|
+
* collation at CREATE, so the registered schema always carries it); BINARY for
|
|
31
|
+
* collation-blind text-capable columns (`json`, the temporal types); identity for
|
|
32
|
+
* never-text columns — plus the engine's `semanticKeyTransform`,
|
|
33
|
+
* the logical type's `groupKey`, today TIMESPAN → total seconds. Deliberately the
|
|
34
|
+
* ENGINE transform, not the store's byte-order variant: identity strings need
|
|
35
|
+
* equality, not memcmp order, so JSON's canonical text is already faithful), whose
|
|
36
|
+
* shape is exactly {@link PkKeying}'s. That shared function is also defensive about a
|
|
37
|
+
* missing `primaryKeyDefinition` or column `logicalType` (test oracles stub minimal
|
|
38
|
+
* schemas — `{ columns: [{ name }] }` only), degrading a missing piece to the identity
|
|
39
|
+
* normalizer (raw value identity) instead of crashing.
|
|
35
40
|
*/
|
|
36
41
|
export declare function resolvePkKeying(schema: TableSchema, resolver: KeyNormalizerResolver): PkKeying;
|
|
37
42
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pk-identity.d.ts","sourceRoot":"","sources":["../../../src/metadata/pk-identity.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pk-identity.d.ts","sourceRoot":"","sources":["../../../src/metadata/pk-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAEN,KAAK,qBAAqB,EAC1B,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAmC,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE3E;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,QAAQ,CAAC;AAEnF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,qBAAqB,GAAG,QAAQ,CAE9F;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CACrC,cAAc,EAAE,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,WAAW,GAAG,SAAS,CAAC,GAAG,SAAS,EAChG,qBAAqB,EAAE,qBAAqB,GAAG,SAAS,GACtD,gBAAgB,CAwBlB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACpC,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,qBAAqB,GAC7B,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,MAAM,CAG5B"}
|
|
@@ -4,44 +4,37 @@
|
|
|
4
4
|
* The sync layer files every per-row record (`cv:`/`tb:`/`cl:`) under the row's
|
|
5
5
|
* IDENTITY — the same "same row?" answer the rest of the engine gives: each pk
|
|
6
6
|
* column normalized under its KEY COLLATION ('apple' ≡ 'APPLE' under nocase) and
|
|
7
|
-
* its logical type's semantic key transform ('PT1H' ≡ 'PT60M' for TIMESPAN).
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* its logical type's semantic key transform ('PT1H' ≡ 'PT60M' for TIMESPAN). This
|
|
8
|
+
* module resolves that keying from a table's schema by calling into
|
|
9
|
+
* `@quereus/quereus`'s `resolvePkIdentityKeying` — the ONE implementation of that
|
|
10
|
+
* recipe, also used by `makePkKeySerializer` in `@quereus/isolation` (the overlay's
|
|
11
|
+
* row-alignment key) — so the two layers can never disagree on row identity.
|
|
11
12
|
*
|
|
12
13
|
* Identity is derived and lossy; the row's ADDRESS (a real, type-valid
|
|
13
14
|
* `SqlValue[]` — any spelling from the equivalence class) lives in the record
|
|
14
15
|
* value and on the wire. See docs/sync.md § Row identity vs. address.
|
|
15
16
|
*/
|
|
16
|
-
import {
|
|
17
|
+
import { resolvePkIdentityKeying, } from '@quereus/quereus';
|
|
17
18
|
import { BUILTIN_KEY_NORMALIZER_RESOLVER } from '@quereus/store';
|
|
18
19
|
import { encodePkIdentity, RAW_PK_KEYING } from './keys.js';
|
|
19
20
|
/**
|
|
20
|
-
* Resolve a table's {@link PkKeying} from its schema
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
21
|
+
* Resolve a table's {@link PkKeying} from its schema. Delegates entirely to
|
|
22
|
+
* `@quereus/quereus`'s {@link resolvePkIdentityKeying} (per pk column: key collation
|
|
23
|
+
* via `pkKeyCollationName` — its own declared collation for a collation-aware column
|
|
24
|
+
* (`text`, `any`; for text the store reconciles an undecorated pk to the table key
|
|
25
|
+
* collation at CREATE, so the registered schema always carries it); BINARY for
|
|
26
|
+
* collation-blind text-capable columns (`json`, the temporal types); identity for
|
|
27
|
+
* never-text columns — plus the engine's `semanticKeyTransform`,
|
|
28
|
+
* the logical type's `groupKey`, today TIMESPAN → total seconds. Deliberately the
|
|
29
|
+
* ENGINE transform, not the store's byte-order variant: identity strings need
|
|
30
|
+
* equality, not memcmp order, so JSON's canonical text is already faithful), whose
|
|
31
|
+
* shape is exactly {@link PkKeying}'s. That shared function is also defensive about a
|
|
32
|
+
* missing `primaryKeyDefinition` or column `logicalType` (test oracles stub minimal
|
|
33
|
+
* schemas — `{ columns: [{ name }] }` only), degrading a missing piece to the identity
|
|
34
|
+
* normalizer (raw value identity) instead of crashing.
|
|
30
35
|
*/
|
|
31
36
|
export function resolvePkKeying(schema, resolver) {
|
|
32
|
-
|
|
33
|
-
// primaryKeyDefinition and typed columns, but test oracles stub minimal
|
|
34
|
-
// schemas ({ columns: [{ name }] } only). A missing piece degrades that
|
|
35
|
-
// position to the identity normalizer (raw value identity) — the same
|
|
36
|
-
// treatment a keyless stub got before — instead of crashing.
|
|
37
|
-
const pkDef = schema.primaryKeyDefinition ?? [];
|
|
38
|
-
return {
|
|
39
|
-
normalizers: pkDef.map(def => {
|
|
40
|
-
const column = schema.columns[def.index];
|
|
41
|
-
return resolver(column?.logicalType ? pkKeyCollationName(column) : undefined);
|
|
42
|
-
}),
|
|
43
|
-
transforms: pkDef.map(def => semanticKeyTransform(schema.columns[def.index]?.logicalType)),
|
|
44
|
-
};
|
|
37
|
+
return resolvePkIdentityKeying(schema, resolver);
|
|
45
38
|
}
|
|
46
39
|
/**
|
|
47
40
|
* Build the {@link PkKeyingResolver} a `SyncManagerImpl` threads through its
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pk-identity.js","sourceRoot":"","sources":["../../../src/metadata/pk-identity.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pk-identity.js","sourceRoot":"","sources":["../../../src/metadata/pk-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EACN,uBAAuB,GAIvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,+BAA+B,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAiB,MAAM,WAAW,CAAC;AAS3E;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,eAAe,CAAC,MAAmB,EAAE,QAA+B;IACnF,OAAO,uBAAuB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,sBAAsB,CACrC,cAAgG,EAChG,qBAAwD;IAExD,IAAI,CAAC,cAAc,EAAE,CAAC;QACrB,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC;IAC5B,CAAC;IACD,8EAA8E;IAC9E,8EAA8E;IAC9E,gDAAgD;IAChD,MAAM,QAAQ,GAAG,qBAAqB,IAAI,+BAA+B,CAAC;IAC1E,MAAM,KAAK,GAAG,IAAI,OAAO,EAAyB,CAAC;IACnD,OAAO,CAAC,UAAU,EAAE,SAAS,EAAE,EAAE;QAChC,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACd,uBAAuB,UAAU,IAAI,SAAS,sCAAsC;kBACjF,kFAAkF,CACrF,CAAC;QACH,CAAC;QACD,IAAI,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,MAAM,GAAG,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAC3C,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3B,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACpC,MAAmB,EACnB,QAA+B;IAE/B,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACjD,OAAO,EAAE,CAAC,EAAE,CAAC,gBAAgB,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -6,19 +6,26 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { KVStore, WriteBatch } from '@quereus/store';
|
|
8
8
|
import { type HLC } from '../clock/hlc.js';
|
|
9
|
-
import type
|
|
9
|
+
import { type SchemaMigration, type SchemaMigrationType, type SchemaObjectKind } from '../sync/protocol.js';
|
|
10
10
|
/**
|
|
11
11
|
* Stored schema migration record.
|
|
12
12
|
*/
|
|
13
13
|
export interface StoredMigration {
|
|
14
14
|
type: SchemaMigrationType;
|
|
15
|
+
/** `rename_table` only: the table name before the rename. */
|
|
16
|
+
fromTable?: string;
|
|
15
17
|
ddl: string;
|
|
16
18
|
hlc: HLC;
|
|
17
19
|
schemaVersion: number;
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
20
22
|
* Serialize a migration for storage.
|
|
21
|
-
* Format: 30 bytes HLC + 4 bytes version + 1 byte type length + type
|
|
23
|
+
* Format: 30 bytes HLC + 4 bytes version + 1 byte type length + type
|
|
24
|
+
* + 2 bytes fromTable byte-length (big-endian; 0 = absent) + fromTable + ddl
|
|
25
|
+
*
|
|
26
|
+
* The `fromTable` slot sits BEFORE the ddl because the ddl is "rest of buffer" —
|
|
27
|
+
* nothing can be appended after it. Inserting the slot is an incompatible layout
|
|
28
|
+
* change, covered by the `fv:` gate (SYNC_METADATA_FORMAT_VERSION 5 in keys.ts).
|
|
22
29
|
*/
|
|
23
30
|
export declare function serializeMigration(migration: StoredMigration): Uint8Array;
|
|
24
31
|
/**
|
|
@@ -27,6 +34,11 @@ export declare function serializeMigration(migration: StoredMigration): Uint8Arr
|
|
|
27
34
|
export declare function deserializeMigration(buffer: Uint8Array): StoredMigration;
|
|
28
35
|
/**
|
|
29
36
|
* Schema migration store operations.
|
|
37
|
+
*
|
|
38
|
+
* Every method is keyed by `(schemaName, kind, objectName)`: the object name
|
|
39
|
+
* alone is ambiguous, since an index migration names an INDEX while every other
|
|
40
|
+
* migration type names a TABLE. Callers derive `kind` from the migration type
|
|
41
|
+
* with {@link import('../sync/protocol.js').migrationObjectKind}.
|
|
30
42
|
*/
|
|
31
43
|
export declare class SchemaMigrationStore {
|
|
32
44
|
private readonly kv;
|
|
@@ -34,27 +46,38 @@ export declare class SchemaMigrationStore {
|
|
|
34
46
|
/**
|
|
35
47
|
* Get a specific migration by version.
|
|
36
48
|
*/
|
|
37
|
-
getMigration(schemaName: string,
|
|
49
|
+
getMigration(schemaName: string, kind: SchemaObjectKind, objectName: string, version: number): Promise<StoredMigration | undefined>;
|
|
38
50
|
/**
|
|
39
51
|
* Record a new migration.
|
|
40
52
|
*/
|
|
41
|
-
recordMigration(schemaName: string,
|
|
53
|
+
recordMigration(schemaName: string, kind: SchemaObjectKind, objectName: string, migration: StoredMigration): Promise<void>;
|
|
42
54
|
/**
|
|
43
55
|
* Record migration in a batch.
|
|
44
56
|
*/
|
|
45
|
-
recordMigrationBatch(batch: WriteBatch, schemaName: string,
|
|
57
|
+
recordMigrationBatch(batch: WriteBatch, schemaName: string, kind: SchemaObjectKind, objectName: string, migration: StoredMigration): void;
|
|
58
|
+
/**
|
|
59
|
+
* Get the current schema version for one object (a table's, or an index's).
|
|
60
|
+
*/
|
|
61
|
+
getCurrentVersion(schemaName: string, kind: SchemaObjectKind, objectName: string): Promise<number>;
|
|
46
62
|
/**
|
|
47
|
-
* Get
|
|
63
|
+
* Get all migrations for one object.
|
|
48
64
|
*/
|
|
49
|
-
|
|
65
|
+
getAllMigrations(schemaName: string, kind: SchemaObjectKind, objectName: string): AsyncIterable<StoredMigration>;
|
|
50
66
|
/**
|
|
51
|
-
*
|
|
67
|
+
* Every migration on this replica as a wire record, in causal (HLC) order.
|
|
68
|
+
*
|
|
69
|
+
* The producers that ship DDL — both snapshot paths and the delta collector —
|
|
70
|
+
* all need the whole set, and all need it causally ordered: DDL replays in list
|
|
71
|
+
* order, so a `create index` ahead of its table's `create table` fails outright.
|
|
72
|
+
* `sm:` key order is (schema, object kind, object name), which puts every index
|
|
73
|
+
* migration ahead of every table migration in a schema, so the scan order can
|
|
74
|
+
* never be used directly.
|
|
52
75
|
*/
|
|
53
|
-
|
|
76
|
+
listAllMigrations(): Promise<SchemaMigration[]>;
|
|
54
77
|
/**
|
|
55
78
|
* Check if a migration conflicts with an existing one.
|
|
56
79
|
* Returns the existing migration if there's a conflict, undefined otherwise.
|
|
57
80
|
*/
|
|
58
|
-
checkConflict(schemaName: string,
|
|
81
|
+
checkConflict(schemaName: string, kind: SchemaObjectKind, objectName: string, version: number, incomingHLC: HLC): Promise<StoredMigration | undefined>;
|
|
59
82
|
}
|
|
60
83
|
//# sourceMappingURL=schema-migration.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-migration.d.ts","sourceRoot":"","sources":["../../../src/metadata/schema-migration.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,KAAK,GAAG,EAA4C,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"schema-migration.d.ts","sourceRoot":"","sources":["../../../src/metadata/schema-migration.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,KAAK,GAAG,EAA4C,MAAM,iBAAiB,CAAC;AAOrF,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,GAAG,CAAC;IACT,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,eAAe,GAAG,UAAU,CAmCzE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,CAqCxE;AAED;;;;;;;GAOG;AACH,qBAAa,oBAAoB;IACnB,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,OAAO;IAExC;;OAEG;IACG,YAAY,CAChB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,gBAAgB,EACtB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAOvC;;OAEG;IACG,eAAe,CACnB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,gBAAgB,EACtB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,eAAe,GACzB,OAAO,CAAC,IAAI,CAAC;IAKhB;;OAEG;IACH,oBAAoB,CAClB,KAAK,EAAE,UAAU,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,gBAAgB,EACtB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,eAAe,GACzB,IAAI;IAKP;;OAEG;IACG,iBAAiB,CACrB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,gBAAgB,EACtB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC;IAYlB;;OAEG;IACI,gBAAgB,CACrB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,gBAAgB,EACtB,UAAU,EAAE,MAAM,GACjB,aAAa,CAAC,eAAe,CAAC;IAQjC;;;;;;;;;OASG;IACG,iBAAiB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAsBrD;;;OAGG;IACG,aAAa,CACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,gBAAgB,EACtB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,GAAG,GACf,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;CAWxC"}
|
|
@@ -5,17 +5,24 @@
|
|
|
5
5
|
* Uses first-writer-wins for conflict resolution on schema changes.
|
|
6
6
|
*/
|
|
7
7
|
import { serializeHLC, deserializeHLC, compareHLC } from '../clock/hlc.js';
|
|
8
|
-
import { buildSchemaMigrationKey, buildSchemaMigrationScanBounds } from './keys.js';
|
|
8
|
+
import { buildAllSchemaMigrationsScanBounds, buildSchemaMigrationKey, buildSchemaMigrationScanBounds, parseSchemaMigrationKey, } from './keys.js';
|
|
9
|
+
import { sortMigrationsByHLC, } from '../sync/protocol.js';
|
|
9
10
|
/**
|
|
10
11
|
* Serialize a migration for storage.
|
|
11
|
-
* Format: 30 bytes HLC + 4 bytes version + 1 byte type length + type
|
|
12
|
+
* Format: 30 bytes HLC + 4 bytes version + 1 byte type length + type
|
|
13
|
+
* + 2 bytes fromTable byte-length (big-endian; 0 = absent) + fromTable + ddl
|
|
14
|
+
*
|
|
15
|
+
* The `fromTable` slot sits BEFORE the ddl because the ddl is "rest of buffer" —
|
|
16
|
+
* nothing can be appended after it. Inserting the slot is an incompatible layout
|
|
17
|
+
* change, covered by the `fv:` gate (SYNC_METADATA_FORMAT_VERSION 5 in keys.ts).
|
|
12
18
|
*/
|
|
13
19
|
export function serializeMigration(migration) {
|
|
14
20
|
const encoder = new TextEncoder();
|
|
15
21
|
const typeBytes = encoder.encode(migration.type);
|
|
22
|
+
const fromBytes = encoder.encode(migration.fromTable ?? '');
|
|
16
23
|
const ddlBytes = encoder.encode(migration.ddl);
|
|
17
24
|
const hlcBytes = serializeHLC(migration.hlc);
|
|
18
|
-
const buffer = new Uint8Array(30 + 4 + 1 + typeBytes.length + ddlBytes.length);
|
|
25
|
+
const buffer = new Uint8Array(30 + 4 + 1 + typeBytes.length + 2 + fromBytes.length + ddlBytes.length);
|
|
19
26
|
let offset = 0;
|
|
20
27
|
// HLC (30 bytes)
|
|
21
28
|
buffer.set(hlcBytes, offset);
|
|
@@ -29,6 +36,11 @@ export function serializeMigration(migration) {
|
|
|
29
36
|
offset += 1;
|
|
30
37
|
buffer.set(typeBytes, offset);
|
|
31
38
|
offset += typeBytes.length;
|
|
39
|
+
// fromTable byte-length (2 bytes, big-endian; 0 = absent) + fromTable
|
|
40
|
+
view.setUint16(offset, fromBytes.length, false);
|
|
41
|
+
offset += 2;
|
|
42
|
+
buffer.set(fromBytes, offset);
|
|
43
|
+
offset += fromBytes.length;
|
|
32
44
|
// DDL (rest of buffer)
|
|
33
45
|
buffer.set(ddlBytes, offset);
|
|
34
46
|
return buffer;
|
|
@@ -51,12 +63,30 @@ export function deserializeMigration(buffer) {
|
|
|
51
63
|
offset += 1;
|
|
52
64
|
const type = decoder.decode(buffer.slice(offset, offset + typeLength));
|
|
53
65
|
offset += typeLength;
|
|
66
|
+
// fromTable (2-byte big-endian byte-length; 0 = absent)
|
|
67
|
+
const fromLength = view.getUint16(offset, false);
|
|
68
|
+
offset += 2;
|
|
69
|
+
const fromTable = fromLength > 0
|
|
70
|
+
? decoder.decode(buffer.slice(offset, offset + fromLength))
|
|
71
|
+
: undefined;
|
|
72
|
+
offset += fromLength;
|
|
54
73
|
// DDL
|
|
55
74
|
const ddl = decoder.decode(buffer.slice(offset));
|
|
56
|
-
return {
|
|
75
|
+
return {
|
|
76
|
+
type,
|
|
77
|
+
...(fromTable !== undefined ? { fromTable } : {}),
|
|
78
|
+
ddl,
|
|
79
|
+
hlc,
|
|
80
|
+
schemaVersion,
|
|
81
|
+
};
|
|
57
82
|
}
|
|
58
83
|
/**
|
|
59
84
|
* Schema migration store operations.
|
|
85
|
+
*
|
|
86
|
+
* Every method is keyed by `(schemaName, kind, objectName)`: the object name
|
|
87
|
+
* alone is ambiguous, since an index migration names an INDEX while every other
|
|
88
|
+
* migration type names a TABLE. Callers derive `kind` from the migration type
|
|
89
|
+
* with {@link import('../sync/protocol.js').migrationObjectKind}.
|
|
60
90
|
*/
|
|
61
91
|
export class SchemaMigrationStore {
|
|
62
92
|
kv;
|
|
@@ -66,8 +96,8 @@ export class SchemaMigrationStore {
|
|
|
66
96
|
/**
|
|
67
97
|
* Get a specific migration by version.
|
|
68
98
|
*/
|
|
69
|
-
async getMigration(schemaName,
|
|
70
|
-
const key = buildSchemaMigrationKey(schemaName,
|
|
99
|
+
async getMigration(schemaName, kind, objectName, version) {
|
|
100
|
+
const key = buildSchemaMigrationKey(schemaName, kind, objectName, version);
|
|
71
101
|
const data = await this.kv.get(key);
|
|
72
102
|
if (!data)
|
|
73
103
|
return undefined;
|
|
@@ -76,22 +106,22 @@ export class SchemaMigrationStore {
|
|
|
76
106
|
/**
|
|
77
107
|
* Record a new migration.
|
|
78
108
|
*/
|
|
79
|
-
async recordMigration(schemaName,
|
|
80
|
-
const key = buildSchemaMigrationKey(schemaName,
|
|
109
|
+
async recordMigration(schemaName, kind, objectName, migration) {
|
|
110
|
+
const key = buildSchemaMigrationKey(schemaName, kind, objectName, migration.schemaVersion);
|
|
81
111
|
await this.kv.put(key, serializeMigration(migration));
|
|
82
112
|
}
|
|
83
113
|
/**
|
|
84
114
|
* Record migration in a batch.
|
|
85
115
|
*/
|
|
86
|
-
recordMigrationBatch(batch, schemaName,
|
|
87
|
-
const key = buildSchemaMigrationKey(schemaName,
|
|
116
|
+
recordMigrationBatch(batch, schemaName, kind, objectName, migration) {
|
|
117
|
+
const key = buildSchemaMigrationKey(schemaName, kind, objectName, migration.schemaVersion);
|
|
88
118
|
batch.put(key, serializeMigration(migration));
|
|
89
119
|
}
|
|
90
120
|
/**
|
|
91
|
-
* Get the current schema version for a table.
|
|
121
|
+
* Get the current schema version for one object (a table's, or an index's).
|
|
92
122
|
*/
|
|
93
|
-
async getCurrentVersion(schemaName,
|
|
94
|
-
const bounds = buildSchemaMigrationScanBounds(schemaName,
|
|
123
|
+
async getCurrentVersion(schemaName, kind, objectName) {
|
|
124
|
+
const bounds = buildSchemaMigrationScanBounds(schemaName, kind, objectName);
|
|
95
125
|
let maxVersion = 0;
|
|
96
126
|
for await (const entry of this.kv.iterate({ ...bounds, reverse: true, limit: 1 })) {
|
|
97
127
|
const migration = deserializeMigration(entry.value);
|
|
@@ -100,20 +130,49 @@ export class SchemaMigrationStore {
|
|
|
100
130
|
return maxVersion;
|
|
101
131
|
}
|
|
102
132
|
/**
|
|
103
|
-
* Get all migrations for
|
|
133
|
+
* Get all migrations for one object.
|
|
104
134
|
*/
|
|
105
|
-
async *getAllMigrations(schemaName,
|
|
106
|
-
const bounds = buildSchemaMigrationScanBounds(schemaName,
|
|
135
|
+
async *getAllMigrations(schemaName, kind, objectName) {
|
|
136
|
+
const bounds = buildSchemaMigrationScanBounds(schemaName, kind, objectName);
|
|
107
137
|
for await (const entry of this.kv.iterate(bounds)) {
|
|
108
138
|
yield deserializeMigration(entry.value);
|
|
109
139
|
}
|
|
110
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Every migration on this replica as a wire record, in causal (HLC) order.
|
|
143
|
+
*
|
|
144
|
+
* The producers that ship DDL — both snapshot paths and the delta collector —
|
|
145
|
+
* all need the whole set, and all need it causally ordered: DDL replays in list
|
|
146
|
+
* order, so a `create index` ahead of its table's `create table` fails outright.
|
|
147
|
+
* `sm:` key order is (schema, object kind, object name), which puts every index
|
|
148
|
+
* migration ahead of every table migration in a schema, so the scan order can
|
|
149
|
+
* never be used directly.
|
|
150
|
+
*/
|
|
151
|
+
async listAllMigrations() {
|
|
152
|
+
const migrations = [];
|
|
153
|
+
for await (const entry of this.kv.iterate(buildAllSchemaMigrationsScanBounds())) {
|
|
154
|
+
const parsed = parseSchemaMigrationKey(entry.key);
|
|
155
|
+
if (!parsed)
|
|
156
|
+
continue;
|
|
157
|
+
const stored = deserializeMigration(entry.value);
|
|
158
|
+
migrations.push({
|
|
159
|
+
type: stored.type,
|
|
160
|
+
schema: parsed.schema,
|
|
161
|
+
table: parsed.table,
|
|
162
|
+
...(stored.fromTable !== undefined ? { fromTable: stored.fromTable } : {}),
|
|
163
|
+
ddl: stored.ddl,
|
|
164
|
+
hlc: stored.hlc,
|
|
165
|
+
schemaVersion: stored.schemaVersion,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
return sortMigrationsByHLC(migrations);
|
|
169
|
+
}
|
|
111
170
|
/**
|
|
112
171
|
* Check if a migration conflicts with an existing one.
|
|
113
172
|
* Returns the existing migration if there's a conflict, undefined otherwise.
|
|
114
173
|
*/
|
|
115
|
-
async checkConflict(schemaName,
|
|
116
|
-
const existing = await this.getMigration(schemaName,
|
|
174
|
+
async checkConflict(schemaName, kind, objectName, version, incomingHLC) {
|
|
175
|
+
const existing = await this.getMigration(schemaName, kind, objectName, version);
|
|
117
176
|
if (!existing)
|
|
118
177
|
return undefined;
|
|
119
178
|
// First-writer-wins: if existing has lower HLC, it wins
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-migration.js","sourceRoot":"","sources":["../../../src/metadata/schema-migration.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAY,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,
|
|
1
|
+
{"version":3,"file":"schema-migration.js","sourceRoot":"","sources":["../../../src/metadata/schema-migration.ts"],"names":[],"mappings":"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"}
|
|
@@ -50,9 +50,25 @@ export interface ResolvedChange {
|
|
|
50
50
|
* 2. Apply data to store via callback
|
|
51
51
|
* 3. Commit CRDT metadata (+ durable quarantine of diverted changes)
|
|
52
52
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
53
|
+
* ORDER-INDEPENDENT: `changes` may arrive in any order and produces the same
|
|
54
|
+
* committed state as the HLC-ordered array. Callers need not (and do not) preserve
|
|
55
|
+
* timestamp order — the coordinator's `onBeforeApplyChanges` approval hook returns a
|
|
56
|
+
* caller-supplied array, and the REST/WebSocket ingress accepts an arbitrary array
|
|
57
|
+
* from any client. Everything whose outcome depends on order is re-derived from the
|
|
58
|
+
* HLCs here rather than from arrival position: resolution and
|
|
59
|
+
* {@link reconcileInBatchDeletes} already compare HLCs, the store apply list is sorted
|
|
60
|
+
* by {@link orderDataChangesByHLC}, the DDL list by {@link orderMigrationsByHLC}, and
|
|
61
|
+
* the emitted payload by {@link emitRemoteChanges}. Everything else (the counters, the
|
|
62
|
+
* quarantine holds, the watermark max) is order-insensitive by construction. The
|
|
63
|
+
* guarantee covers committed STATE, not the event stream: `onConflictResolved` fires
|
|
64
|
+
* from the resolve loop in arrival order, and an `onUnknownTable` event reports the
|
|
65
|
+
* FIRST changeset that referenced the table as the straggler origin, so a reordered
|
|
66
|
+
* batch can name a different relayer — telemetry only, never a stored fact.
|
|
67
|
+
*
|
|
68
|
+
* Unknown-table disposition: a change referencing a table the batch leaves absent — not
|
|
69
|
+
* in the local basis and not created by this batch's schema steps (a retired-table
|
|
70
|
+
* straggler delta — see `docs/migration.md` § 4 Contract) — is **diverted** during
|
|
71
|
+
* resolution — never resolved, applied, or recorded as CRDT
|
|
56
72
|
* metadata, so the change log stays clean (no survivor-HLC pollution). Diverted
|
|
57
73
|
* changes are held (durably, idempotently — `quarantine`, or `store-and-forward`
|
|
58
74
|
* which additionally marks them forwardable) or ignored per
|
|
@@ -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;
|
|
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;AAEvB,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;AAwID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,YAAY,CACjC,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,SAAS,EAAE,GAClB,OAAO,CAAC,WAAW,CAAC,CAwStB;AA6FD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,gBAAgB,CACrC,GAAG,EAAE,WAAW,EAChB,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAsBjB;AAgGD;;;;;;;;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"}
|