@quereus/plugin-sync 0.3.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 +154 -0
- package/dist/src/clock/hlc.d.ts +105 -0
- package/dist/src/clock/hlc.d.ts.map +1 -0
- package/dist/src/clock/hlc.js +251 -0
- package/dist/src/clock/hlc.js.map +1 -0
- package/dist/src/clock/index.d.ts +6 -0
- package/dist/src/clock/index.d.ts.map +1 -0
- package/dist/src/clock/index.js +6 -0
- package/dist/src/clock/index.js.map +1 -0
- package/dist/src/clock/site.d.ts +58 -0
- package/dist/src/clock/site.d.ts.map +1 -0
- package/dist/src/clock/site.js +137 -0
- package/dist/src/clock/site.js.map +1 -0
- package/dist/src/create-sync-module.d.ts +85 -0
- package/dist/src/create-sync-module.d.ts.map +1 -0
- package/dist/src/create-sync-module.js +54 -0
- package/dist/src/create-sync-module.js.map +1 -0
- package/dist/src/index.d.ts +31 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +42 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/metadata/change-log.d.ts +67 -0
- package/dist/src/metadata/change-log.d.ts.map +1 -0
- package/dist/src/metadata/change-log.js +107 -0
- package/dist/src/metadata/change-log.js.map +1 -0
- package/dist/src/metadata/column-version.d.ts +58 -0
- package/dist/src/metadata/column-version.d.ts.map +1 -0
- package/dist/src/metadata/column-version.js +100 -0
- package/dist/src/metadata/column-version.js.map +1 -0
- package/dist/src/metadata/index.d.ts +11 -0
- package/dist/src/metadata/index.d.ts.map +1 -0
- package/dist/src/metadata/index.js +11 -0
- package/dist/src/metadata/index.js.map +1 -0
- package/dist/src/metadata/keys.d.ts +180 -0
- package/dist/src/metadata/keys.d.ts.map +1 -0
- package/dist/src/metadata/keys.js +390 -0
- package/dist/src/metadata/keys.js.map +1 -0
- package/dist/src/metadata/peer-state.d.ts +52 -0
- package/dist/src/metadata/peer-state.d.ts.map +1 -0
- package/dist/src/metadata/peer-state.js +87 -0
- package/dist/src/metadata/peer-state.js.map +1 -0
- package/dist/src/metadata/schema-migration.d.ts +60 -0
- package/dist/src/metadata/schema-migration.d.ts.map +1 -0
- package/dist/src/metadata/schema-migration.js +126 -0
- package/dist/src/metadata/schema-migration.js.map +1 -0
- package/dist/src/metadata/schema-version.d.ts +163 -0
- package/dist/src/metadata/schema-version.d.ts.map +1 -0
- package/dist/src/metadata/schema-version.js +307 -0
- package/dist/src/metadata/schema-version.js.map +1 -0
- package/dist/src/metadata/tombstones.d.ts +67 -0
- package/dist/src/metadata/tombstones.d.ts.map +1 -0
- package/dist/src/metadata/tombstones.js +125 -0
- package/dist/src/metadata/tombstones.js.map +1 -0
- package/dist/src/sync/events.d.ts +117 -0
- package/dist/src/sync/events.d.ts.map +1 -0
- package/dist/src/sync/events.js +56 -0
- package/dist/src/sync/events.js.map +1 -0
- package/dist/src/sync/index.d.ts +8 -0
- package/dist/src/sync/index.d.ts.map +1 -0
- package/dist/src/sync/index.js +8 -0
- package/dist/src/sync/index.js.map +1 -0
- package/dist/src/sync/manager.d.ts +146 -0
- package/dist/src/sync/manager.d.ts.map +1 -0
- package/dist/src/sync/manager.js +8 -0
- package/dist/src/sync/manager.js.map +1 -0
- package/dist/src/sync/protocol.d.ts +282 -0
- package/dist/src/sync/protocol.d.ts.map +1 -0
- package/dist/src/sync/protocol.js +16 -0
- package/dist/src/sync/protocol.js.map +1 -0
- package/dist/src/sync/store-adapter.d.ts +42 -0
- package/dist/src/sync/store-adapter.d.ts.map +1 -0
- package/dist/src/sync/store-adapter.js +232 -0
- package/dist/src/sync/store-adapter.js.map +1 -0
- package/dist/src/sync/sync-manager-impl.d.ts +91 -0
- package/dist/src/sync/sync-manager-impl.d.ts.map +1 -0
- package/dist/src/sync/sync-manager-impl.js +1123 -0
- package/dist/src/sync/sync-manager-impl.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema migration tracking for sync.
|
|
3
|
+
*
|
|
4
|
+
* Tracks DDL changes (CREATE TABLE, ALTER TABLE, etc.) with HLC timestamps.
|
|
5
|
+
* Uses first-writer-wins for conflict resolution on schema changes.
|
|
6
|
+
*/
|
|
7
|
+
import { serializeHLC, deserializeHLC, compareHLC } from '../clock/hlc.js';
|
|
8
|
+
import { buildSchemaMigrationKey, buildSchemaMigrationScanBounds } from './keys.js';
|
|
9
|
+
/**
|
|
10
|
+
* Serialize a migration for storage.
|
|
11
|
+
* Format: 26 bytes HLC + 4 bytes version + 1 byte type length + type + ddl
|
|
12
|
+
*/
|
|
13
|
+
export function serializeMigration(migration) {
|
|
14
|
+
const encoder = new TextEncoder();
|
|
15
|
+
const typeBytes = encoder.encode(migration.type);
|
|
16
|
+
const ddlBytes = encoder.encode(migration.ddl);
|
|
17
|
+
const hlcBytes = serializeHLC(migration.hlc);
|
|
18
|
+
const buffer = new Uint8Array(26 + 4 + 1 + typeBytes.length + ddlBytes.length);
|
|
19
|
+
let offset = 0;
|
|
20
|
+
// HLC (26 bytes)
|
|
21
|
+
buffer.set(hlcBytes, offset);
|
|
22
|
+
offset += 26;
|
|
23
|
+
// Schema version (4 bytes, big-endian)
|
|
24
|
+
const view = new DataView(buffer.buffer);
|
|
25
|
+
view.setUint32(offset, migration.schemaVersion, false);
|
|
26
|
+
offset += 4;
|
|
27
|
+
// Type length (1 byte) + type
|
|
28
|
+
buffer[offset] = typeBytes.length;
|
|
29
|
+
offset += 1;
|
|
30
|
+
buffer.set(typeBytes, offset);
|
|
31
|
+
offset += typeBytes.length;
|
|
32
|
+
// DDL (rest of buffer)
|
|
33
|
+
buffer.set(ddlBytes, offset);
|
|
34
|
+
return buffer;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Deserialize a migration from storage.
|
|
38
|
+
*/
|
|
39
|
+
export function deserializeMigration(buffer) {
|
|
40
|
+
const decoder = new TextDecoder();
|
|
41
|
+
let offset = 0;
|
|
42
|
+
// HLC (26 bytes)
|
|
43
|
+
const hlc = deserializeHLC(buffer.slice(0, 26));
|
|
44
|
+
offset += 26;
|
|
45
|
+
// Schema version (4 bytes)
|
|
46
|
+
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
47
|
+
const schemaVersion = view.getUint32(offset, false);
|
|
48
|
+
offset += 4;
|
|
49
|
+
// Type length + type
|
|
50
|
+
const typeLength = buffer[offset];
|
|
51
|
+
offset += 1;
|
|
52
|
+
const type = decoder.decode(buffer.slice(offset, offset + typeLength));
|
|
53
|
+
offset += typeLength;
|
|
54
|
+
// DDL
|
|
55
|
+
const ddl = decoder.decode(buffer.slice(offset));
|
|
56
|
+
return { type, ddl, hlc, schemaVersion };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Schema migration store operations.
|
|
60
|
+
*/
|
|
61
|
+
export class SchemaMigrationStore {
|
|
62
|
+
kv;
|
|
63
|
+
constructor(kv) {
|
|
64
|
+
this.kv = kv;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get a specific migration by version.
|
|
68
|
+
*/
|
|
69
|
+
async getMigration(schemaName, tableName, version) {
|
|
70
|
+
const key = buildSchemaMigrationKey(schemaName, tableName, version);
|
|
71
|
+
const data = await this.kv.get(key);
|
|
72
|
+
if (!data)
|
|
73
|
+
return undefined;
|
|
74
|
+
return deserializeMigration(data);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Record a new migration.
|
|
78
|
+
*/
|
|
79
|
+
async recordMigration(schemaName, tableName, migration) {
|
|
80
|
+
const key = buildSchemaMigrationKey(schemaName, tableName, migration.schemaVersion);
|
|
81
|
+
await this.kv.put(key, serializeMigration(migration));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Record migration in a batch.
|
|
85
|
+
*/
|
|
86
|
+
recordMigrationBatch(batch, schemaName, tableName, migration) {
|
|
87
|
+
const key = buildSchemaMigrationKey(schemaName, tableName, migration.schemaVersion);
|
|
88
|
+
batch.put(key, serializeMigration(migration));
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get the current schema version for a table.
|
|
92
|
+
*/
|
|
93
|
+
async getCurrentVersion(schemaName, tableName) {
|
|
94
|
+
const bounds = buildSchemaMigrationScanBounds(schemaName, tableName);
|
|
95
|
+
let maxVersion = 0;
|
|
96
|
+
for await (const entry of this.kv.iterate({ ...bounds, reverse: true, limit: 1 })) {
|
|
97
|
+
const migration = deserializeMigration(entry.value);
|
|
98
|
+
maxVersion = migration.schemaVersion;
|
|
99
|
+
}
|
|
100
|
+
return maxVersion;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Get all migrations for a table.
|
|
104
|
+
*/
|
|
105
|
+
async *getAllMigrations(schemaName, tableName) {
|
|
106
|
+
const bounds = buildSchemaMigrationScanBounds(schemaName, tableName);
|
|
107
|
+
for await (const entry of this.kv.iterate(bounds)) {
|
|
108
|
+
yield deserializeMigration(entry.value);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Check if a migration conflicts with an existing one.
|
|
113
|
+
* Returns the existing migration if there's a conflict, undefined otherwise.
|
|
114
|
+
*/
|
|
115
|
+
async checkConflict(schemaName, tableName, version, incomingHLC) {
|
|
116
|
+
const existing = await this.getMigration(schemaName, tableName, version);
|
|
117
|
+
if (!existing)
|
|
118
|
+
return undefined;
|
|
119
|
+
// First-writer-wins: if existing has lower HLC, it wins
|
|
120
|
+
if (compareHLC(existing.hlc, incomingHLC) < 0) {
|
|
121
|
+
return existing; // Existing wins, return it as the conflict winner
|
|
122
|
+
}
|
|
123
|
+
return undefined; // Incoming wins or they're equal
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=schema-migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-migration.js","sourceRoot":"","sources":["../../../src/metadata/schema-migration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Column-level schema versioning for sync.
|
|
3
|
+
*
|
|
4
|
+
* Tracks the HLC timestamp of each column's definition to enable
|
|
5
|
+
* "most destructive wins" conflict resolution for schema changes.
|
|
6
|
+
*
|
|
7
|
+
* Key format: sv:{schema}.{table}:{column}
|
|
8
|
+
* Value: HLC (26 bytes) + 1 byte type + type-specific data
|
|
9
|
+
*
|
|
10
|
+
* Column types:
|
|
11
|
+
* - 0x01: Regular column (type affinity, nullable, default)
|
|
12
|
+
* - 0x02: Dropped column (tombstone)
|
|
13
|
+
* - 0x03: Table-level (for CREATE/DROP TABLE)
|
|
14
|
+
*
|
|
15
|
+
* Destructiveness hierarchy (higher wins in conflicts):
|
|
16
|
+
* - DROP TABLE/COLUMN: 3 (most destructive)
|
|
17
|
+
* - ALTER COLUMN: 2
|
|
18
|
+
* - ADD COLUMN/CREATE TABLE: 1 (least destructive)
|
|
19
|
+
*/
|
|
20
|
+
import type { KVStore, WriteBatch } from '@quereus/plugin-store';
|
|
21
|
+
import { type HLC } from '../clock/hlc.js';
|
|
22
|
+
/** Schema version entry types. */
|
|
23
|
+
export type SchemaVersionType = 'column' | 'dropped' | 'table';
|
|
24
|
+
/**
|
|
25
|
+
* Schema version entry for a column or table.
|
|
26
|
+
*/
|
|
27
|
+
export interface SchemaVersion {
|
|
28
|
+
hlc: HLC;
|
|
29
|
+
type: SchemaVersionType;
|
|
30
|
+
/** Column type affinity (for column type). */
|
|
31
|
+
affinity?: string;
|
|
32
|
+
/** Whether column is nullable (for column type). */
|
|
33
|
+
nullable?: boolean;
|
|
34
|
+
/** Default value expression (for column type). */
|
|
35
|
+
defaultExpr?: string;
|
|
36
|
+
/** DDL statement (for table type). */
|
|
37
|
+
ddl?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build a schema version key.
|
|
41
|
+
* Format: sv:{schema}.{table}:{column}
|
|
42
|
+
* For table-level entries, column is '__table__'.
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildSchemaVersionKey(schemaName: string, tableName: string, columnName?: string): Uint8Array;
|
|
45
|
+
/**
|
|
46
|
+
* Build scan bounds for all schema versions of a table.
|
|
47
|
+
*/
|
|
48
|
+
export declare function buildSchemaVersionScanBounds(schemaName: string, tableName: string): {
|
|
49
|
+
gte: Uint8Array;
|
|
50
|
+
lt: Uint8Array;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Build scan bounds for ALL schema versions across all tables.
|
|
54
|
+
*/
|
|
55
|
+
export declare function buildAllSchemaVersionsScanBounds(): {
|
|
56
|
+
gte: Uint8Array;
|
|
57
|
+
lt: Uint8Array;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Serialize a schema version for storage.
|
|
61
|
+
*/
|
|
62
|
+
export declare function serializeSchemaVersion(version: SchemaVersion): Uint8Array;
|
|
63
|
+
/**
|
|
64
|
+
* Deserialize a schema version from storage.
|
|
65
|
+
*/
|
|
66
|
+
export declare function deserializeSchemaVersion(buffer: Uint8Array): SchemaVersion;
|
|
67
|
+
/**
|
|
68
|
+
* Parse a schema version key to extract components.
|
|
69
|
+
*/
|
|
70
|
+
export declare function parseSchemaVersionKey(key: Uint8Array): {
|
|
71
|
+
schema: string;
|
|
72
|
+
table: string;
|
|
73
|
+
column: string;
|
|
74
|
+
} | null;
|
|
75
|
+
/**
|
|
76
|
+
* Schema version store operations.
|
|
77
|
+
*/
|
|
78
|
+
export declare class SchemaVersionStore {
|
|
79
|
+
private readonly kv;
|
|
80
|
+
constructor(kv: KVStore);
|
|
81
|
+
/**
|
|
82
|
+
* Get the schema version for a column.
|
|
83
|
+
*/
|
|
84
|
+
getColumnVersion(schemaName: string, tableName: string, columnName: string): Promise<SchemaVersion | undefined>;
|
|
85
|
+
/**
|
|
86
|
+
* Get the schema version for a table.
|
|
87
|
+
*/
|
|
88
|
+
getTableVersion(schemaName: string, tableName: string): Promise<SchemaVersion | undefined>;
|
|
89
|
+
/**
|
|
90
|
+
* Set the schema version for a column.
|
|
91
|
+
*/
|
|
92
|
+
setColumnVersion(schemaName: string, tableName: string, columnName: string, version: SchemaVersion): Promise<void>;
|
|
93
|
+
/**
|
|
94
|
+
* Set the schema version for a column in a batch.
|
|
95
|
+
*/
|
|
96
|
+
setColumnVersionBatch(batch: WriteBatch, schemaName: string, tableName: string, columnName: string, version: SchemaVersion): void;
|
|
97
|
+
/**
|
|
98
|
+
* Set the schema version for a table.
|
|
99
|
+
*/
|
|
100
|
+
setTableVersion(schemaName: string, tableName: string, version: SchemaVersion): Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* Get all column versions for a table.
|
|
103
|
+
*/
|
|
104
|
+
getAllColumnVersions(schemaName: string, tableName: string): AsyncIterable<{
|
|
105
|
+
column: string;
|
|
106
|
+
version: SchemaVersion;
|
|
107
|
+
}>;
|
|
108
|
+
/**
|
|
109
|
+
* Get all schema versions across all tables.
|
|
110
|
+
*/
|
|
111
|
+
getAllSchemaVersions(): AsyncIterable<{
|
|
112
|
+
schema: string;
|
|
113
|
+
table: string;
|
|
114
|
+
column: string;
|
|
115
|
+
version: SchemaVersion;
|
|
116
|
+
}>;
|
|
117
|
+
/**
|
|
118
|
+
* Check if a schema change should be applied based on HLC comparison.
|
|
119
|
+
* Returns true if the incoming change should win.
|
|
120
|
+
*/
|
|
121
|
+
shouldApplyChange(schemaName: string, tableName: string, columnName: string, incomingHLC: HLC): Promise<boolean>;
|
|
122
|
+
/**
|
|
123
|
+
* Check if a schema change should be applied using "most destructive wins" semantics.
|
|
124
|
+
*
|
|
125
|
+
* When two schema changes conflict (same HLC or concurrent changes):
|
|
126
|
+
* 1. More destructive changes win over less destructive ones
|
|
127
|
+
* 2. If same destructiveness, higher HLC wins
|
|
128
|
+
*
|
|
129
|
+
* Destructiveness hierarchy:
|
|
130
|
+
* - DROP (column or table): 3
|
|
131
|
+
* - ALTER (type change, constraint change): 2
|
|
132
|
+
* - ADD (new column, create table): 1
|
|
133
|
+
*
|
|
134
|
+
* @returns true if the incoming change should be applied
|
|
135
|
+
*/
|
|
136
|
+
shouldApplySchemaChange(schemaName: string, tableName: string, columnName: string, incomingVersion: SchemaVersion): Promise<boolean>;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get the destructiveness level of a schema version type.
|
|
140
|
+
* Higher values are more destructive.
|
|
141
|
+
*/
|
|
142
|
+
export declare function getDestructiveness(type: SchemaVersionType): number;
|
|
143
|
+
/**
|
|
144
|
+
* Schema change operation types for more granular destructiveness.
|
|
145
|
+
*/
|
|
146
|
+
export type SchemaChangeOperation = 'drop_table' | 'drop_column' | 'alter_column' | 'add_column' | 'create_table';
|
|
147
|
+
/**
|
|
148
|
+
* Get the destructiveness level of a schema change operation.
|
|
149
|
+
* Higher values are more destructive.
|
|
150
|
+
*/
|
|
151
|
+
export declare function getOperationDestructiveness(operation: SchemaChangeOperation): number;
|
|
152
|
+
/**
|
|
153
|
+
* Determine if an incoming schema change should be applied based on
|
|
154
|
+
* "most destructive wins" semantics.
|
|
155
|
+
*
|
|
156
|
+
* @param incomingOp The incoming schema change operation
|
|
157
|
+
* @param incomingHLC The HLC of the incoming change
|
|
158
|
+
* @param existingOp The existing schema change operation (if any)
|
|
159
|
+
* @param existingHLC The HLC of the existing change (if any)
|
|
160
|
+
* @returns true if the incoming change should be applied
|
|
161
|
+
*/
|
|
162
|
+
export declare function shouldApplySchemaChangeByOperation(incomingOp: SchemaChangeOperation, incomingHLC: HLC, existingOp?: SchemaChangeOperation, existingHLC?: HLC): boolean;
|
|
163
|
+
//# sourceMappingURL=schema-version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-version.d.ts","sourceRoot":"","sources":["../../../src/metadata/schema-version.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Column-level schema versioning for sync.
|
|
3
|
+
*
|
|
4
|
+
* Tracks the HLC timestamp of each column's definition to enable
|
|
5
|
+
* "most destructive wins" conflict resolution for schema changes.
|
|
6
|
+
*
|
|
7
|
+
* Key format: sv:{schema}.{table}:{column}
|
|
8
|
+
* Value: HLC (26 bytes) + 1 byte type + type-specific data
|
|
9
|
+
*
|
|
10
|
+
* Column types:
|
|
11
|
+
* - 0x01: Regular column (type affinity, nullable, default)
|
|
12
|
+
* - 0x02: Dropped column (tombstone)
|
|
13
|
+
* - 0x03: Table-level (for CREATE/DROP TABLE)
|
|
14
|
+
*
|
|
15
|
+
* Destructiveness hierarchy (higher wins in conflicts):
|
|
16
|
+
* - DROP TABLE/COLUMN: 3 (most destructive)
|
|
17
|
+
* - ALTER COLUMN: 2
|
|
18
|
+
* - ADD COLUMN/CREATE TABLE: 1 (least destructive)
|
|
19
|
+
*/
|
|
20
|
+
import { serializeHLC, deserializeHLC, compareHLC } from '../clock/hlc.js';
|
|
21
|
+
const encoder = new TextEncoder();
|
|
22
|
+
const decoder = new TextDecoder();
|
|
23
|
+
/**
|
|
24
|
+
* Build a schema version key.
|
|
25
|
+
* Format: sv:{schema}.{table}:{column}
|
|
26
|
+
* For table-level entries, column is '__table__'.
|
|
27
|
+
*/
|
|
28
|
+
export function buildSchemaVersionKey(schemaName, tableName, columnName = '__table__') {
|
|
29
|
+
return encoder.encode(`sv:${schemaName}.${tableName}:${columnName}`);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build scan bounds for all schema versions of a table.
|
|
33
|
+
*/
|
|
34
|
+
export function buildSchemaVersionScanBounds(schemaName, tableName) {
|
|
35
|
+
const prefix = `sv:${schemaName}.${tableName}:`;
|
|
36
|
+
return {
|
|
37
|
+
gte: encoder.encode(prefix),
|
|
38
|
+
lt: incrementLastByte(encoder.encode(prefix)),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Build scan bounds for ALL schema versions across all tables.
|
|
43
|
+
*/
|
|
44
|
+
export function buildAllSchemaVersionsScanBounds() {
|
|
45
|
+
const prefix = encoder.encode('sv:');
|
|
46
|
+
return {
|
|
47
|
+
gte: prefix,
|
|
48
|
+
lt: incrementLastByte(prefix),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Serialize a schema version for storage.
|
|
53
|
+
*/
|
|
54
|
+
export function serializeSchemaVersion(version) {
|
|
55
|
+
const hlcBytes = serializeHLC(version.hlc);
|
|
56
|
+
const typeByte = version.type === 'column' ? 0x01 : version.type === 'dropped' ? 0x02 : 0x03;
|
|
57
|
+
// Serialize type-specific data as JSON
|
|
58
|
+
const data = {};
|
|
59
|
+
if (version.affinity !== undefined)
|
|
60
|
+
data.affinity = version.affinity;
|
|
61
|
+
if (version.nullable !== undefined)
|
|
62
|
+
data.nullable = version.nullable;
|
|
63
|
+
if (version.defaultExpr !== undefined)
|
|
64
|
+
data.defaultExpr = version.defaultExpr;
|
|
65
|
+
if (version.ddl !== undefined)
|
|
66
|
+
data.ddl = version.ddl;
|
|
67
|
+
const dataBytes = encoder.encode(JSON.stringify(data));
|
|
68
|
+
const buffer = new Uint8Array(26 + 1 + dataBytes.length);
|
|
69
|
+
buffer.set(hlcBytes, 0);
|
|
70
|
+
buffer[26] = typeByte;
|
|
71
|
+
buffer.set(dataBytes, 27);
|
|
72
|
+
return buffer;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Deserialize a schema version from storage.
|
|
76
|
+
*/
|
|
77
|
+
export function deserializeSchemaVersion(buffer) {
|
|
78
|
+
const hlc = deserializeHLC(buffer.slice(0, 26));
|
|
79
|
+
const typeByte = buffer[26];
|
|
80
|
+
const type = typeByte === 0x01 ? 'column' : typeByte === 0x02 ? 'dropped' : 'table';
|
|
81
|
+
const dataJson = decoder.decode(buffer.slice(27));
|
|
82
|
+
const data = dataJson ? JSON.parse(dataJson) : {};
|
|
83
|
+
return {
|
|
84
|
+
hlc,
|
|
85
|
+
type,
|
|
86
|
+
affinity: data.affinity,
|
|
87
|
+
nullable: data.nullable,
|
|
88
|
+
defaultExpr: data.defaultExpr,
|
|
89
|
+
ddl: data.ddl,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Parse a schema version key to extract components.
|
|
94
|
+
*/
|
|
95
|
+
export function parseSchemaVersionKey(key) {
|
|
96
|
+
const keyStr = decoder.decode(key);
|
|
97
|
+
if (!keyStr.startsWith('sv:'))
|
|
98
|
+
return null;
|
|
99
|
+
const rest = keyStr.slice(3);
|
|
100
|
+
const firstDot = rest.indexOf('.');
|
|
101
|
+
if (firstDot === -1)
|
|
102
|
+
return null;
|
|
103
|
+
const schema = rest.slice(0, firstDot);
|
|
104
|
+
const afterDot = rest.slice(firstDot + 1);
|
|
105
|
+
const firstColon = afterDot.indexOf(':');
|
|
106
|
+
if (firstColon === -1)
|
|
107
|
+
return null;
|
|
108
|
+
const table = afterDot.slice(0, firstColon);
|
|
109
|
+
const column = afterDot.slice(firstColon + 1);
|
|
110
|
+
return { schema, table, column };
|
|
111
|
+
}
|
|
112
|
+
function incrementLastByte(key) {
|
|
113
|
+
const result = new Uint8Array(key.length);
|
|
114
|
+
result.set(key);
|
|
115
|
+
for (let i = result.length - 1; i >= 0; i--) {
|
|
116
|
+
if (result[i] < 255) {
|
|
117
|
+
result[i]++;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
result[i] = 0;
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Schema version store operations.
|
|
126
|
+
*/
|
|
127
|
+
export class SchemaVersionStore {
|
|
128
|
+
kv;
|
|
129
|
+
constructor(kv) {
|
|
130
|
+
this.kv = kv;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get the schema version for a column.
|
|
134
|
+
*/
|
|
135
|
+
async getColumnVersion(schemaName, tableName, columnName) {
|
|
136
|
+
const key = buildSchemaVersionKey(schemaName, tableName, columnName);
|
|
137
|
+
const data = await this.kv.get(key);
|
|
138
|
+
if (!data)
|
|
139
|
+
return undefined;
|
|
140
|
+
return deserializeSchemaVersion(data);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get the schema version for a table.
|
|
144
|
+
*/
|
|
145
|
+
async getTableVersion(schemaName, tableName) {
|
|
146
|
+
return this.getColumnVersion(schemaName, tableName, '__table__');
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Set the schema version for a column.
|
|
150
|
+
*/
|
|
151
|
+
async setColumnVersion(schemaName, tableName, columnName, version) {
|
|
152
|
+
const key = buildSchemaVersionKey(schemaName, tableName, columnName);
|
|
153
|
+
await this.kv.put(key, serializeSchemaVersion(version));
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Set the schema version for a column in a batch.
|
|
157
|
+
*/
|
|
158
|
+
setColumnVersionBatch(batch, schemaName, tableName, columnName, version) {
|
|
159
|
+
const key = buildSchemaVersionKey(schemaName, tableName, columnName);
|
|
160
|
+
batch.put(key, serializeSchemaVersion(version));
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Set the schema version for a table.
|
|
164
|
+
*/
|
|
165
|
+
async setTableVersion(schemaName, tableName, version) {
|
|
166
|
+
return this.setColumnVersion(schemaName, tableName, '__table__', version);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get all column versions for a table.
|
|
170
|
+
*/
|
|
171
|
+
async *getAllColumnVersions(schemaName, tableName) {
|
|
172
|
+
const bounds = buildSchemaVersionScanBounds(schemaName, tableName);
|
|
173
|
+
for await (const entry of this.kv.iterate(bounds)) {
|
|
174
|
+
const parsed = parseSchemaVersionKey(entry.key);
|
|
175
|
+
if (!parsed)
|
|
176
|
+
continue;
|
|
177
|
+
yield {
|
|
178
|
+
column: parsed.column,
|
|
179
|
+
version: deserializeSchemaVersion(entry.value),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get all schema versions across all tables.
|
|
185
|
+
*/
|
|
186
|
+
async *getAllSchemaVersions() {
|
|
187
|
+
const bounds = buildAllSchemaVersionsScanBounds();
|
|
188
|
+
for await (const entry of this.kv.iterate(bounds)) {
|
|
189
|
+
const parsed = parseSchemaVersionKey(entry.key);
|
|
190
|
+
if (!parsed)
|
|
191
|
+
continue;
|
|
192
|
+
yield {
|
|
193
|
+
schema: parsed.schema,
|
|
194
|
+
table: parsed.table,
|
|
195
|
+
column: parsed.column,
|
|
196
|
+
version: deserializeSchemaVersion(entry.value),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Check if a schema change should be applied based on HLC comparison.
|
|
202
|
+
* Returns true if the incoming change should win.
|
|
203
|
+
*/
|
|
204
|
+
async shouldApplyChange(schemaName, tableName, columnName, incomingHLC) {
|
|
205
|
+
const existing = await this.getColumnVersion(schemaName, tableName, columnName);
|
|
206
|
+
if (!existing)
|
|
207
|
+
return true; // No existing version, apply the change
|
|
208
|
+
// LWW: higher HLC wins
|
|
209
|
+
return compareHLC(incomingHLC, existing.hlc) > 0;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Check if a schema change should be applied using "most destructive wins" semantics.
|
|
213
|
+
*
|
|
214
|
+
* When two schema changes conflict (same HLC or concurrent changes):
|
|
215
|
+
* 1. More destructive changes win over less destructive ones
|
|
216
|
+
* 2. If same destructiveness, higher HLC wins
|
|
217
|
+
*
|
|
218
|
+
* Destructiveness hierarchy:
|
|
219
|
+
* - DROP (column or table): 3
|
|
220
|
+
* - ALTER (type change, constraint change): 2
|
|
221
|
+
* - ADD (new column, create table): 1
|
|
222
|
+
*
|
|
223
|
+
* @returns true if the incoming change should be applied
|
|
224
|
+
*/
|
|
225
|
+
async shouldApplySchemaChange(schemaName, tableName, columnName, incomingVersion) {
|
|
226
|
+
const existing = await this.getColumnVersion(schemaName, tableName, columnName);
|
|
227
|
+
if (!existing)
|
|
228
|
+
return true; // No existing version, apply the change
|
|
229
|
+
const incomingDestructiveness = getDestructiveness(incomingVersion.type);
|
|
230
|
+
const existingDestructiveness = getDestructiveness(existing.type);
|
|
231
|
+
// More destructive wins
|
|
232
|
+
if (incomingDestructiveness > existingDestructiveness) {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
if (incomingDestructiveness < existingDestructiveness) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
// Same destructiveness: LWW (higher HLC wins)
|
|
239
|
+
return compareHLC(incomingVersion.hlc, existing.hlc) > 0;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get the destructiveness level of a schema version type.
|
|
244
|
+
* Higher values are more destructive.
|
|
245
|
+
*/
|
|
246
|
+
export function getDestructiveness(type) {
|
|
247
|
+
switch (type) {
|
|
248
|
+
case 'dropped':
|
|
249
|
+
return 3; // Most destructive
|
|
250
|
+
case 'table':
|
|
251
|
+
// Table type can be either CREATE or DROP, but we treat it as moderate
|
|
252
|
+
// The actual destructiveness depends on whether it's a create or drop
|
|
253
|
+
return 2;
|
|
254
|
+
case 'column':
|
|
255
|
+
return 1; // Least destructive (add/alter)
|
|
256
|
+
default:
|
|
257
|
+
return 0;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Get the destructiveness level of a schema change operation.
|
|
262
|
+
* Higher values are more destructive.
|
|
263
|
+
*/
|
|
264
|
+
export function getOperationDestructiveness(operation) {
|
|
265
|
+
switch (operation) {
|
|
266
|
+
case 'drop_table':
|
|
267
|
+
return 5; // Most destructive
|
|
268
|
+
case 'drop_column':
|
|
269
|
+
return 4;
|
|
270
|
+
case 'alter_column':
|
|
271
|
+
return 3;
|
|
272
|
+
case 'add_column':
|
|
273
|
+
return 2;
|
|
274
|
+
case 'create_table':
|
|
275
|
+
return 1; // Least destructive
|
|
276
|
+
default:
|
|
277
|
+
return 0;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Determine if an incoming schema change should be applied based on
|
|
282
|
+
* "most destructive wins" semantics.
|
|
283
|
+
*
|
|
284
|
+
* @param incomingOp The incoming schema change operation
|
|
285
|
+
* @param incomingHLC The HLC of the incoming change
|
|
286
|
+
* @param existingOp The existing schema change operation (if any)
|
|
287
|
+
* @param existingHLC The HLC of the existing change (if any)
|
|
288
|
+
* @returns true if the incoming change should be applied
|
|
289
|
+
*/
|
|
290
|
+
export function shouldApplySchemaChangeByOperation(incomingOp, incomingHLC, existingOp, existingHLC) {
|
|
291
|
+
// No existing change, apply the incoming one
|
|
292
|
+
if (!existingOp || !existingHLC) {
|
|
293
|
+
return true;
|
|
294
|
+
}
|
|
295
|
+
const incomingDestructiveness = getOperationDestructiveness(incomingOp);
|
|
296
|
+
const existingDestructiveness = getOperationDestructiveness(existingOp);
|
|
297
|
+
// More destructive wins
|
|
298
|
+
if (incomingDestructiveness > existingDestructiveness) {
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
if (incomingDestructiveness < existingDestructiveness) {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
// Same destructiveness: LWW (higher HLC wins)
|
|
305
|
+
return compareHLC(incomingHLC, existingHLC) > 0;
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=schema-version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-version.js","sourceRoot":"","sources":["../../../src/metadata/schema-version.ts"],"names":[],"mappings":"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"}
|