@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,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Refusing a placement map that goes backwards, and the durable state that makes it possible.
|
|
3
|
+
*
|
|
4
|
+
* A signed map for version 3 verifies correctly forever - that is what a signature is. So replacing
|
|
5
|
+
* the client's map file with an older signed one loads cleanly, routes writes to the previous
|
|
6
|
+
* placement, and **nothing protests**. Today that costs a client a stale schema. Once the migration
|
|
7
|
+
* state travels in the map, it costs them writes: a library reverted from dual-write to
|
|
8
|
+
* single-write in the middle of a migration drops exactly the rows the migration exists not to
|
|
9
|
+
* drop.
|
|
10
|
+
*
|
|
11
|
+
* Refusing it needs one thing a library has never had: **memory**. Everything else here is a pure
|
|
12
|
+
* function of a document, a model and a key, which is why it can be verified by reading it. This
|
|
13
|
+
* module is the exception, and each of the three obvious places to keep that memory is worse than
|
|
14
|
+
* the one chosen:
|
|
15
|
+
*
|
|
16
|
+
* - **in the process** protects until the first restart, and a restart is when a swapped file is
|
|
17
|
+
* read. A protection that lapses exactly when it is needed;
|
|
18
|
+
* - **in a file** needs a configured path, and in a container that path is usually ephemeral - so it
|
|
19
|
+
* degrades to the first option while continuing to look like the third. The worst property
|
|
20
|
+
* available: a guarantee present in the code and absent in production;
|
|
21
|
+
* - **with us** would mean the library asking our service whether it may start, which is the one
|
|
22
|
+
* thing this product promises it will never need to do. Our outage would become the client's.
|
|
23
|
+
*
|
|
24
|
+
* So it lives **in the client's own engines**, in a table this library owns. Four properties, and
|
|
25
|
+
* the first two are what make it safe rather than merely present.
|
|
26
|
+
*
|
|
27
|
+
* **Append-only, and the watermark is `max(map_version)`.** No update, no key enforcement, no
|
|
28
|
+
* row-level contention - and therefore identical semantics in PostgreSQL and in ClickHouse, which is
|
|
29
|
+
* the engine that has no unique constraint to offer. A stale row can never lower the bar.
|
|
30
|
+
*
|
|
31
|
+
* **Every participating engine is written, and the watermark is the maximum over all of them.**
|
|
32
|
+
* Losing an engine cannot lose the protection, and one engine lagging cannot weaken it.
|
|
33
|
+
*
|
|
34
|
+
* **An engine that cannot store it does not participate, and that is reported rather than hidden.**
|
|
35
|
+
* A protection whose state cannot be read is a protection taken on trust.
|
|
36
|
+
*
|
|
37
|
+
* **Only signed maps are checked.** An unsigned map is the client's own document: hand-writing one
|
|
38
|
+
* and pointing a library at it is the no-account mode, and their business what they replace it with.
|
|
39
|
+
* In pure no-account mode this module does nothing at all - no table, no query, no cost.
|
|
40
|
+
*
|
|
41
|
+
* The escape hatch is deliberately not a parameter. A legitimate rollback - we issued a bad map -
|
|
42
|
+
* means clearing the bookkeeping, and the refusal says how. A parameter called `allowRollback` would
|
|
43
|
+
* be set once during an incident and left set.
|
|
44
|
+
*/
|
|
45
|
+
import { compareCodePoints } from './canonical.js';
|
|
46
|
+
import { satisfies, WATERMARK_MEMBERS } from './capabilities.js';
|
|
47
|
+
import { MapRolledBack } from './errors.js';
|
|
48
|
+
import { WATERMARK_TABLE } from './placement.js';
|
|
49
|
+
/**
|
|
50
|
+
* A compile-time ratchet between the interface and the list the runtime check uses.
|
|
51
|
+
*
|
|
52
|
+
* Add a method to `WatermarkStore` and forget `WATERMARK_MEMBERS`, and this stops being assignable.
|
|
53
|
+
* The runtime list cannot be derived from the interface - types are erased - so the compiler holds
|
|
54
|
+
* the other direction instead.
|
|
55
|
+
*/
|
|
56
|
+
export const WATERMARK_STORE_IS_TOTAL = true;
|
|
57
|
+
export function watermarkRecord(check) {
|
|
58
|
+
return {
|
|
59
|
+
protection: check.protection,
|
|
60
|
+
map_version: check.mapVersion,
|
|
61
|
+
highest_seen: check.highestSeen,
|
|
62
|
+
participating: [...check.participating],
|
|
63
|
+
unable: [...check.unable],
|
|
64
|
+
why: check.why,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function split(engines) {
|
|
68
|
+
const able = [];
|
|
69
|
+
const unable = [];
|
|
70
|
+
for (const name of Object.keys(engines).sort(compareCodePoints)) {
|
|
71
|
+
if (satisfies(engines[name], WATERMARK_MEMBERS))
|
|
72
|
+
able.push(name);
|
|
73
|
+
else
|
|
74
|
+
unable.push(name);
|
|
75
|
+
}
|
|
76
|
+
return { able, unable };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Refuse a signed map older than the newest one these engines have seen.
|
|
80
|
+
*
|
|
81
|
+
* Throws {@link MapRolledBack}. Equal is allowed - restarting a process against the same map is the
|
|
82
|
+
* ordinary case - and only strictly lower is refused.
|
|
83
|
+
*
|
|
84
|
+
* Asynchronous because reading a watermark is a query, and split in two inside: this gathers the
|
|
85
|
+
* numbers and {@link decide} makes the decision from them. The decision is therefore a pure
|
|
86
|
+
* function of a document and a set of integers, which is what lets the `migration/` vectors check
|
|
87
|
+
* it, and what lets a reader confirm by reading that nothing here depends on the order the engines
|
|
88
|
+
* answered in.
|
|
89
|
+
*
|
|
90
|
+
* **Nothing is read for an unsigned map.** The no-account mode promises no table, no query and no
|
|
91
|
+
* cost, and this function gathered every watermark and *then* asked whether the map was signed
|
|
92
|
+
* until `migration/001` existed - the right answer, with the promise broken, which is the one shape
|
|
93
|
+
* of defect a record of the decision cannot show.
|
|
94
|
+
*/
|
|
95
|
+
export async function enforceForwardOnly(placement, engines) {
|
|
96
|
+
if (!placement.signed)
|
|
97
|
+
return decide(placement, engines, new Map());
|
|
98
|
+
const { able } = split(engines);
|
|
99
|
+
const seen = new Map();
|
|
100
|
+
for (const name of able) {
|
|
101
|
+
const store = engines[name];
|
|
102
|
+
seen.set(name, await store.mapWatermark());
|
|
103
|
+
}
|
|
104
|
+
const check = decide(placement, engines, seen);
|
|
105
|
+
if (check.protection === 'enforced' &&
|
|
106
|
+
(check.highestSeen === null || placement.mapVersion > check.highestSeen)) {
|
|
107
|
+
// Written only when it moves, and only after the decision - so a refusal writes nothing.
|
|
108
|
+
// Recording every start would grow the table by one row per process restart and the watermark
|
|
109
|
+
// would say nothing more than it does now.
|
|
110
|
+
for (const name of check.participating) {
|
|
111
|
+
const store = engines[name];
|
|
112
|
+
await store.recordMapVersion(placement.mapVersion, { modelVersion: placement.modelVersion });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return check;
|
|
116
|
+
}
|
|
117
|
+
function decide(placement, engines, seen) {
|
|
118
|
+
if (!placement.signed) {
|
|
119
|
+
return {
|
|
120
|
+
protection: 'not_applicable',
|
|
121
|
+
mapVersion: placement.mapVersion,
|
|
122
|
+
highestSeen: null,
|
|
123
|
+
participating: [],
|
|
124
|
+
unable: Object.keys(engines).sort(compareCodePoints),
|
|
125
|
+
why: 'this map is unsigned, so it is your own document rather than one we issued. Replacing it ' +
|
|
126
|
+
'with another is the no-account mode working as documented, and there is no newest ' +
|
|
127
|
+
'version for us to be the authority on.',
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const { able, unable } = split(engines);
|
|
131
|
+
if (able.length === 0) {
|
|
132
|
+
return {
|
|
133
|
+
protection: 'unavailable',
|
|
134
|
+
mapVersion: placement.mapVersion,
|
|
135
|
+
highestSeen: null,
|
|
136
|
+
participating: [],
|
|
137
|
+
unable,
|
|
138
|
+
why: `none of the engines in this map can keep bookkeeping ([${unable.join(', ')}]), so a map ` +
|
|
139
|
+
`that goes backwards cannot be recognised. An engine whose schema is fixed in its own ` +
|
|
140
|
+
`source - ours is - has nowhere to put it. Nothing is wrong with your configuration; this ` +
|
|
141
|
+
`protection simply does not exist for it.`,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const known = able
|
|
145
|
+
.map((name) => seen.get(name) ?? null)
|
|
146
|
+
.filter((value) => value !== null);
|
|
147
|
+
const highest = known.length === 0 ? null : Math.max(...known);
|
|
148
|
+
if (highest !== null && placement.mapVersion < highest) {
|
|
149
|
+
throw new MapRolledBack(`this map is version ${placement.mapVersion} and version ${highest} has already been ` +
|
|
150
|
+
`applied against these engines. Refusing to go backwards: an older signed map verifies ` +
|
|
151
|
+
`perfectly - that is what a signature is - so nothing else here would notice that the ` +
|
|
152
|
+
`file was replaced, and the writes would go to the previous placement. If this is ` +
|
|
153
|
+
`deliberate, because the newer map was wrong, clear the bookkeeping: DELETE FROM ` +
|
|
154
|
+
`${WATERMARK_TABLE} WHERE map_version > ${placement.mapVersion}; in ` +
|
|
155
|
+
`[${able.join(', ')}], and on an engine that deletes asynchronously, let the deletion ` +
|
|
156
|
+
`finish before restarting. That is a deliberate act with a stated consequence, which is ` +
|
|
157
|
+
`why it is not a flag.`);
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
protection: 'enforced',
|
|
161
|
+
mapVersion: placement.mapVersion,
|
|
162
|
+
highestSeen: highest,
|
|
163
|
+
participating: able,
|
|
164
|
+
unable,
|
|
165
|
+
why: `the highest map version applied against these engines is ` +
|
|
166
|
+
`${highest ?? placement.mapVersion}, kept in ${WATERMARK_TABLE} in [${able.join(', ')}]. ` +
|
|
167
|
+
`A map older than that is refused.`,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=watermark.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watermark.js","sourceRoot":"","sources":["../src/watermark.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export { DRAIN_TABLE, EPOCH_COLUMN, checkEpoch } from './generation.js';
|
|
2
|
+
export declare const FENCE_PREFIX = "__sde_f_";
|
|
3
|
+
export type ColumnState = 'absent' | 'valid' | 'conflict';
|
|
4
|
+
export interface FenceMetadata {
|
|
5
|
+
readonly identity: string;
|
|
6
|
+
readonly column: ColumnState;
|
|
7
|
+
readonly constraints: Readonly<Record<string, string>>;
|
|
8
|
+
}
|
|
9
|
+
/** Provisioning/execution capability, separate from a runtime role's INSERT permission. */
|
|
10
|
+
export interface FenceBackend {
|
|
11
|
+
metadata(table: string): Promise<FenceMetadata>;
|
|
12
|
+
addColumn(table: string): Promise<void>;
|
|
13
|
+
addConstraint(table: string, name: string, expression: string): Promise<void>;
|
|
14
|
+
dropConstraint(table: string, name: string): Promise<void>;
|
|
15
|
+
drain(table: string, options: {
|
|
16
|
+
projectId: string;
|
|
17
|
+
hold: string;
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
restore(table: string, options: {
|
|
20
|
+
projectId: string;
|
|
21
|
+
hold: string;
|
|
22
|
+
}): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export declare class FenceState {
|
|
25
|
+
readonly identity: string;
|
|
26
|
+
readonly projectId: string | undefined;
|
|
27
|
+
readonly column: ColumnState;
|
|
28
|
+
readonly minimums: readonly number[];
|
|
29
|
+
readonly maximums: readonly number[];
|
|
30
|
+
readonly holds: readonly string[];
|
|
31
|
+
readonly retired: readonly string[];
|
|
32
|
+
constructor(identity: string, projectId: string | undefined, column: ColumnState, minimums: readonly number[], maximums: readonly number[], holds: readonly string[], retired: readonly string[]);
|
|
33
|
+
get lowerEpoch(): number | undefined;
|
|
34
|
+
get upperEpoch(): number | undefined;
|
|
35
|
+
get complete(): boolean;
|
|
36
|
+
get closed(): boolean;
|
|
37
|
+
get epoch(): number | undefined;
|
|
38
|
+
asRecord(): Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
export declare function fenceState(metadata: FenceMetadata): FenceState;
|
|
41
|
+
/** Serialize administrative calls per project. A partial command may leave the table closed. */
|
|
42
|
+
export declare class WriteFence {
|
|
43
|
+
private readonly backend;
|
|
44
|
+
readonly table: string;
|
|
45
|
+
readonly projectId: string;
|
|
46
|
+
constructor(backend: FenceBackend, table: string, options: {
|
|
47
|
+
projectId: string;
|
|
48
|
+
});
|
|
49
|
+
state(): Promise<FenceState>;
|
|
50
|
+
private ready;
|
|
51
|
+
prepare(epoch: number): Promise<FenceState>;
|
|
52
|
+
freeze(requestId: string): Promise<FenceState>;
|
|
53
|
+
resume(requestId: string): Promise<FenceState>;
|
|
54
|
+
resumePrepare(epoch: number): Promise<FenceState>;
|
|
55
|
+
advance(epoch: number): Promise<FenceState>;
|
|
56
|
+
private bounds;
|
|
57
|
+
release(requestId: string): Promise<FenceState>;
|
|
58
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/** Native write generations and named barriers. These primitives do not authorize a cutover. */
|
|
2
|
+
import { MigrationRefused } from './errors.js';
|
|
3
|
+
import { DRAIN_TABLE, EPOCH_COLUMN, checkEpoch } from './generation.js';
|
|
4
|
+
export { DRAIN_TABLE, EPOCH_COLUMN, checkEpoch } from './generation.js';
|
|
5
|
+
import { BACKFILL_TABLE, WATERMARK_TABLE } from './placement.js';
|
|
6
|
+
export const FENCE_PREFIX = '__sde_f_';
|
|
7
|
+
const SETUP = FENCE_PREFIX + 'setup';
|
|
8
|
+
function identity(value, label) {
|
|
9
|
+
if (typeof value !== 'string' || !/^[0-9a-f]{32}$/.test(value)) {
|
|
10
|
+
throw new MigrationRefused(`write fence ${label} must be 32 lowercase hexadecimal digits`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function predicate(raw) {
|
|
14
|
+
let value = raw.trim();
|
|
15
|
+
if (value.startsWith('CHECK'))
|
|
16
|
+
value = value.slice(5).trim();
|
|
17
|
+
value = value.replace(/\s+NOT\s+VALID$/, '');
|
|
18
|
+
while (value.startsWith('(') && value.endsWith(')'))
|
|
19
|
+
value = value.slice(1, -1).trim();
|
|
20
|
+
if (value === 'true' || value === '1')
|
|
21
|
+
return '1';
|
|
22
|
+
if (value === 'false' || value === '0')
|
|
23
|
+
return '0';
|
|
24
|
+
const matched = /^\(*\s*(?:"__sde_write_epoch"|`__sde_write_epoch`|__sde_write_epoch)\s*\)*\s*(>=|<=)\s*\(*\s*(?:([0-9]+)|'([0-9]+)'::bigint)\s*\)*$/.exec(value);
|
|
25
|
+
if (matched === null)
|
|
26
|
+
return '<unrecognized>';
|
|
27
|
+
const number = BigInt(matched[2] ?? matched[3]);
|
|
28
|
+
return EPOCH_COLUMN + matched[1] + number.toString();
|
|
29
|
+
}
|
|
30
|
+
export class FenceState {
|
|
31
|
+
identity;
|
|
32
|
+
projectId;
|
|
33
|
+
column;
|
|
34
|
+
minimums;
|
|
35
|
+
maximums;
|
|
36
|
+
holds;
|
|
37
|
+
retired;
|
|
38
|
+
constructor(identity, projectId, column, minimums, maximums, holds, retired) {
|
|
39
|
+
this.identity = identity;
|
|
40
|
+
this.projectId = projectId;
|
|
41
|
+
this.column = column;
|
|
42
|
+
this.minimums = Object.freeze([...minimums].sort((a, b) => a - b));
|
|
43
|
+
this.maximums = Object.freeze([...maximums].sort((a, b) => a - b));
|
|
44
|
+
this.holds = Object.freeze([...holds].sort());
|
|
45
|
+
this.retired = Object.freeze([...retired].sort());
|
|
46
|
+
Object.freeze(this);
|
|
47
|
+
}
|
|
48
|
+
get lowerEpoch() { return this.minimums.at(-1); }
|
|
49
|
+
get upperEpoch() { return this.maximums[0]; }
|
|
50
|
+
get complete() {
|
|
51
|
+
return this.projectId !== undefined && this.column === 'valid' &&
|
|
52
|
+
this.minimums.length > 0 && this.maximums.length > 0;
|
|
53
|
+
}
|
|
54
|
+
get closed() {
|
|
55
|
+
return this.holds.length > 0 || (this.lowerEpoch !== undefined && this.upperEpoch !== undefined &&
|
|
56
|
+
this.lowerEpoch > this.upperEpoch);
|
|
57
|
+
}
|
|
58
|
+
get epoch() {
|
|
59
|
+
return this.complete && this.lowerEpoch === this.upperEpoch ? this.lowerEpoch : undefined;
|
|
60
|
+
}
|
|
61
|
+
asRecord() {
|
|
62
|
+
return {
|
|
63
|
+
identity: this.identity, project_id: this.projectId ?? null, column: this.column,
|
|
64
|
+
lower_epoch: this.lowerEpoch ?? null, upper_epoch: this.upperEpoch ?? null,
|
|
65
|
+
holds: [...this.holds], retired: [...this.retired], closed: this.closed,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export function fenceState(metadata) {
|
|
70
|
+
const owners = [], minima = [], maxima = [], holds = [], retired = [];
|
|
71
|
+
for (const name of Object.keys(metadata.constraints).sort()) {
|
|
72
|
+
if (!name.startsWith(FENCE_PREFIX))
|
|
73
|
+
continue;
|
|
74
|
+
const suffix = name.slice(FENCE_PREFIX.length);
|
|
75
|
+
const actual = predicate(metadata.constraints[name]);
|
|
76
|
+
let expected;
|
|
77
|
+
if (/^owner_[0-9a-f]{32}$/.test(suffix)) {
|
|
78
|
+
owners.push(suffix.slice(6));
|
|
79
|
+
expected = '1';
|
|
80
|
+
}
|
|
81
|
+
else if (/^retired_[0-9a-f]{32}$/.test(suffix)) {
|
|
82
|
+
retired.push(suffix.slice(8));
|
|
83
|
+
expected = '1';
|
|
84
|
+
}
|
|
85
|
+
else if (suffix === 'setup' || /^hold_[0-9a-f]{32}$/.test(suffix)) {
|
|
86
|
+
holds.push(suffix === 'setup' ? 'setup' : suffix.slice(5));
|
|
87
|
+
expected = '0';
|
|
88
|
+
}
|
|
89
|
+
else if (/^(?:min|max)_[1-9][0-9]*$/.test(suffix)) {
|
|
90
|
+
const value = checkEpoch(Number(suffix.slice(4)));
|
|
91
|
+
(suffix.startsWith('min_') ? minima : maxima).push(value);
|
|
92
|
+
expected = EPOCH_COLUMN + (suffix.startsWith('min_') ? '>=' : '<=') + String(value);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
throw new MigrationRefused('unrecognized constraint in the reserved write-fence namespace');
|
|
96
|
+
}
|
|
97
|
+
if (actual !== expected) {
|
|
98
|
+
throw new MigrationRefused(`write fence constraint ${name} has an unexpected predicate`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (owners.length > 1) {
|
|
102
|
+
throw new MigrationRefused('the table carries write fences from more than one project');
|
|
103
|
+
}
|
|
104
|
+
if (owners.length === 0 && (minima.length > 0 || maxima.length > 0 || holds.length > 0 || retired.length > 0)) {
|
|
105
|
+
throw new MigrationRefused('write fence constraints have no project owner');
|
|
106
|
+
}
|
|
107
|
+
return new FenceState(metadata.identity, owners[0], metadata.column, minima, maxima, holds, retired);
|
|
108
|
+
}
|
|
109
|
+
/** Serialize administrative calls per project. A partial command may leave the table closed. */
|
|
110
|
+
export class WriteFence {
|
|
111
|
+
backend;
|
|
112
|
+
table;
|
|
113
|
+
projectId;
|
|
114
|
+
constructor(backend, table, options) {
|
|
115
|
+
this.backend = backend;
|
|
116
|
+
this.table = table;
|
|
117
|
+
identity(options.projectId, 'project_id');
|
|
118
|
+
if (typeof table !== 'string' || table.length === 0 || table.includes('\0')) {
|
|
119
|
+
throw new MigrationRefused('write fence table must be a nonempty identifier');
|
|
120
|
+
}
|
|
121
|
+
if ([DRAIN_TABLE, BACKFILL_TABLE, WATERMARK_TABLE].includes(table)) {
|
|
122
|
+
throw new MigrationRefused('write fences cannot take over an SDK metadata table');
|
|
123
|
+
}
|
|
124
|
+
this.projectId = options.projectId;
|
|
125
|
+
}
|
|
126
|
+
async state() {
|
|
127
|
+
const state = fenceState(await this.backend.metadata(this.table));
|
|
128
|
+
if (state.projectId !== undefined && state.projectId !== this.projectId) {
|
|
129
|
+
throw new MigrationRefused('the write fence belongs to another project');
|
|
130
|
+
}
|
|
131
|
+
if (state.column === 'conflict') {
|
|
132
|
+
throw new MigrationRefused('the reserved write-epoch column has an incompatible definition');
|
|
133
|
+
}
|
|
134
|
+
return state;
|
|
135
|
+
}
|
|
136
|
+
async ready() {
|
|
137
|
+
const state = await this.state();
|
|
138
|
+
if (!state.complete)
|
|
139
|
+
throw new MigrationRefused('the write fence is not fully provisioned');
|
|
140
|
+
return state;
|
|
141
|
+
}
|
|
142
|
+
async prepare(epoch) {
|
|
143
|
+
checkEpoch(epoch);
|
|
144
|
+
const state = await this.state();
|
|
145
|
+
if (state.complete && !state.holds.includes('setup')) {
|
|
146
|
+
if (state.epoch !== epoch)
|
|
147
|
+
throw new MigrationRefused('provisioning cannot change an existing write epoch');
|
|
148
|
+
return state;
|
|
149
|
+
}
|
|
150
|
+
if (state.projectId === undefined && state.column !== 'absent') {
|
|
151
|
+
throw new MigrationRefused('the reserved write-epoch column is not owned by this project');
|
|
152
|
+
}
|
|
153
|
+
if (state.lowerEpoch !== undefined && state.lowerEpoch > epoch) {
|
|
154
|
+
throw new MigrationRefused('a write epoch cannot move backwards');
|
|
155
|
+
}
|
|
156
|
+
if (state.projectId === undefined) {
|
|
157
|
+
await this.backend.addConstraint(this.table, FENCE_PREFIX + 'owner_' + this.projectId, '1');
|
|
158
|
+
}
|
|
159
|
+
await this.backend.addConstraint(this.table, SETUP, '0');
|
|
160
|
+
await this.backend.addColumn(this.table);
|
|
161
|
+
await this.bounds(epoch);
|
|
162
|
+
await this.backend.drain(this.table, { projectId: this.projectId, hold: 'setup' });
|
|
163
|
+
await this.backend.dropConstraint(this.table, SETUP);
|
|
164
|
+
return this.ready();
|
|
165
|
+
}
|
|
166
|
+
async freeze(requestId) {
|
|
167
|
+
identity(requestId, 'request_id');
|
|
168
|
+
const state = await this.ready();
|
|
169
|
+
if (state.retired.includes(requestId))
|
|
170
|
+
throw new MigrationRefused('a completed write barrier id cannot be reused');
|
|
171
|
+
await this.backend.addConstraint(this.table, FENCE_PREFIX + 'hold_' + requestId, '0');
|
|
172
|
+
// Constraint existence closes admission, not INSERTs using an older metadata snapshot.
|
|
173
|
+
await this.backend.drain(this.table, { projectId: this.projectId, hold: requestId });
|
|
174
|
+
return this.ready();
|
|
175
|
+
}
|
|
176
|
+
async resume(requestId) {
|
|
177
|
+
identity(requestId, 'request_id');
|
|
178
|
+
await this.backend.restore(this.table, { projectId: this.projectId, hold: requestId });
|
|
179
|
+
return this.freeze(requestId);
|
|
180
|
+
}
|
|
181
|
+
async resumePrepare(epoch) {
|
|
182
|
+
checkEpoch(epoch);
|
|
183
|
+
await this.backend.restore(this.table, { projectId: this.projectId, hold: 'setup' });
|
|
184
|
+
return this.prepare(epoch);
|
|
185
|
+
}
|
|
186
|
+
async advance(epoch) {
|
|
187
|
+
checkEpoch(epoch);
|
|
188
|
+
const state = await this.ready();
|
|
189
|
+
if (state.holds.length === 0)
|
|
190
|
+
throw new MigrationRefused('changing a write epoch needs a named write barrier');
|
|
191
|
+
if (state.lowerEpoch !== undefined && epoch < state.lowerEpoch) {
|
|
192
|
+
throw new MigrationRefused('a write epoch cannot move backwards');
|
|
193
|
+
}
|
|
194
|
+
await this.bounds(epoch);
|
|
195
|
+
return this.ready();
|
|
196
|
+
}
|
|
197
|
+
async bounds(epoch) {
|
|
198
|
+
await this.backend.addConstraint(this.table, FENCE_PREFIX + 'min_' + epoch, `${EPOCH_COLUMN} >= ${epoch}`);
|
|
199
|
+
await this.backend.addConstraint(this.table, FENCE_PREFIX + 'max_' + epoch, `${EPOCH_COLUMN} <= ${epoch}`);
|
|
200
|
+
const state = await this.state();
|
|
201
|
+
if (state.lowerEpoch !== undefined && state.lowerEpoch > epoch) {
|
|
202
|
+
throw new MigrationRefused('another executor installed a newer write epoch; table stays closed');
|
|
203
|
+
}
|
|
204
|
+
for (const old of state.maximums) {
|
|
205
|
+
if (old < epoch)
|
|
206
|
+
await this.backend.dropConstraint(this.table, FENCE_PREFIX + 'max_' + old);
|
|
207
|
+
}
|
|
208
|
+
for (const old of state.minimums) {
|
|
209
|
+
if (old < epoch)
|
|
210
|
+
await this.backend.dropConstraint(this.table, FENCE_PREFIX + 'min_' + old);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async release(requestId) {
|
|
214
|
+
identity(requestId, 'request_id');
|
|
215
|
+
const state = await this.ready();
|
|
216
|
+
if (state.epoch === undefined)
|
|
217
|
+
throw new MigrationRefused('cannot release a barrier with an incomplete epoch change');
|
|
218
|
+
if (!state.holds.includes(requestId))
|
|
219
|
+
return state;
|
|
220
|
+
await this.backend.addConstraint(this.table, FENCE_PREFIX + 'retired_' + requestId, '1');
|
|
221
|
+
await this.backend.dropConstraint(this.table, FENCE_PREFIX + 'hold_' + requestId);
|
|
222
|
+
return this.ready();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=write-fence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-fence.js","sourceRoot":"","sources":["../src/write-fence.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@smart-data-engines/sde",
|
|
3
|
+
"version": "0.1.0-dev.0",
|
|
4
|
+
"description": "Smart Data Engine client library: declare a data model, we place it and move it",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"homepage": "https://github.com/Smart-Data-Engines/smart-data-engine-sdk",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Smart-Data-Engines/smart-data-engine-sdk.git",
|
|
10
|
+
"directory": "typescript"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/Smart-Data-Engines/smart-data-engine-sdk/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"database",
|
|
17
|
+
"placement",
|
|
18
|
+
"polystore",
|
|
19
|
+
"postgresql",
|
|
20
|
+
"clickhouse"
|
|
21
|
+
],
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./engines/postgres": {
|
|
34
|
+
"types": "./dist/engines/postgres.d.ts",
|
|
35
|
+
"import": "./dist/engines/postgres.js"
|
|
36
|
+
},
|
|
37
|
+
"./engines/clickhouse": {
|
|
38
|
+
"types": "./dist/engines/clickhouse.d.ts",
|
|
39
|
+
"import": "./dist/engines/clickhouse.js"
|
|
40
|
+
},
|
|
41
|
+
"./testing": {
|
|
42
|
+
"types": "./dist/testing/memory.d.ts",
|
|
43
|
+
"import": "./dist/testing/memory.js"
|
|
44
|
+
},
|
|
45
|
+
"./weather": {
|
|
46
|
+
"types": "./dist/demo/weather.d.ts",
|
|
47
|
+
"import": "./dist/demo/weather.js"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"files": [
|
|
51
|
+
"dist",
|
|
52
|
+
"README.md",
|
|
53
|
+
"LICENSE",
|
|
54
|
+
"NOTICE",
|
|
55
|
+
"bin"
|
|
56
|
+
],
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=18 <27"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {
|
|
61
|
+
"prepack": "npm run build",
|
|
62
|
+
"build": "tsc -p tsconfig.build.json && tsc --noEmit --allowJs --checkJs --strict --target ES2022 --module NodeNext --types node --skipLibCheck bin/weather.mjs",
|
|
63
|
+
"test": "vitest run",
|
|
64
|
+
"typecheck": "tsc --noEmit",
|
|
65
|
+
"lint": "eslint src tests --max-warnings 0"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@types/node": "^20.11.0",
|
|
69
|
+
"@types/pg": "^8.23.1",
|
|
70
|
+
"pg": "^8.23.0",
|
|
71
|
+
"typescript": "^5.4.0",
|
|
72
|
+
"vite": "^6.4.3",
|
|
73
|
+
"vitest": "^3.2.7"
|
|
74
|
+
},
|
|
75
|
+
"peerDependencies": {
|
|
76
|
+
"pg": ">=8"
|
|
77
|
+
},
|
|
78
|
+
"peerDependenciesMeta": {
|
|
79
|
+
"pg": {
|
|
80
|
+
"optional": true
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"bin": {
|
|
84
|
+
"sde-weather-ts": "./bin/weather.mjs"
|
|
85
|
+
}
|
|
86
|
+
}
|