@smart-data-engines/sde 0.1.0-dev.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/LICENSE +201 -0
- package/NOTICE +13 -0
- package/README.md +153 -0
- package/bin/weather.mjs +32 -0
- package/dist/_usage.d.ts +30 -0
- package/dist/_usage.js +194 -0
- package/dist/_usage.js.map +1 -0
- package/dist/bulk.d.ts +9 -0
- package/dist/bulk.js +83 -0
- package/dist/bulk.js.map +1 -0
- package/dist/canonical.d.ts +46 -0
- package/dist/canonical.js +150 -0
- package/dist/canonical.js.map +1 -0
- package/dist/capabilities.d.ts +48 -0
- package/dist/capabilities.js +62 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/cutover.d.ts +36 -0
- package/dist/cutover.js +219 -0
- package/dist/cutover.js.map +1 -0
- package/dist/demo/model.d.ts +28 -0
- package/dist/demo/model.js +40 -0
- package/dist/demo/model.js.map +1 -0
- package/dist/demo/project.d.ts +19 -0
- package/dist/demo/project.js +128 -0
- package/dist/demo/project.js.map +1 -0
- package/dist/demo/weather.d.ts +73 -0
- package/dist/demo/weather.js +334 -0
- package/dist/demo/weather.js.map +1 -0
- package/dist/engines/_clickhouse-connection.d.ts +17 -0
- package/dist/engines/_clickhouse-connection.js +182 -0
- package/dist/engines/_clickhouse-connection.js.map +1 -0
- package/dist/engines/_tls-peer-identity.d.ts +2 -0
- package/dist/engines/_tls-peer-identity.js +23 -0
- package/dist/engines/_tls-peer-identity.js.map +1 -0
- package/dist/engines/_write-fences.d.ts +51 -0
- package/dist/engines/_write-fences.js +189 -0
- package/dist/engines/_write-fences.js.map +1 -0
- package/dist/engines/clickhouse.d.ts +193 -0
- package/dist/engines/clickhouse.js +899 -0
- package/dist/engines/clickhouse.js.map +1 -0
- package/dist/engines/postgres.d.ts +293 -0
- package/dist/engines/postgres.js +981 -0
- package/dist/engines/postgres.js.map +1 -0
- package/dist/errors.d.ts +89 -0
- package/dist/errors.js +90 -0
- package/dist/errors.js.map +1 -0
- package/dist/frozen-verification.d.ts +26 -0
- package/dist/frozen-verification.js +67 -0
- package/dist/frozen-verification.js.map +1 -0
- package/dist/generation.d.ts +34 -0
- package/dist/generation.js +81 -0
- package/dist/generation.js.map +1 -0
- package/dist/groups.d.ts +17 -0
- package/dist/groups.js +66 -0
- package/dist/groups.js.map +1 -0
- package/dist/hashing.d.ts +68 -0
- package/dist/hashing.js +146 -0
- package/dist/hashing.js.map +1 -0
- package/dist/in-place-index.d.ts +43 -0
- package/dist/in-place-index.js +272 -0
- package/dist/in-place-index.js.map +1 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/inspection.d.ts +19 -0
- package/dist/inspection.js +31 -0
- package/dist/inspection.js.map +1 -0
- package/dist/internal.d.ts +42 -0
- package/dist/internal.js +56 -0
- package/dist/internal.js.map +1 -0
- package/dist/layout.d.ts +36 -0
- package/dist/layout.js +62 -0
- package/dist/layout.js.map +1 -0
- package/dist/migration.d.ts +197 -0
- package/dist/migration.js +592 -0
- package/dist/migration.js.map +1 -0
- package/dist/model.d.ts +93 -0
- package/dist/model.js +313 -0
- package/dist/model.js.map +1 -0
- package/dist/physical.d.ts +128 -0
- package/dist/physical.js +421 -0
- package/dist/physical.js.map +1 -0
- package/dist/placement.d.ts +157 -0
- package/dist/placement.js +651 -0
- package/dist/placement.js.map +1 -0
- package/dist/provisioning.d.ts +6 -0
- package/dist/provisioning.js +45 -0
- package/dist/provisioning.js.map +1 -0
- package/dist/query.d.ts +68 -0
- package/dist/query.js +340 -0
- package/dist/query.js.map +1 -0
- package/dist/routing.d.ts +25 -0
- package/dist/routing.js +35 -0
- package/dist/routing.js.map +1 -0
- package/dist/schema.d.ts +110 -0
- package/dist/schema.js +337 -0
- package/dist/schema.js.map +1 -0
- package/dist/session.d.ts +195 -0
- package/dist/session.js +870 -0
- package/dist/session.js.map +1 -0
- package/dist/shapes.d.ts +30 -0
- package/dist/shapes.js +112 -0
- package/dist/shapes.js.map +1 -0
- package/dist/staging.d.ts +29 -0
- package/dist/staging.js +214 -0
- package/dist/staging.js.map +1 -0
- package/dist/telemetry.d.ts +468 -0
- package/dist/telemetry.js +872 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/testing/loader.d.ts +38 -0
- package/dist/testing/loader.js +86 -0
- package/dist/testing/loader.js.map +1 -0
- package/dist/testing/memory.d.ts +131 -0
- package/dist/testing/memory.js +311 -0
- package/dist/testing/memory.js.map +1 -0
- package/dist/timestamp.d.ts +20 -0
- package/dist/timestamp.js +89 -0
- package/dist/timestamp.js.map +1 -0
- package/dist/types.d.ts +79 -0
- package/dist/types.js +100 -0
- package/dist/types.js.map +1 -0
- package/dist/verification.d.ts +41 -0
- package/dist/verification.js +169 -0
- package/dist/verification.js.map +1 -0
- package/dist/watermark.d.ts +103 -0
- package/dist/watermark.js +170 -0
- package/dist/watermark.js.map +1 -0
- package/dist/write-fence.d.ts +58 -0
- package/dist/write-fence.js +225 -0
- package/dist/write-fence.js.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hashed identifiers - a port of section 2a of the format contract, not of the Python file.
|
|
3
|
+
*
|
|
4
|
+
* The distinction is the same one `canonical.ts` makes, and it earns its keep the same way. What is
|
|
5
|
+
* being pinned here is not the HMAC; anything computes an HMAC. It is the *message*: NFC-normalised
|
|
6
|
+
* parts, joined with U+0000, prefix outside, fields hashed together with their entity. Every one of
|
|
7
|
+
* those four is invisible in a test whose identifiers are ASCII, and three of them are things a
|
|
8
|
+
* translator would plausibly get wrong while every existing test stayed green.
|
|
9
|
+
*
|
|
10
|
+
* The NFC rule is here because the reference implementation got it wrong first. Section 1 normalises
|
|
11
|
+
* before emitting bytes, which is why two libraries that declare an identifier in different normal
|
|
12
|
+
* forms compute the same `model_version`. Hashing the raw name threw that away: two digests, two
|
|
13
|
+
* versions, and a placement map issued for the Python service that this one would refuse. It passed
|
|
14
|
+
* every test in both languages, because every other identifier in every other test is ASCII, where
|
|
15
|
+
* the two forms coincide. `conformance/vectors/hashing/002-normalisation` exists so that neither
|
|
16
|
+
* implementation can lose it again.
|
|
17
|
+
*
|
|
18
|
+
* Two JavaScript-specific notes, and the first one is a correctness bug this file had.
|
|
19
|
+
*
|
|
20
|
+
* **Every name-keyed object here is `Object.create(null)`, never `{}`.** A plain object inherits
|
|
21
|
+
* `Object.prototype`, so a client entity or field named `__proto__` does not get an own property:
|
|
22
|
+
* assigning a string to `__proto__` is silently a no-op, and reading it back yields the prototype
|
|
23
|
+
* object rather than a digest. Worse, `relationNames[source] ?? {}` would then hand back
|
|
24
|
+
* `Object.prototype` itself - truthy, so the `??` does not fire - and the next line writes a property
|
|
25
|
+
* onto it, polluting every object in the process. Python's `dict` has none of this, so the same model
|
|
26
|
+
* hashed in both languages produced a correct result in one and a corrupt one in the other: the byte
|
|
27
|
+
* contract's failure mode arriving through the container rather than the encoder.
|
|
28
|
+
* `conformance/vectors/hashing/003-reserved-object-keys` exists so neither implementation can lose it,
|
|
29
|
+
* and `constructor` is in that vector alongside `__proto__` because it is a name a real model might
|
|
30
|
+
* plausibly use.
|
|
31
|
+
*
|
|
32
|
+
* The second note is about ordering. It matters twice below - members within an atomicity group,
|
|
33
|
+
* and the groups among themselves - and both use `compareCodePoints` rather than the default
|
|
34
|
+
* comparator. Hashed names are `e_` plus hex, so UTF-16 and code point order agree on them today and
|
|
35
|
+
* the default sort would pass every vector. It is still wrong, for the same reason it is wrong in
|
|
36
|
+
* `canonical.ts`: the day a prefix or a digest alphabet changes, a silent divergence is worse than a
|
|
37
|
+
* loud one.
|
|
38
|
+
*/
|
|
39
|
+
import type { LogicalModel } from './model.js';
|
|
40
|
+
/**
|
|
41
|
+
* Twelve hex characters, i.e. 48 bits. Half a `model_version`, because these end up in table names.
|
|
42
|
+
* A collision would merge two entities into one, so it is checked and refused rather than assumed.
|
|
43
|
+
*/
|
|
44
|
+
export declare const DIGEST_CHARS = 12;
|
|
45
|
+
/**
|
|
46
|
+
* The translation between what the client wrote and what we see.
|
|
47
|
+
*
|
|
48
|
+
* Held only in the client's process. The library needs it because the placement map is keyed by
|
|
49
|
+
* hashed names while the application still says `session.get('User', ...)`, so every lookup crosses
|
|
50
|
+
* this boundary and this is the only thing that does.
|
|
51
|
+
*/
|
|
52
|
+
export interface NameMap {
|
|
53
|
+
readonly entities: Readonly<Record<string, string>>;
|
|
54
|
+
readonly fields: Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
55
|
+
readonly relations: Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Return an equivalent model with every identifier replaced by a keyed digest.
|
|
59
|
+
*
|
|
60
|
+
* What is deliberately not hashed: `residency`, because a jurisdiction is not an identifier and a
|
|
61
|
+
* hashed placement constraint is an unenforceable one; the cost ceiling, because it is a number and
|
|
62
|
+
* a currency; and types, keys and nullability, because the physical schema is derived from them and
|
|
63
|
+
* none of them is a name.
|
|
64
|
+
*/
|
|
65
|
+
export declare function hashIdentifiers(model: LogicalModel, salt: Buffer): {
|
|
66
|
+
readonly model: LogicalModel;
|
|
67
|
+
readonly names: NameMap;
|
|
68
|
+
};
|
package/dist/hashing.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hashed identifiers - a port of section 2a of the format contract, not of the Python file.
|
|
3
|
+
*
|
|
4
|
+
* The distinction is the same one `canonical.ts` makes, and it earns its keep the same way. What is
|
|
5
|
+
* being pinned here is not the HMAC; anything computes an HMAC. It is the *message*: NFC-normalised
|
|
6
|
+
* parts, joined with U+0000, prefix outside, fields hashed together with their entity. Every one of
|
|
7
|
+
* those four is invisible in a test whose identifiers are ASCII, and three of them are things a
|
|
8
|
+
* translator would plausibly get wrong while every existing test stayed green.
|
|
9
|
+
*
|
|
10
|
+
* The NFC rule is here because the reference implementation got it wrong first. Section 1 normalises
|
|
11
|
+
* before emitting bytes, which is why two libraries that declare an identifier in different normal
|
|
12
|
+
* forms compute the same `model_version`. Hashing the raw name threw that away: two digests, two
|
|
13
|
+
* versions, and a placement map issued for the Python service that this one would refuse. It passed
|
|
14
|
+
* every test in both languages, because every other identifier in every other test is ASCII, where
|
|
15
|
+
* the two forms coincide. `conformance/vectors/hashing/002-normalisation` exists so that neither
|
|
16
|
+
* implementation can lose it again.
|
|
17
|
+
*
|
|
18
|
+
* Two JavaScript-specific notes, and the first one is a correctness bug this file had.
|
|
19
|
+
*
|
|
20
|
+
* **Every name-keyed object here is `Object.create(null)`, never `{}`.** A plain object inherits
|
|
21
|
+
* `Object.prototype`, so a client entity or field named `__proto__` does not get an own property:
|
|
22
|
+
* assigning a string to `__proto__` is silently a no-op, and reading it back yields the prototype
|
|
23
|
+
* object rather than a digest. Worse, `relationNames[source] ?? {}` would then hand back
|
|
24
|
+
* `Object.prototype` itself - truthy, so the `??` does not fire - and the next line writes a property
|
|
25
|
+
* onto it, polluting every object in the process. Python's `dict` has none of this, so the same model
|
|
26
|
+
* hashed in both languages produced a correct result in one and a corrupt one in the other: the byte
|
|
27
|
+
* contract's failure mode arriving through the container rather than the encoder.
|
|
28
|
+
* `conformance/vectors/hashing/003-reserved-object-keys` exists so neither implementation can lose it,
|
|
29
|
+
* and `constructor` is in that vector alongside `__proto__` because it is a name a real model might
|
|
30
|
+
* plausibly use.
|
|
31
|
+
*
|
|
32
|
+
* The second note is about ordering. It matters twice below - members within an atomicity group,
|
|
33
|
+
* and the groups among themselves - and both use `compareCodePoints` rather than the default
|
|
34
|
+
* comparator. Hashed names are `e_` plus hex, so UTF-16 and code point order agree on them today and
|
|
35
|
+
* the default sort would pass every vector. It is still wrong, for the same reason it is wrong in
|
|
36
|
+
* `canonical.ts`: the day a prefix or a digest alphabet changes, a silent divergence is worse than a
|
|
37
|
+
* loud one.
|
|
38
|
+
*/
|
|
39
|
+
import { createHmac } from 'node:crypto';
|
|
40
|
+
import { compareCodePoints } from './canonical.js';
|
|
41
|
+
import { DeclarationError } from './errors.js';
|
|
42
|
+
import { assemble } from './model.js';
|
|
43
|
+
/**
|
|
44
|
+
* Twelve hex characters, i.e. 48 bits. Half a `model_version`, because these end up in table names.
|
|
45
|
+
* A collision would merge two entities into one, so it is checked and refused rather than assumed.
|
|
46
|
+
*/
|
|
47
|
+
export const DIGEST_CHARS = 12;
|
|
48
|
+
const ENTITY_PREFIX = 'e_';
|
|
49
|
+
const FIELD_PREFIX = 'f_';
|
|
50
|
+
const RELATION_PREFIX = 'r_';
|
|
51
|
+
/**
|
|
52
|
+
* U+0000, which cannot occur in an identifier - so the join is unambiguous. Written as a code point
|
|
53
|
+
* rather than an escape in a string literal, because a raw NUL in a source file survives a git diff,
|
|
54
|
+
* a code review and a copy-paste in ways nobody intends.
|
|
55
|
+
*/
|
|
56
|
+
const SEPARATOR = String.fromCodePoint(0);
|
|
57
|
+
/** The shortest salt this library will accept. A guessable salt hides nothing. */
|
|
58
|
+
const MIN_SALT_BYTES = 16;
|
|
59
|
+
function digest(salt, prefix, parts) {
|
|
60
|
+
const message = parts.map((part) => part.normalize('NFC')).join(SEPARATOR);
|
|
61
|
+
const hex = createHmac('sha256', salt).update(message, 'utf8').digest('hex');
|
|
62
|
+
return prefix + hex.slice(0, DIGEST_CHARS);
|
|
63
|
+
}
|
|
64
|
+
/** Lexicographic over two already-sorted member lists, by code point. */
|
|
65
|
+
function compareGroups(left, right) {
|
|
66
|
+
for (let index = 0; index < Math.min(left.length, right.length); index += 1) {
|
|
67
|
+
const order = compareCodePoints(left[index], right[index]);
|
|
68
|
+
if (order !== 0)
|
|
69
|
+
return order;
|
|
70
|
+
}
|
|
71
|
+
return left.length - right.length;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Return an equivalent model with every identifier replaced by a keyed digest.
|
|
75
|
+
*
|
|
76
|
+
* What is deliberately not hashed: `residency`, because a jurisdiction is not an identifier and a
|
|
77
|
+
* hashed placement constraint is an unenforceable one; the cost ceiling, because it is a number and
|
|
78
|
+
* a currency; and types, keys and nullability, because the physical schema is derived from them and
|
|
79
|
+
* none of them is a name.
|
|
80
|
+
*/
|
|
81
|
+
export function hashIdentifiers(model, salt) {
|
|
82
|
+
if (salt.length < MIN_SALT_BYTES) {
|
|
83
|
+
throw new DeclarationError(`the salt must be at least ${MIN_SALT_BYTES} bytes`);
|
|
84
|
+
}
|
|
85
|
+
// Object.create(null), not {}, and this is a correctness rule rather than a style one - see the
|
|
86
|
+
// note about reserved keys at the top of this file.
|
|
87
|
+
const entityNames = Object.create(null);
|
|
88
|
+
const seen = new Map();
|
|
89
|
+
for (const spec of model.entities) {
|
|
90
|
+
const hashed = digest(salt, ENTITY_PREFIX, [spec.name]);
|
|
91
|
+
const clash = seen.get(hashed);
|
|
92
|
+
if (clash !== undefined) {
|
|
93
|
+
throw new DeclarationError(`${spec.name} and ${clash} hash to the same name. Refused rather than merged: a model ` +
|
|
94
|
+
'with one entity where there were two would place both in one engine and write both ' +
|
|
95
|
+
'into one table. Change the salt.');
|
|
96
|
+
}
|
|
97
|
+
seen.set(hashed, spec.name);
|
|
98
|
+
entityNames[spec.name] = hashed;
|
|
99
|
+
}
|
|
100
|
+
const fieldNames = Object.create(null);
|
|
101
|
+
const relationNames = Object.create(null);
|
|
102
|
+
const entities = [];
|
|
103
|
+
for (const spec of model.entities) {
|
|
104
|
+
const mapping = Object.create(null);
|
|
105
|
+
for (const field of spec.fields) {
|
|
106
|
+
mapping[field.name] = digest(salt, FIELD_PREFIX, [spec.name, field.name]);
|
|
107
|
+
}
|
|
108
|
+
if (new Set(Object.values(mapping)).size !== Object.keys(mapping).length) {
|
|
109
|
+
throw new DeclarationError(`two fields of ${spec.name} hash to the same name. Refused rather than merged. ` +
|
|
110
|
+
'Change the salt.');
|
|
111
|
+
}
|
|
112
|
+
fieldNames[spec.name] = mapping;
|
|
113
|
+
entities.push({
|
|
114
|
+
name: entityNames[spec.name],
|
|
115
|
+
fields: spec.fields.map((field) => ({
|
|
116
|
+
name: mapping[field.name],
|
|
117
|
+
type: field.type,
|
|
118
|
+
nullable: field.nullable,
|
|
119
|
+
})),
|
|
120
|
+
key: spec.key.map((field) => mapping[field]),
|
|
121
|
+
pii: spec.pii.map((field) => mapping[field]),
|
|
122
|
+
residency: spec.residency,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const relations = [];
|
|
126
|
+
for (const relation of model.relations) {
|
|
127
|
+
const hashed = digest(salt, RELATION_PREFIX, [relation.source, relation.name]);
|
|
128
|
+
const bucket = relationNames[relation.source] ?? Object.create(null);
|
|
129
|
+
bucket[relation.name] = hashed;
|
|
130
|
+
relationNames[relation.source] = bucket;
|
|
131
|
+
relations.push({
|
|
132
|
+
name: hashed,
|
|
133
|
+
source: entityNames[relation.source],
|
|
134
|
+
target: entityNames[relation.target],
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const atomic = model.atomic
|
|
138
|
+
.map((group) => group.map((member) => entityNames[member]).sort(compareCodePoints))
|
|
139
|
+
.sort(compareGroups);
|
|
140
|
+
const costCeiling = model.costCeiling;
|
|
141
|
+
return {
|
|
142
|
+
model: assemble(entities, relations, atomic, costCeiling),
|
|
143
|
+
names: { entities: entityNames, fields: fieldNames, relations: relationNames },
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=hashing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hashing.js","sourceRoot":"","sources":["../src/hashing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { LogicalModel } from './model.js';
|
|
2
|
+
import { type LoadOptions, type PlacementMap } from './placement.js';
|
|
3
|
+
/** Indexes added to one group's source, built on the tables in force; no copy, no cutover. */
|
|
4
|
+
export declare const INDEX_PROTOCOL = 1;
|
|
5
|
+
/** Indexes added to and removed from one group's source, in place; at least one removed. */
|
|
6
|
+
export declare const INDEX_CHANGE_PROTOCOL = 2;
|
|
7
|
+
/** A day. The budget bounds a build on a server that stopped answering; nothing is paused while
|
|
8
|
+
* it runs, so it is not a pause budget and it is deliberately far longer than a cutover's. */
|
|
9
|
+
export declare const MAX_BUILD_BUDGET_MS = 86400000;
|
|
10
|
+
type PublicKeys = NonNullable<LoadOptions['publicKey']>;
|
|
11
|
+
type Index = Readonly<Record<string, unknown>>;
|
|
12
|
+
/** The physical name of the `position`-th index an in-place build adds (one-based). */
|
|
13
|
+
export declare function indexBuildName(indexId: string, position: number): string;
|
|
14
|
+
export declare class IndexPlan {
|
|
15
|
+
readonly indexId: string;
|
|
16
|
+
readonly projectId: string;
|
|
17
|
+
readonly group: string;
|
|
18
|
+
readonly current: PlacementMap;
|
|
19
|
+
readonly prepared: PlacementMap;
|
|
20
|
+
readonly buildBudgetMs: number;
|
|
21
|
+
/** The new index definitions, in position order, exactly as the prepared map carries them. */
|
|
22
|
+
readonly added: readonly Index[];
|
|
23
|
+
readonly verifiedWith: string | null;
|
|
24
|
+
/** Protocol 2: the definitions in force the next map drops, in their order in force. */
|
|
25
|
+
readonly removed: readonly Index[];
|
|
26
|
+
constructor(indexId: string, projectId: string, group: string, current: PlacementMap, prepared: PlacementMap, buildBudgetMs: number,
|
|
27
|
+
/** The new index definitions, in position order, exactly as the prepared map carries them. */
|
|
28
|
+
added: readonly Index[], verifiedWith: string | null,
|
|
29
|
+
/** Protocol 2: the definitions in force the next map drops, in their order in force. */
|
|
30
|
+
removed?: readonly Index[]);
|
|
31
|
+
private loaded;
|
|
32
|
+
get fingerprint(): string | undefined;
|
|
33
|
+
get protocol(): number;
|
|
34
|
+
asRecord(): Record<string, unknown>;
|
|
35
|
+
preparedPayload(): Uint8Array;
|
|
36
|
+
checkCurrent(current: PlacementMap): void;
|
|
37
|
+
}
|
|
38
|
+
export declare function loadIndexPlan(raw: unknown, options: {
|
|
39
|
+
model: LogicalModel;
|
|
40
|
+
projectId: string;
|
|
41
|
+
publicKey: PublicKeys;
|
|
42
|
+
}): IndexPlan;
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signed authorization to build indexes on the tables in force, in place.
|
|
3
|
+
*
|
|
4
|
+
* A design that only adds indexes to a group's source used to run as a relayout: a fresh copy with
|
|
5
|
+
* the index, then a cutover that copies and compares every row while the source's writes are
|
|
6
|
+
* frozen - a pause linear in the table. Building the index on the live table moves no row and
|
|
7
|
+
* pauses no write. This authorization binds that build: the exact map in force and the next map,
|
|
8
|
+
* which differs from it only by indexes added to one group's source. The Python library executes
|
|
9
|
+
* it; this loader holds both libraries to one reading of the packet.
|
|
10
|
+
*
|
|
11
|
+
* Protocol 2 also removes indexes the map in force declares: the removed ones leave the next map,
|
|
12
|
+
* the others keep their order, and the new ones follow. The operator removes them after its decision.
|
|
13
|
+
*/
|
|
14
|
+
import { createHash } from 'node:crypto';
|
|
15
|
+
import { CanonicalError, canonicalBytes, compareCodePoints } from './canonical.js';
|
|
16
|
+
import { MapError, MigrationRefused } from './errors.js';
|
|
17
|
+
import { checkMapProject, GENERATIONS_SINCE } from './generation.js';
|
|
18
|
+
import { fingerprintOf, loadMap, verifyMapSignature } from './placement.js';
|
|
19
|
+
/** Indexes added to one group's source, built on the tables in force; no copy, no cutover. */
|
|
20
|
+
export const INDEX_PROTOCOL = 1;
|
|
21
|
+
/** Indexes added to and removed from one group's source, in place; at least one removed. */
|
|
22
|
+
export const INDEX_CHANGE_PROTOCOL = 2;
|
|
23
|
+
/** A day. The budget bounds a build on a server that stopped answering; nothing is paused while
|
|
24
|
+
* it runs, so it is not a pause budget and it is deliberately far longer than a cutover's. */
|
|
25
|
+
export const MAX_BUILD_BUDGET_MS = 86_400_000;
|
|
26
|
+
const FIELDS = ['kind', 'protocol', 'index_id', 'project_id', 'group', 'current', 'prepared', 'build_budget_ms', 'signature']
|
|
27
|
+
.sort(compareCodePoints);
|
|
28
|
+
const provenance = new WeakMap();
|
|
29
|
+
function record(value, name) {
|
|
30
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
31
|
+
throw new MigrationRefused(`index build ${name} must be an object`);
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
function hex(value, width, name) {
|
|
36
|
+
if (typeof value !== 'string' || !new RegExp(`^[0-9a-f]{${width}}$`).test(value)) {
|
|
37
|
+
throw new MigrationRefused(`index build ${name} must be ${width} lowercase hexadecimal digits`);
|
|
38
|
+
}
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
function equal(left, right) {
|
|
42
|
+
return canonicalBytes(left).equals(canonicalBytes(right));
|
|
43
|
+
}
|
|
44
|
+
function sortedKeys(value) {
|
|
45
|
+
return Object.keys(value).sort(compareCodePoints);
|
|
46
|
+
}
|
|
47
|
+
function signature(document) {
|
|
48
|
+
const value = record(document['signature'], 'signature');
|
|
49
|
+
const names = sortedKeys(value);
|
|
50
|
+
if (!equal(names, ['alg', 'value']) && !equal(names, ['alg', 'key_id', 'value'])) {
|
|
51
|
+
throw new MigrationRefused('index build signatures have missing or unknown fields');
|
|
52
|
+
}
|
|
53
|
+
const malformed = 'index build signatures must use ed25519 and canonical base64';
|
|
54
|
+
if (value['alg'] !== 'ed25519' || typeof value['value'] !== 'string')
|
|
55
|
+
throw new MigrationRefused(malformed);
|
|
56
|
+
const decoded = Buffer.from(value['value'], 'base64');
|
|
57
|
+
if (decoded.length !== 64 || decoded.toString('base64') !== value['value'])
|
|
58
|
+
throw new MigrationRefused(malformed);
|
|
59
|
+
if ('key_id' in value && typeof value['key_id'] !== 'string') {
|
|
60
|
+
throw new MigrationRefused('index build signature key_id must be a string');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function except(value, keys) {
|
|
64
|
+
return Object.fromEntries(Object.entries(value).filter(([name]) => !keys.includes(name)));
|
|
65
|
+
}
|
|
66
|
+
function withoutIndexes(material) {
|
|
67
|
+
return { ...material, layout: except(record(material['layout'], 'layout'), ['indexes']) };
|
|
68
|
+
}
|
|
69
|
+
function indexesOf(material) {
|
|
70
|
+
const value = record(material['layout'], 'layout')['indexes'];
|
|
71
|
+
return Array.isArray(value) ? value : [];
|
|
72
|
+
}
|
|
73
|
+
/** Every table and index name any layout of a map document uses. */
|
|
74
|
+
function names(document) {
|
|
75
|
+
const found = new Set();
|
|
76
|
+
for (const rawGroup of Object.values(record(document['groups'], 'groups'))) {
|
|
77
|
+
const value = record(rawGroup, 'group');
|
|
78
|
+
const copies = Array.isArray(value['derived']) ? value['derived'] : [];
|
|
79
|
+
for (const material of [value['source'], ...copies]) {
|
|
80
|
+
const layout = record(record(material, 'materialization')['layout'], 'layout');
|
|
81
|
+
for (const table of Object.values(record(layout['tables'] ?? {}, 'tables')))
|
|
82
|
+
found.add(String(table));
|
|
83
|
+
for (const index of indexesOf(record(material, 'materialization')))
|
|
84
|
+
found.add(String(record(index, 'index')['name']));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return found;
|
|
88
|
+
}
|
|
89
|
+
/** The physical name of the `position`-th index an in-place build adds (one-based). */
|
|
90
|
+
export function indexBuildName(indexId, position) {
|
|
91
|
+
hex(indexId, 32, 'index_id');
|
|
92
|
+
if (!Number.isInteger(position) || position < 1 || position > 999999) {
|
|
93
|
+
throw new MigrationRefused('index build position must be an integer from 1 through 999999');
|
|
94
|
+
}
|
|
95
|
+
return `sde_i_${indexId}_${String(position).padStart(6, '0')}`;
|
|
96
|
+
}
|
|
97
|
+
export class IndexPlan {
|
|
98
|
+
indexId;
|
|
99
|
+
projectId;
|
|
100
|
+
group;
|
|
101
|
+
current;
|
|
102
|
+
prepared;
|
|
103
|
+
buildBudgetMs;
|
|
104
|
+
added;
|
|
105
|
+
verifiedWith;
|
|
106
|
+
removed;
|
|
107
|
+
constructor(indexId, projectId, group, current, prepared, buildBudgetMs,
|
|
108
|
+
/** The new index definitions, in position order, exactly as the prepared map carries them. */
|
|
109
|
+
added, verifiedWith,
|
|
110
|
+
/** Protocol 2: the definitions in force the next map drops, in their order in force. */
|
|
111
|
+
removed = []) {
|
|
112
|
+
this.indexId = indexId;
|
|
113
|
+
this.projectId = projectId;
|
|
114
|
+
this.group = group;
|
|
115
|
+
this.current = current;
|
|
116
|
+
this.prepared = prepared;
|
|
117
|
+
this.buildBudgetMs = buildBudgetMs;
|
|
118
|
+
this.added = added;
|
|
119
|
+
this.verifiedWith = verifiedWith;
|
|
120
|
+
this.removed = removed;
|
|
121
|
+
Object.freeze(this);
|
|
122
|
+
}
|
|
123
|
+
loaded() {
|
|
124
|
+
const saved = provenance.get(this);
|
|
125
|
+
if (saved === undefined)
|
|
126
|
+
throw new MigrationRefused('an index build requires an immutable loaded authorization');
|
|
127
|
+
return saved;
|
|
128
|
+
}
|
|
129
|
+
get fingerprint() { return provenance.get(this)?.fingerprint; }
|
|
130
|
+
get protocol() { return this.asRecord()['protocol']; }
|
|
131
|
+
asRecord() { return JSON.parse(this.loaded().document); }
|
|
132
|
+
preparedPayload() { return canonicalBytes(this.asRecord()['prepared']); }
|
|
133
|
+
checkCurrent(current) {
|
|
134
|
+
this.loaded();
|
|
135
|
+
checkMapProject(current, this.projectId);
|
|
136
|
+
if (!current.signed || fingerprintOf(current) !== fingerprintOf(this.current)) {
|
|
137
|
+
throw new MigrationRefused('index build authorization does not name the signed current map');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function load(raw, model, projectId, publicKey) {
|
|
142
|
+
const body = record(structuredClone(raw), 'authorization');
|
|
143
|
+
if (!equal(sortedKeys(body), FIELDS))
|
|
144
|
+
throw new MigrationRefused('index build authorization has missing or unknown fields');
|
|
145
|
+
if (typeof body['protocol'] !== 'number' || ![INDEX_PROTOCOL, INDEX_CHANGE_PROTOCOL].includes(body['protocol']) ||
|
|
146
|
+
body['kind'] !== 'sde-index') {
|
|
147
|
+
throw new MigrationRefused('unsupported index build authorization kind or protocol');
|
|
148
|
+
}
|
|
149
|
+
const protocol = body['protocol'];
|
|
150
|
+
const identity = hex(body['index_id'], 32, 'index_id'), local = hex(body['project_id'], 32, 'project_id');
|
|
151
|
+
if (local !== projectId)
|
|
152
|
+
throw new MigrationRefused('index build authorization belongs to another local project');
|
|
153
|
+
const group = body['group'];
|
|
154
|
+
if (typeof group !== 'string' || group.length === 0)
|
|
155
|
+
throw new MigrationRefused('index build group must be a nonempty string');
|
|
156
|
+
const budget = body['build_budget_ms'];
|
|
157
|
+
if (typeof budget !== 'number' || !Number.isInteger(budget) || budget < 1 || budget > MAX_BUILD_BUDGET_MS) {
|
|
158
|
+
throw new MigrationRefused(`index build budget must be an integer from 1 through ${MAX_BUILD_BUDGET_MS} ms`);
|
|
159
|
+
}
|
|
160
|
+
signature(body);
|
|
161
|
+
const verified = verifyMapSignature(body, publicKey);
|
|
162
|
+
const maps = [];
|
|
163
|
+
for (const name of ['current', 'prepared']) {
|
|
164
|
+
const document = record(body[name], name);
|
|
165
|
+
signature(document);
|
|
166
|
+
for (const rawGroup of Object.values(record(document['groups'], 'groups'))) {
|
|
167
|
+
const value = record(rawGroup, 'group'), copies = value['derived'] === undefined ? [] : value['derived'];
|
|
168
|
+
if (!Array.isArray(copies))
|
|
169
|
+
throw new MigrationRefused('index build derived copies must be an array');
|
|
170
|
+
for (const rawMaterial of [value['source'], ...copies]) {
|
|
171
|
+
const layout = record(record(rawMaterial, 'materialization')['layout'], 'layout');
|
|
172
|
+
if (layout['auto'])
|
|
173
|
+
throw new MigrationRefused('index build maps need explicit physical layouts');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const parsed = loadMap(document, { model, publicKey, requireSignature: true });
|
|
177
|
+
if (parsed.contract < GENERATIONS_SINCE) {
|
|
178
|
+
throw new MigrationRefused(`index build protocol ${protocol} requires map contract ${GENERATIONS_SINCE} or later`);
|
|
179
|
+
}
|
|
180
|
+
checkMapProject(parsed, projectId);
|
|
181
|
+
for (const placed of Object.values(parsed.groups)) {
|
|
182
|
+
for (const material of [placed.source, ...placed.derived]) {
|
|
183
|
+
if (Object.keys(material.layout.tables).length === 0 || Object.keys(material.layout.columns).length === 0) {
|
|
184
|
+
throw new MigrationRefused('index build maps need explicit physical layouts');
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
maps.push(parsed);
|
|
189
|
+
}
|
|
190
|
+
const current = maps[0], prepared = maps[1];
|
|
191
|
+
// The prepared map may raise the contract, because an index method first appears here - a BRIN
|
|
192
|
+
// index under a contract-4 current map. It may not lower it: a lower number would tell an older
|
|
193
|
+
// library it may ignore keys that the current map already relies on.
|
|
194
|
+
if (prepared.contract < current.contract)
|
|
195
|
+
throw new MigrationRefused('an index build cannot lower the placement map contract');
|
|
196
|
+
if (current.mapVersion >= prepared.mapVersion)
|
|
197
|
+
throw new MigrationRefused('an index build must allocate a newer prepared map');
|
|
198
|
+
if (!(group in current.groups) || !equal(Object.keys(current.groups).sort(compareCodePoints), Object.keys(prepared.groups).sort(compareCodePoints))) {
|
|
199
|
+
throw new MigrationRefused('an index build cannot add or remove colocation groups');
|
|
200
|
+
}
|
|
201
|
+
const old = current.groups[group], next = prepared.groups[group];
|
|
202
|
+
if (old.derived.length !== 0 || old.alsoWrite.length !== 0 || next.derived.length !== 0 || next.alsoWrite.length !== 0) {
|
|
203
|
+
throw new MigrationRefused('an index build begins and ends with a source-only group');
|
|
204
|
+
}
|
|
205
|
+
// Nothing is fenced: the tables stay and every running process keeps writing to them.
|
|
206
|
+
if (next.writeEpoch !== old.writeEpoch)
|
|
207
|
+
throw new MigrationRefused("an index build keeps the source's write generation");
|
|
208
|
+
const currentRaw = record(body['current'], 'current'), preparedRaw = record(body['prepared'], 'prepared');
|
|
209
|
+
const oldGroups = record(currentRaw['groups'], 'groups'), newGroups = record(preparedRaw['groups'], 'groups');
|
|
210
|
+
const oldGroup = record(oldGroups[group], 'group'), newGroup = record(newGroups[group], 'group');
|
|
211
|
+
if (!equal(sortedKeys(oldGroup), ['source', 'write_epoch']) || !equal(sortedKeys(newGroup), ['source', 'write_epoch'])) {
|
|
212
|
+
throw new MigrationRefused('an index build group is a source and its write generation only');
|
|
213
|
+
}
|
|
214
|
+
const oldSource = record(oldGroup['source'], 'source'), newSource = record(newGroup['source'], 'source');
|
|
215
|
+
if (!equal(withoutIndexes(oldSource), withoutIndexes(newSource))) {
|
|
216
|
+
throw new MigrationRefused('an index build changes nothing about the source but its indexes; a new key order, ' +
|
|
217
|
+
'partition, table or engine is a relayout or a move');
|
|
218
|
+
}
|
|
219
|
+
const inForce = indexesOf(oldSource), after = indexesOf(newSource);
|
|
220
|
+
const nameOf = (index) => String(record(index, 'index')['name']);
|
|
221
|
+
let kept = inForce, removed = [];
|
|
222
|
+
if (protocol === INDEX_PROTOCOL) {
|
|
223
|
+
if (after.length <= kept.length || !equal(after.slice(0, kept.length), kept)) {
|
|
224
|
+
throw new MigrationRefused('an index build keeps every index in force, in order, and adds at least one after them');
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
const remaining = new Set(after.map(nameOf));
|
|
229
|
+
kept = inForce.filter(index => remaining.has(nameOf(index)));
|
|
230
|
+
removed = inForce.filter(index => !remaining.has(nameOf(index)));
|
|
231
|
+
if (removed.length === 0)
|
|
232
|
+
throw new MigrationRefused('index build protocol 2 removes at least one index in force');
|
|
233
|
+
if (!equal(after.slice(0, kept.length), kept)) {
|
|
234
|
+
throw new MigrationRefused('an index change keeps the other indexes in force, in order, before the new ones');
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const added = after.slice(kept.length);
|
|
238
|
+
added.forEach((index, offset) => {
|
|
239
|
+
if (record(index, 'index')['name'] !== indexBuildName(identity, offset + 1)) {
|
|
240
|
+
throw new MigrationRefused('new indexes need fresh names bound to the index build id and their position');
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
const used = names(currentRaw);
|
|
244
|
+
if (added.some(index => used.has(String(record(index, 'index')['name'])))) {
|
|
245
|
+
throw new MigrationRefused('an index build cannot reuse a name the current map uses');
|
|
246
|
+
}
|
|
247
|
+
const ignored = ['signature', 'map_version', 'groups', 'contract'];
|
|
248
|
+
if (!equal(except(currentRaw, ignored), except(preparedRaw, ignored)) || !equal(current.routing, prepared.routing)) {
|
|
249
|
+
throw new MigrationRefused('an index build cannot change routing or other map attributes');
|
|
250
|
+
}
|
|
251
|
+
for (const other of Object.keys(current.groups)) {
|
|
252
|
+
if (other !== group && !equal(oldGroups[other], newGroups[other]))
|
|
253
|
+
throw new MigrationRefused('an index build cannot change an unaffected group');
|
|
254
|
+
}
|
|
255
|
+
// From the loaded map, which freezes nested structures, not from the caller's objects.
|
|
256
|
+
const gone = new Set(removed.map(nameOf));
|
|
257
|
+
const plan = new IndexPlan(identity, local, group, current, prepared, budget, next.source.layout.indexes.slice(kept.length), verified, old.source.layout.indexes.filter(index => gone.has(String(index['name']))));
|
|
258
|
+
provenance.set(plan, { document: canonicalBytes(body).toString('utf8'),
|
|
259
|
+
fingerprint: createHash('sha256').update(canonicalBytes(except(body, ['signature']))).digest('hex') });
|
|
260
|
+
return plan;
|
|
261
|
+
}
|
|
262
|
+
export function loadIndexPlan(raw, options) {
|
|
263
|
+
try {
|
|
264
|
+
return load(raw, options.model, options.projectId, options.publicKey);
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
if (error instanceof MapError || error instanceof CanonicalError)
|
|
268
|
+
throw new MigrationRefused(`index build authorization refused: ${error.message}`);
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=in-place-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"in-place-index.js","sourceRoot":"","sources":["../src/in-place-index.ts"],"names":[],"mappings":"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"}
|