@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.
Files changed (131) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +13 -0
  3. package/README.md +153 -0
  4. package/bin/weather.mjs +32 -0
  5. package/dist/_usage.d.ts +30 -0
  6. package/dist/_usage.js +194 -0
  7. package/dist/_usage.js.map +1 -0
  8. package/dist/bulk.d.ts +9 -0
  9. package/dist/bulk.js +83 -0
  10. package/dist/bulk.js.map +1 -0
  11. package/dist/canonical.d.ts +46 -0
  12. package/dist/canonical.js +150 -0
  13. package/dist/canonical.js.map +1 -0
  14. package/dist/capabilities.d.ts +48 -0
  15. package/dist/capabilities.js +62 -0
  16. package/dist/capabilities.js.map +1 -0
  17. package/dist/cutover.d.ts +36 -0
  18. package/dist/cutover.js +219 -0
  19. package/dist/cutover.js.map +1 -0
  20. package/dist/demo/model.d.ts +28 -0
  21. package/dist/demo/model.js +40 -0
  22. package/dist/demo/model.js.map +1 -0
  23. package/dist/demo/project.d.ts +19 -0
  24. package/dist/demo/project.js +128 -0
  25. package/dist/demo/project.js.map +1 -0
  26. package/dist/demo/weather.d.ts +73 -0
  27. package/dist/demo/weather.js +334 -0
  28. package/dist/demo/weather.js.map +1 -0
  29. package/dist/engines/_clickhouse-connection.d.ts +17 -0
  30. package/dist/engines/_clickhouse-connection.js +182 -0
  31. package/dist/engines/_clickhouse-connection.js.map +1 -0
  32. package/dist/engines/_tls-peer-identity.d.ts +2 -0
  33. package/dist/engines/_tls-peer-identity.js +23 -0
  34. package/dist/engines/_tls-peer-identity.js.map +1 -0
  35. package/dist/engines/_write-fences.d.ts +51 -0
  36. package/dist/engines/_write-fences.js +189 -0
  37. package/dist/engines/_write-fences.js.map +1 -0
  38. package/dist/engines/clickhouse.d.ts +193 -0
  39. package/dist/engines/clickhouse.js +899 -0
  40. package/dist/engines/clickhouse.js.map +1 -0
  41. package/dist/engines/postgres.d.ts +293 -0
  42. package/dist/engines/postgres.js +981 -0
  43. package/dist/engines/postgres.js.map +1 -0
  44. package/dist/errors.d.ts +89 -0
  45. package/dist/errors.js +90 -0
  46. package/dist/errors.js.map +1 -0
  47. package/dist/frozen-verification.d.ts +26 -0
  48. package/dist/frozen-verification.js +67 -0
  49. package/dist/frozen-verification.js.map +1 -0
  50. package/dist/generation.d.ts +34 -0
  51. package/dist/generation.js +81 -0
  52. package/dist/generation.js.map +1 -0
  53. package/dist/groups.d.ts +17 -0
  54. package/dist/groups.js +66 -0
  55. package/dist/groups.js.map +1 -0
  56. package/dist/hashing.d.ts +68 -0
  57. package/dist/hashing.js +146 -0
  58. package/dist/hashing.js.map +1 -0
  59. package/dist/in-place-index.d.ts +43 -0
  60. package/dist/in-place-index.js +272 -0
  61. package/dist/in-place-index.js.map +1 -0
  62. package/dist/index.d.ts +79 -0
  63. package/dist/index.js +64 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/inspection.d.ts +19 -0
  66. package/dist/inspection.js +31 -0
  67. package/dist/inspection.js.map +1 -0
  68. package/dist/internal.d.ts +42 -0
  69. package/dist/internal.js +56 -0
  70. package/dist/internal.js.map +1 -0
  71. package/dist/layout.d.ts +36 -0
  72. package/dist/layout.js +62 -0
  73. package/dist/layout.js.map +1 -0
  74. package/dist/migration.d.ts +197 -0
  75. package/dist/migration.js +592 -0
  76. package/dist/migration.js.map +1 -0
  77. package/dist/model.d.ts +93 -0
  78. package/dist/model.js +313 -0
  79. package/dist/model.js.map +1 -0
  80. package/dist/physical.d.ts +128 -0
  81. package/dist/physical.js +421 -0
  82. package/dist/physical.js.map +1 -0
  83. package/dist/placement.d.ts +157 -0
  84. package/dist/placement.js +651 -0
  85. package/dist/placement.js.map +1 -0
  86. package/dist/provisioning.d.ts +6 -0
  87. package/dist/provisioning.js +45 -0
  88. package/dist/provisioning.js.map +1 -0
  89. package/dist/query.d.ts +68 -0
  90. package/dist/query.js +340 -0
  91. package/dist/query.js.map +1 -0
  92. package/dist/routing.d.ts +25 -0
  93. package/dist/routing.js +35 -0
  94. package/dist/routing.js.map +1 -0
  95. package/dist/schema.d.ts +110 -0
  96. package/dist/schema.js +337 -0
  97. package/dist/schema.js.map +1 -0
  98. package/dist/session.d.ts +195 -0
  99. package/dist/session.js +870 -0
  100. package/dist/session.js.map +1 -0
  101. package/dist/shapes.d.ts +30 -0
  102. package/dist/shapes.js +112 -0
  103. package/dist/shapes.js.map +1 -0
  104. package/dist/staging.d.ts +29 -0
  105. package/dist/staging.js +214 -0
  106. package/dist/staging.js.map +1 -0
  107. package/dist/telemetry.d.ts +468 -0
  108. package/dist/telemetry.js +872 -0
  109. package/dist/telemetry.js.map +1 -0
  110. package/dist/testing/loader.d.ts +38 -0
  111. package/dist/testing/loader.js +86 -0
  112. package/dist/testing/loader.js.map +1 -0
  113. package/dist/testing/memory.d.ts +131 -0
  114. package/dist/testing/memory.js +311 -0
  115. package/dist/testing/memory.js.map +1 -0
  116. package/dist/timestamp.d.ts +20 -0
  117. package/dist/timestamp.js +89 -0
  118. package/dist/timestamp.js.map +1 -0
  119. package/dist/types.d.ts +79 -0
  120. package/dist/types.js +100 -0
  121. package/dist/types.js.map +1 -0
  122. package/dist/verification.d.ts +41 -0
  123. package/dist/verification.js +169 -0
  124. package/dist/verification.js.map +1 -0
  125. package/dist/watermark.d.ts +103 -0
  126. package/dist/watermark.js +170 -0
  127. package/dist/watermark.js.map +1 -0
  128. package/dist/write-fence.d.ts +58 -0
  129. package/dist/write-fence.js +225 -0
  130. package/dist/write-fence.js.map +1 -0
  131. package/package.json +86 -0
@@ -0,0 +1,592 @@
1
+ import { withReader } from './_usage.js';
2
+ import { EPOCH_COLUMN } from './generation.js';
3
+ /**
4
+ * Copying a group's rows into the copy a map fans writes out to, and comparing the two.
5
+ *
6
+ * Both halves read the client's data, which is why they are here in the public library and not in
7
+ * the control plane. What crosses the boundary to us is **numbers, never rows**: a verify report
8
+ * carries seven counts and the detail that makes a mismatch fixable stays on the client's machine.
9
+ *
10
+ * **The progress marker is a row count, never a key, and that is the whole design.** A key resumes
11
+ * exactly and needs a **codec**: every key value would have to round-trip through JSON and back, in
12
+ * every language that ever writes an adapter - and a lossy codec puts the resume point *after* rows
13
+ * nobody copied. That is silent data loss, and it differs per language. A count cannot fail that
14
+ * way; resuming costs one indexed scan, once per resume, rather than once per chunk.
15
+ *
16
+ * Three properties hold each other up:
17
+ *
18
+ * **The chunk goes before the marker.** A crash in between costs a recopy, which the target's key
19
+ * semantics absorb. The other order costs the chunk, permanently.
20
+ *
21
+ * **Idempotence comes from the target's semantics** - `ON CONFLICT DO NOTHING` in PostgreSQL, a
22
+ * `ReplacingMergeTree` collapse in ClickHouse. Not the same mechanism, which is why the live tests
23
+ * run in every direction rather than in one.
24
+ *
25
+ * **There is no ceiling and no second pass**, because dual write precedes backfill: reaching the end
26
+ * of the table **once** is enough. Rows arriving above that point are the fan-out's. The narrow
27
+ * claim that holds is worth stating precisely, because a wider one was written first and was false:
28
+ * no row that existed when the backfill started is skipped - inserting rows can only move a row
29
+ * further along the key order, never before the resume point. Rows that arrived later belong to the
30
+ * fan-out, which is the same premise the missing ceiling stands on.
31
+ */
32
+ import { compareCodePoints } from './canonical.js';
33
+ import { Timestamp } from './timestamp.js';
34
+ import { MIGRATABLE_MEMBERS, satisfies } from './capabilities.js';
35
+ import { EngineError, MigrationRefused } from './errors.js';
36
+ import { colocationGroups } from './groups.js';
37
+ import { groupColumns } from './layout.js';
38
+ import { placementOf } from './placement.js';
39
+ import { tableFor } from './session.js';
40
+ /**
41
+ * Rows per chunk, by default.
42
+ *
43
+ * A thousand rather than a round ten thousand: a chunk is held in memory twice during `verify` -
44
+ * the source's rows and the target's - and the number that matters is not throughput but how much
45
+ * work a crash discards, which is one chunk.
46
+ */
47
+ export const CHUNK_ROWS = 1000;
48
+ /**
49
+ * Sub-second digits each dialect keeps, for the neutral types where dialects differ.
50
+ *
51
+ * Both current SQL timestamp types retain six digits. This table guards future dialects; the
52
+ * runtime value must preserve those digits too, which is why the adapters return Timestamp.
53
+ */
54
+ export const DIALECT_PRECISION = {
55
+ 'timestamp|postgres': 6,
56
+ 'timestamptz|postgres': 6,
57
+ 'timestamp|clickhouse': 6,
58
+ 'timestamptz|clickhouse': 6,
59
+ };
60
+ /**
61
+ * Neutral types a copy between dialects does not silently change.
62
+ *
63
+ * Not the same claim as "every value survives". PostgreSQL's `date` has a wider range than
64
+ * ClickHouse's `Date32`, so a date in the year 1800 does not survive that move - but it fails
65
+ * *loudly*, on the insert or as a mismatch in `verify`. The line drawn here is silent-and-universal
66
+ * loss, which is a much smaller set than lossy, and it is the set worth a refusal that arrives
67
+ * before the work.
68
+ */
69
+ export const PRECISION_INDEPENDENT = new Set([
70
+ 'bool',
71
+ 'int32',
72
+ 'int64',
73
+ 'float32',
74
+ 'float64',
75
+ 'string',
76
+ 'bytes',
77
+ 'uuid',
78
+ 'date',
79
+ 'json',
80
+ ]);
81
+ /** A compile-time ratchet between the interface and the list the runtime check uses. */
82
+ export const MIGRATABLE_IS_TOTAL = true;
83
+ /**
84
+ * The ordering columns for a keyset scan, refusing an empty one.
85
+ *
86
+ * Here rather than in each adapter so that the two cannot disagree about it, and exported because
87
+ * an adapter written outside this repository has the same argument to check. An empty order is not
88
+ * a scan of everything in an unspecified order - it is a paginated scan with no pagination, which
89
+ * returns the same first page forever.
90
+ */
91
+ export function keyColumns(order, table) {
92
+ if (order.length === 0) {
93
+ throw new EngineError(`a keyset scan of ${table} needs at least one ordering column. With none, every page is the ` +
94
+ `first page and a backfill would copy the same chunk until it was stopped.`);
95
+ }
96
+ return [...order];
97
+ }
98
+ /** A bound has one value per ordering column, or the comparison is not the one intended. */
99
+ export function sameWidth(bound, cols, name) {
100
+ if (bound.length !== cols.length) {
101
+ throw new EngineError(`${name} has ${bound.length} values and the order has ${cols.length} columns ` +
102
+ `[${cols.join(', ')}]. A row-value comparison of different widths is not a narrower ` +
103
+ `comparison, it is a different one.`);
104
+ }
105
+ }
106
+ export function entityProgressRecord(progress) {
107
+ return {
108
+ entity: progress.entity,
109
+ engine: progress.engine,
110
+ table: progress.table,
111
+ rows_copied: progress.rowsCopied,
112
+ rows_this_run: progress.rowsThisRun,
113
+ chunks: progress.chunks,
114
+ complete: progress.complete,
115
+ };
116
+ }
117
+ export function backfillRecord(progress) {
118
+ return {
119
+ group: progress.group,
120
+ complete: progress.complete,
121
+ rows_this_run: progress.rowsThisRun,
122
+ entities: progress.entities.map(entityProgressRecord),
123
+ };
124
+ }
125
+ export function backfillForAHuman(progress) {
126
+ const lines = [
127
+ `backfill of ${progress.group}: ${progress.complete ? 'complete' : 'more to do'}, ` +
128
+ `${progress.rowsThisRun} rows this run`,
129
+ ];
130
+ for (const entity of progress.entities) {
131
+ lines.push(` ${entity.entity} -> ${entity.engine}.${entity.table}: ${entity.rowsCopied} rows copied ` +
132
+ `(${entity.rowsThisRun} this run, ${entity.chunks} chunks)` +
133
+ `${entity.complete ? '' : ', more to do'}`);
134
+ }
135
+ return lines.join('\n');
136
+ }
137
+ const DIFFERENCES_KEPT = 20;
138
+ /** The seven counts the gate reads. **Numbers, never rows** - see {@link VerifyReport}. */
139
+ export function verifyRecord(report) {
140
+ return {
141
+ at: report.at,
142
+ ...(report.request === undefined ? {} : { request: report.request.asRecord() }),
143
+ chunks_compared: report.chunksCompared,
144
+ chunks_mismatched: report.chunksMismatched,
145
+ tail_rows_read: report.tailRowsRead,
146
+ tail_rows_missing_in_target: report.tailRowsMissingInTarget,
147
+ rows_source: report.rowsSource,
148
+ rows_target: report.rowsTarget,
149
+ };
150
+ }
151
+ export function verifyForAHuman(report) {
152
+ const lines = [
153
+ `verify of ${report.group} at ${report.at}: ${report.matched ? 'matched' : 'DID NOT MATCH'}`,
154
+ ` below the marker: ${report.chunksCompared} chunks compared, ` +
155
+ `${report.chunksMismatched} mismatched` +
156
+ (report.chunksMismatched === 0 ? '' : ' <- the backfill did not copy these'),
157
+ ` above the marker: ${report.tailRowsRead} rows read, ` +
158
+ `${report.tailRowsMissingInTarget} missing in the copy` +
159
+ (report.tailRowsMissingInTarget === 0
160
+ ? ''
161
+ : ' <- the dual-write fan-out did not reach these'),
162
+ ` rows: ${report.rowsSource} in the source, ${report.rowsTarget} in the copy (reported, not ` +
163
+ `gated on: two counts of live tables are taken at different instants)`,
164
+ ];
165
+ if (report.differences.length > 0) {
166
+ lines.push(' the rows below are your own data. They are not part of what is reported to Smart Data ' +
167
+ 'Engines:');
168
+ for (const difference of report.differences) {
169
+ const where = difference.columns.length === 0
170
+ ? 'absent'
171
+ : `differs in [${difference.columns.join(', ')}]`;
172
+ lines.push(` ${difference.entity} ${JSON.stringify(difference.key)} -> ${where}`);
173
+ }
174
+ if (report.differencesSuppressed > 0) {
175
+ lines.push(` ... and ${report.differencesSuppressed} more`);
176
+ }
177
+ }
178
+ return lines.join('\n');
179
+ }
180
+ function migratable(session, name, role, group) {
181
+ const engine = session.engineNamed(name);
182
+ if (!satisfies(engine, MIGRATABLE_MEMBERS)) {
183
+ throw new MigrationRefused(`'${name}' cannot act as the ${role} of a migration of '${group}': its adapter does not ` +
184
+ `offer the row-level operations a copy needs. An engine whose schema is fixed in its own ` +
185
+ `source has nowhere to keep a progress marker and no table to scan in key order, so this ` +
186
+ `is a property of the engine rather than a missing feature. Refused here rather than ` +
187
+ `skipped, because a migration that copies nothing and says nothing is the worst thing ` +
188
+ `this module could do.`);
189
+ }
190
+ return engine;
191
+ }
192
+ /**
193
+ * Why a copy of these columns between these two dialects would change values, or `null`.
194
+ *
195
+ * A string rather than a throw, and dialect names rather than engines, because two doors ask this
196
+ * question and only one of them used to. `backfill` refuses a copy that would truncate; the write
197
+ * fan-out in `Session` was doing the same truncation one row at a time, for as long as an
198
+ * `also_write` map was in force. Measured on live servers before it was fixed: a `timestamptz`
199
+ * written as `09:30:15.123456` came back from PostgreSQL unchanged and from ClickHouse as
200
+ * `09:30:15.123`, with no error on either side.
201
+ */
202
+ export function precisionRefusal(group, entity, columns, sourceDialect, targetDialect) {
203
+ for (const column of Object.keys(columns).sort(compareCodePoints)) {
204
+ const neutral = columns[column];
205
+ if (neutral.startsWith('decimal(') || PRECISION_INDEPENDENT.has(neutral))
206
+ continue;
207
+ const here = DIALECT_PRECISION[`${neutral}|${sourceDialect}`];
208
+ const there = DIALECT_PRECISION[`${neutral}|${targetDialect}`];
209
+ if (here === undefined || there === undefined) {
210
+ return (`${group}.${entity}.${column} has neutral type '${neutral}', and this library does not ` +
211
+ `know whether ${sourceDialect} and ${targetDialect} store it to the same precision. ` +
212
+ `Refused rather than attempted: a type nobody classified is a type nobody checked, and ` +
213
+ `the failure mode of guessing here is a value that comes back changed with no error ` +
214
+ `anywhere.`);
215
+ }
216
+ if (there < here) {
217
+ return (`${group}.${entity}.${column} is '${neutral}', which ${sourceDialect} stores to ${here} ` +
218
+ `sub-second digits and ${targetDialect} to ${there}. Copying it would truncate every ` +
219
+ `value with more precision than that - silently, because the insert succeeds and the ` +
220
+ `value comes back changed - and verify would then find every such row mismatched at ` +
221
+ `the end of the copy rather than before it. Your rows may all happen to be aligned to ` +
222
+ `${there} digits, in which case this refusal costs you a migration that would have ` +
223
+ `worked; we cannot tell without reading your data, and a copy that is faithful only ` +
224
+ `for ` +
225
+ `the values that happen to be present is not something to build a gate on.`);
226
+ }
227
+ }
228
+ return null;
229
+ }
230
+ /**
231
+ * The target's table has the same columns as the source's, or this is not a move.
232
+ *
233
+ * A fan-out target is allowed to be any derived materialisation, and a derived materialisation is
234
+ * allowed to be a denormalised wide table - which is a useful thing and not a migration target.
235
+ * Filling one means reading the group's relations and assembling rows that exist in no single
236
+ * table, and this module copies rows. Refused by name, because the alternative is a copy that
237
+ * leaves the extra columns null and looks like it worked.
238
+ */
239
+ function shapesAgree(group, entity, source, target) {
240
+ const here = Object.keys(source.layout.columns[entity] ?? {});
241
+ const there = Object.keys(target.layout.columns[entity] ?? {});
242
+ for (const [label, columns, materialization] of [
243
+ ['source', here, source],
244
+ ['target', there, target],
245
+ ]) {
246
+ if (columns.length === 0) {
247
+ throw new MigrationRefused(`the ${label} materialisation '${materialization.id}' of '${group}' does not describe the ` +
248
+ `columns of ${entity}, so a copy cannot be checked for shape before it starts. ` +
249
+ `ensureSchema needs them too; a layout with tables and no columns is not one this ` +
250
+ `library can apply.`);
251
+ }
252
+ }
253
+ const onlySource = here.filter((column) => !there.includes(column)).sort(compareCodePoints);
254
+ const onlyTarget = there.filter((column) => !here.includes(column)).sort(compareCodePoints);
255
+ if (onlySource.length > 0 || onlyTarget.length > 0) {
256
+ throw new MigrationRefused(`${group}.${entity} has different columns in '${source.id}' and '${target.id}' (only in the ` +
257
+ `source: [${onlySource.join(', ')}]; only in the target: [${onlyTarget.join(', ')}]). That ` +
258
+ `is a reshape rather than a move: filling a wide table means reading the group's relations ` +
259
+ `and assembling rows that exist in no single table, and this module copies rows. A copy ` +
260
+ `would leave the extra columns null and look like it had worked.`);
261
+ }
262
+ }
263
+ /**
264
+ * Every refusal, before a single row moves.
265
+ *
266
+ * A migration is the operation with the least tolerance for a late discovery in this whole library:
267
+ * the cost of finding a problem at chunk four thousand is four thousand chunks of the client's I/O
268
+ * and an operator who now has to decide whether what has been copied is safe to leave.
269
+ */
270
+ function plan(session, group) {
271
+ const members = colocationGroups(session.model).find((candidate) => candidate.name === group);
272
+ if (members === undefined) {
273
+ const names = colocationGroups(session.model)
274
+ .map((candidate) => candidate.name)
275
+ .sort(compareCodePoints);
276
+ throw new MigrationRefused(`'${group}' is not a colocation group of this model. It has [${names.join(', ')}].`);
277
+ }
278
+ const body = placementOf(session.placement, group);
279
+ if (body.alsoWrite.length === 0) {
280
+ throw new MigrationRefused(`'${group}' has no fan-out target in this map, so there is nothing to backfill. A migration ` +
281
+ `reaches this library as a placement map with 'also_write' - there is no phase name in the ` +
282
+ `document and no second channel - so a map without that key is one that says this group is ` +
283
+ `not being migrated.`);
284
+ }
285
+ const source = migratable(session, body.source.engine, 'source', group);
286
+ const columns = groupColumns(session.model, members);
287
+ const copies = [];
288
+ for (const copy of body.alsoWrite) {
289
+ const target = migratable(session, copy.engine, 'target', group);
290
+ for (const entity of members.members) {
291
+ const spec = session.model.entities.find((candidate) => candidate.name === entity);
292
+ const key = spec === undefined ? [] : [...spec.key];
293
+ if (key.length === 0) {
294
+ throw new MigrationRefused(`${group}.${entity} has no key, so its rows cannot be scanned in a stable order and a ` +
295
+ `chunk boundary would not mean anything.`);
296
+ }
297
+ shapesAgree(group, entity, body.source, copy);
298
+ // No precision check here. `Session.open` refuses a map whose fan-out would truncate,
299
+ // over every group, and this needs a session - so a copy reaching this line has already
300
+ // been through that door. Asking twice leaves a branch no mutation can reach alone.
301
+ copies.push({
302
+ entity,
303
+ key,
304
+ source,
305
+ sourceEngine: body.source.engine,
306
+ sourceTable: tableFor(body.source.layout, entity),
307
+ target,
308
+ targetEngine: copy.engine,
309
+ targetId: copy.id,
310
+ targetTable: tableFor(copy.layout, entity),
311
+ writeEpoch: body.writeEpoch,
312
+ });
313
+ }
314
+ }
315
+ return copies;
316
+ }
317
+ /**
318
+ * Copy a group's existing rows into every fan-out target the map names. Resumable.
319
+ *
320
+ * Called again after an interruption it picks up from the marker, and called again after completion
321
+ * it does nothing - both because the marker is durable and lives in the target engine, next to the
322
+ * rows it describes. That is the correct coupling: a target dropped and recreated loses its marker
323
+ * with its data, and a marker kept anywhere else would claim work that no longer exists.
324
+ */
325
+ export async function backfill(session, group, options = {}) {
326
+ return withReader(session, async () => {
327
+ const chunkRows = options.chunkRows ?? CHUNK_ROWS;
328
+ if (chunkRows < 1)
329
+ throw new MigrationRefused(`a chunk of ${chunkRows} rows is not a chunk`);
330
+ const entities = [];
331
+ for (const copy of plan(session, group)) {
332
+ entities.push(await backfillOne(copy, chunkRows, options.stopAfter));
333
+ }
334
+ return {
335
+ group,
336
+ entities,
337
+ complete: entities.every((entity) => entity.complete),
338
+ rowsThisRun: entities.reduce((sum, entity) => sum + entity.rowsThisRun, 0),
339
+ };
340
+ });
341
+ }
342
+ async function backfillOne(copy, chunkRows, stopAfter) {
343
+ let marker = await copy.target.backfillMarker({
344
+ materialization: copy.targetId,
345
+ entity: copy.entity,
346
+ });
347
+ let after = await resumePoint(copy, marker);
348
+ let rowsThisRun = 0;
349
+ let chunks = 0;
350
+ let complete = false;
351
+ while (stopAfter === undefined || chunks < stopAfter) {
352
+ const rows = await copy.source.keyRange(copy.sourceTable, copy.key, after === null ? { limit: chunkRows } : { after, limit: chunkRows });
353
+ if (rows.length === 0) {
354
+ complete = true;
355
+ break;
356
+ }
357
+ // The chunk first, then the marker. A crash between them costs a recopy, which the target's key
358
+ // semantics absorb; the other order costs the chunk, permanently.
359
+ const payload = copy.writeEpoch === undefined ? rows : rows.map((row) => ({ ...logicalCopyRow(copy, row), [EPOCH_COLUMN]: copy.writeEpoch }));
360
+ await copy.target.copyIn(copy.targetTable, payload);
361
+ marker += rows.length;
362
+ await copy.target.recordBackfillMarker({
363
+ materialization: copy.targetId,
364
+ entity: copy.entity,
365
+ rows: marker,
366
+ });
367
+ const last = rows[rows.length - 1];
368
+ after = copy.key.map((column) => last[column]);
369
+ rowsThisRun += rows.length;
370
+ chunks += 1;
371
+ if (rows.length < chunkRows) {
372
+ // The end of the table, once. Rows arriving above this point from here on are the fan-out's,
373
+ // which is why there is no ceiling and no second pass.
374
+ complete = true;
375
+ break;
376
+ }
377
+ }
378
+ return {
379
+ entity: copy.entity,
380
+ engine: copy.targetEngine,
381
+ table: copy.targetTable,
382
+ rowsCopied: marker,
383
+ rowsThisRun,
384
+ chunks,
385
+ complete,
386
+ };
387
+ }
388
+ /**
389
+ * Turn a row count back into a key, or refuse if the source has lost rows.
390
+ *
391
+ * One indexed scan, paid once per resume rather than once per chunk, which is the trade that makes a
392
+ * row count an acceptable marker. If rows have been inserted below this point since the marker was
393
+ * written, row N is now an earlier row, so this key moves down and the backfill recopies.
394
+ *
395
+ * A source with fewer rows than the marker claims were copied is the one case that refuses. It means
396
+ * rows left the source outside this library, and a backfill cannot resume against a table that has
397
+ * shrunk: the marker would be describing a table that no longer exists.
398
+ */
399
+ async function resumePoint(copy, marker) {
400
+ if (marker <= 0)
401
+ return null;
402
+ const position = await copy.source.nthKey(copy.sourceTable, copy.key, { position: marker });
403
+ if (position === null) {
404
+ throw new MigrationRefused(`the marker for ${copy.entity} in ${copy.targetEngine} says ${marker} rows have been copied, ` +
405
+ `and ${copy.sourceEngine}.${copy.sourceTable} does not have that many. Rows have left the ` +
406
+ `source outside this library, so the marker describes a table that no longer exists and ` +
407
+ `resuming from it would be guessing. Nothing has been copied by this call.`);
408
+ }
409
+ return position;
410
+ }
411
+ /**
412
+ * Compare both copies of a group and report counts. The gate reads the counts, not the rows.
413
+ *
414
+ * Reads the **source first and the target second**, always, and the order is load-bearing. A write
415
+ * is in the source before it is in the copy, so a row read from the source may not have reached the
416
+ * copy yet - but the copy is read after the whole source window, so the window has already elapsed.
417
+ * Anything still missing is then looked up once more by point read, on what is normally an empty
418
+ * set. Reversing the two reads would make this flaky in the direction that stops a healthy
419
+ * migration.
420
+ */
421
+ export async function verify(session, group, options = {}) {
422
+ return withReader(session, async () => {
423
+ options.request?.checkSession(session.placement, session.projectId, group);
424
+ if (options.at !== undefined)
425
+ options.request?.checkTime(options.at);
426
+ if (options.request !== undefined && session.model.version !== options.request.modelVersion) {
427
+ throw new MigrationRefused('verification request names another session model');
428
+ }
429
+ const chunkRows = options.chunkRows ?? CHUNK_ROWS;
430
+ if (chunkRows < 1)
431
+ throw new MigrationRefused(`a chunk of ${chunkRows} rows is not a chunk`);
432
+ let chunksCompared = 0;
433
+ let chunksMismatched = 0;
434
+ let tailRowsRead = 0;
435
+ let tailMissing = 0;
436
+ let rowsSource = 0;
437
+ let rowsTarget = 0;
438
+ const differences = [];
439
+ let suppressed = 0;
440
+ for (const copy of plan(session, group)) {
441
+ const marker = await copy.target.backfillMarker({
442
+ materialization: copy.targetId,
443
+ entity: copy.entity,
444
+ });
445
+ rowsSource += await copy.source.count(copy.sourceTable);
446
+ rowsTarget += await copy.target.count(copy.targetTable);
447
+ let after = null;
448
+ let seen = 0;
449
+ for (;;) {
450
+ const below = seen < marker;
451
+ const want = below ? Math.min(chunkRows, marker - seen) : chunkRows;
452
+ const rows = await copy.source.keyRange(copy.sourceTable, copy.key, after === null ? { limit: want } : { after, limit: want });
453
+ if (rows.length === 0)
454
+ break;
455
+ const last = rows[rows.length - 1];
456
+ const high = copy.key.map((column) => last[column]);
457
+ const missing = await missingInTarget(copy, rows, after, high);
458
+ if (below) {
459
+ chunksCompared += 1;
460
+ if (missing.length > 0)
461
+ chunksMismatched += 1;
462
+ }
463
+ else {
464
+ tailRowsRead += rows.length;
465
+ tailMissing += missing.length;
466
+ }
467
+ for (const difference of missing) {
468
+ if (differences.length < DIFFERENCES_KEPT)
469
+ differences.push(difference);
470
+ else
471
+ suppressed += 1;
472
+ }
473
+ after = high;
474
+ seen += rows.length;
475
+ }
476
+ }
477
+ const at = options.at ?? new Date().toISOString();
478
+ options.request?.checkTime(at);
479
+ return {
480
+ at,
481
+ ...(options.request === undefined ? {} : { request: options.request }),
482
+ group,
483
+ chunksCompared,
484
+ chunksMismatched,
485
+ tailRowsRead,
486
+ tailRowsMissingInTarget: tailMissing,
487
+ rowsSource,
488
+ rowsTarget,
489
+ differences,
490
+ differencesSuppressed: suppressed,
491
+ matched: chunksMismatched === 0 && tailMissing === 0,
492
+ };
493
+ });
494
+ }
495
+ /**
496
+ * Which of these source rows the target does not have, or has differently.
497
+ *
498
+ * One windowed read of the target instead of one point read per row, and then point reads only for
499
+ * what the window says is missing - which is normally nothing. The second look is what absorbs a
500
+ * fan-out that was in flight during the first: it happens after the whole window has been read, so
501
+ * the write has had that long to land.
502
+ */
503
+ async function missingInTarget(copy, rows, low, high) {
504
+ const mirror = await copy.target.keyRange(copy.targetTable, copy.key, low === null ? { upto: high } : { after: low, upto: high });
505
+ const index = new Map();
506
+ for (const row of mirror)
507
+ index.set(keyOf(copy.key, row), logicalCopyRow(copy, row));
508
+ const out = [];
509
+ for (const physicalRow of rows) {
510
+ const row = logicalCopyRow(copy, physicalRow);
511
+ const there = index.get(keyOf(copy.key, row));
512
+ if (there !== undefined && differingColumns(row, there).length === 0)
513
+ continue;
514
+ // Not there, or there and different. Look once more, directly, before calling it a loss.
515
+ const named = {};
516
+ for (const column of copy.key)
517
+ named[column] = row[column];
518
+ const again = await copy.target.get(copy.targetTable, named);
519
+ if (again !== null) {
520
+ const differs = differingColumns(row, logicalCopyRow(copy, again));
521
+ if (differs.length === 0)
522
+ continue;
523
+ out.push({ entity: copy.entity, table: copy.targetTable, key: named, columns: differs });
524
+ continue;
525
+ }
526
+ out.push({ entity: copy.entity, table: copy.targetTable, key: named, columns: [] });
527
+ }
528
+ return out;
529
+ }
530
+ /**
531
+ * A comparable string for one row's key.
532
+ *
533
+ * `JSON.stringify` of the values in key order, which is a **lookup key inside this function** and
534
+ * never a stored one - the distinction the whole module rests on. A codec good enough to index a
535
+ * chunk in memory is not good enough to resume a backfill from, because a lossy round trip there
536
+ * puts the resume point after rows nobody copied.
537
+ */
538
+ function keyOf(order, row) {
539
+ // Timestamp.toJSON preserves six digits; native int64 keys must also remain exact.
540
+ return JSON.stringify(order.map((column) => row[column] ?? null), (_key, value) => typeof value === 'bigint' ? ['bigint', value.toString()] : value);
541
+ }
542
+ /**
543
+ * Columns of the **source** row whose values the target does not match, by name.
544
+ *
545
+ * Compared value by value rather than by digest, and the reason is diagnostics. Both copies are read
546
+ * by one process on one machine, so a checksum would compress a comparison that costs nothing to do
547
+ * exactly - and an exact comparison can say *which column* differs, which is the difference between
548
+ * an operator who can fix a migration and one who can only stop it.
549
+ *
550
+ * Over the source's columns and not over the union, which is a decision rather than an oversight.
551
+ * `ensureSchema` allows a table to have columns the map does not name - it permits the write, because
552
+ * a client may have added one outside SDE - so a copy table with an extra column is a supported
553
+ * state. Comparing the union would then report *every* row as differing, on a column that has
554
+ * nothing to do with the copy, and stop a healthy migration.
555
+ */
556
+ function differingColumns(source, target) {
557
+ const out = [];
558
+ for (const column of Object.keys(source)) {
559
+ if (!(column in target) || !sameValue(source[column], target[column]))
560
+ out.push(column);
561
+ }
562
+ return out.sort(compareCodePoints);
563
+ }
564
+ /**
565
+ * Whether two column values are the same.
566
+ *
567
+ * Exact epoch microseconds for Timestamp, with Date and Buffer also reduced to comparable values.
568
+ * This runtime's `===` on two equal dates is false and would report every timestamp column
569
+ * as differing - the failure mode being a healthy migration that can never pass its gate. The
570
+ * reference implementation gets this for free from Python's `==`.
571
+ */
572
+ function sameValue(left, right) {
573
+ if (left instanceof Timestamp && right instanceof Timestamp) {
574
+ return left.epochMicroseconds === right.epochMicroseconds;
575
+ }
576
+ if (left instanceof Date && right instanceof Date)
577
+ return left.getTime() === right.getTime();
578
+ if (left instanceof Uint8Array && right instanceof Uint8Array) {
579
+ return left.length === right.length && left.every((byte, index) => byte === right[index]);
580
+ }
581
+ if (typeof left === 'bigint' || typeof right === 'bigint') {
582
+ // A driver may hand back a bigint where the other hands back a number for the same int64
583
+ // column. Comparing those with `Object.is` reports every row as differing.
584
+ return String(left) === String(right);
585
+ }
586
+ return Object.is(left, right);
587
+ }
588
+ function logicalCopyRow(copy, row) {
589
+ return copy.writeEpoch === undefined ? row :
590
+ Object.fromEntries(Object.entries(row).filter(([name]) => name !== EPOCH_COLUMN));
591
+ }
592
+ //# sourceMappingURL=migration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration.js","sourceRoot":"","sources":["../src/migration.ts"],"names":[],"mappings":"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"}