@chkit/plugin-backfill 0.1.0-beta.2 → 0.1.0-beta.20

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 (80) hide show
  1. package/README.md +40 -0
  2. package/dist/args.d.ts +109 -6
  3. package/dist/args.d.ts.map +1 -1
  4. package/dist/args.js +73 -97
  5. package/dist/args.js.map +1 -1
  6. package/dist/async-backfill.d.ts +64 -0
  7. package/dist/async-backfill.d.ts.map +1 -0
  8. package/dist/async-backfill.js +251 -0
  9. package/dist/async-backfill.js.map +1 -0
  10. package/dist/check.d.ts +9 -0
  11. package/dist/check.d.ts.map +1 -0
  12. package/dist/check.js +79 -0
  13. package/dist/check.js.map +1 -0
  14. package/dist/chunking/analyze.d.ts +38 -0
  15. package/dist/chunking/analyze.d.ts.map +1 -0
  16. package/dist/chunking/analyze.js +76 -0
  17. package/dist/chunking/analyze.js.map +1 -0
  18. package/dist/chunking/build.d.ts +11 -0
  19. package/dist/chunking/build.d.ts.map +1 -0
  20. package/dist/chunking/build.js +51 -0
  21. package/dist/chunking/build.js.map +1 -0
  22. package/dist/chunking/introspect.d.ts +34 -0
  23. package/dist/chunking/introspect.d.ts.map +1 -0
  24. package/dist/chunking/introspect.js +96 -0
  25. package/dist/chunking/introspect.js.map +1 -0
  26. package/dist/chunking/splitter.d.ts +20 -0
  27. package/dist/chunking/splitter.d.ts.map +1 -0
  28. package/dist/chunking/splitter.js +76 -0
  29. package/dist/chunking/splitter.js.map +1 -0
  30. package/dist/chunking/sql.d.ts +12 -0
  31. package/dist/chunking/sql.d.ts.map +1 -0
  32. package/dist/chunking/sql.js +221 -0
  33. package/dist/chunking/sql.js.map +1 -0
  34. package/dist/chunking/types.d.ts +29 -0
  35. package/dist/chunking/types.d.ts.map +1 -0
  36. package/dist/chunking/types.js +2 -0
  37. package/dist/chunking/types.js.map +1 -0
  38. package/dist/detect.d.ts +13 -0
  39. package/dist/detect.d.ts.map +1 -0
  40. package/dist/detect.js +113 -0
  41. package/dist/detect.js.map +1 -0
  42. package/dist/index.d.ts +8 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +3 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/options.d.ts +151 -4
  47. package/dist/options.d.ts.map +1 -1
  48. package/dist/options.js +161 -109
  49. package/dist/options.js.map +1 -1
  50. package/dist/payload.d.ts +7 -17
  51. package/dist/payload.d.ts.map +1 -1
  52. package/dist/payload.js +8 -18
  53. package/dist/payload.js.map +1 -1
  54. package/dist/planner.d.ts +9 -8
  55. package/dist/planner.d.ts.map +1 -1
  56. package/dist/planner.js +92 -92
  57. package/dist/planner.js.map +1 -1
  58. package/dist/plugin.d.ts +4 -3
  59. package/dist/plugin.d.ts.map +1 -1
  60. package/dist/plugin.js +295 -215
  61. package/dist/plugin.js.map +1 -1
  62. package/dist/queries.d.ts +21 -0
  63. package/dist/queries.d.ts.map +1 -0
  64. package/dist/queries.js +113 -0
  65. package/dist/queries.js.map +1 -0
  66. package/dist/runtime.d.ts +14 -0
  67. package/dist/runtime.d.ts.map +1 -1
  68. package/dist/runtime.js +162 -83
  69. package/dist/runtime.js.map +1 -1
  70. package/dist/state.d.ts +16 -28
  71. package/dist/state.d.ts.map +1 -1
  72. package/dist/state.js +59 -124
  73. package/dist/state.js.map +1 -1
  74. package/dist/table-config.d.ts +9 -0
  75. package/dist/table-config.d.ts.map +1 -0
  76. package/dist/table-config.js +2 -0
  77. package/dist/table-config.js.map +1 -0
  78. package/dist/types.d.ts +50 -103
  79. package/dist/types.d.ts.map +1 -1
  80. package/package.json +23 -2
@@ -0,0 +1,251 @@
1
+ import pMap from 'p-map';
2
+ function sleep(ms) {
3
+ return new Promise((resolve) => setTimeout(resolve, ms));
4
+ }
5
+ /** Build the deterministic query ID for a chunk. */
6
+ function chunkQueryId(planId, chunkId) {
7
+ return `backfill-${planId}-${chunkId}`;
8
+ }
9
+ function applyQueryStatus(state, qs) {
10
+ if (qs.status === 'running') {
11
+ const next = {
12
+ ...state,
13
+ status: 'running',
14
+ readRows: qs.readRows,
15
+ readBytes: qs.readBytes,
16
+ writtenRows: qs.writtenRows,
17
+ writtenBytes: qs.writtenBytes,
18
+ elapsedMs: qs.elapsedMs,
19
+ };
20
+ const metricsChanged = state.status !== 'running' ||
21
+ state.readRows !== qs.readRows ||
22
+ state.writtenRows !== qs.writtenRows ||
23
+ state.elapsedMs !== qs.elapsedMs;
24
+ return { state: next, changed: metricsChanged };
25
+ }
26
+ if (qs.status === 'finished') {
27
+ return {
28
+ state: {
29
+ ...state,
30
+ status: 'done',
31
+ finishedAt: new Date().toISOString(),
32
+ durationMs: qs.durationMs,
33
+ writtenRows: qs.writtenRows,
34
+ writtenBytes: qs.writtenBytes,
35
+ },
36
+ changed: true,
37
+ };
38
+ }
39
+ if (qs.status === 'failed') {
40
+ return {
41
+ state: {
42
+ ...state,
43
+ status: 'failed',
44
+ finishedAt: new Date().toISOString(),
45
+ durationMs: qs.durationMs,
46
+ error: qs.error,
47
+ },
48
+ changed: true,
49
+ };
50
+ }
51
+ // 'unknown' — leave status as-is (query_log may not have flushed yet)
52
+ return { state, changed: false };
53
+ }
54
+ function getChunk(progress, id) {
55
+ const state = progress[id];
56
+ if (!state)
57
+ throw new Error(`No progress entry for chunk ${id}`);
58
+ return state;
59
+ }
60
+ function updateChunk(progress, id, next) {
61
+ return { ...progress, [id]: next };
62
+ }
63
+ /**
64
+ * Reconcile local progress with server-side state.
65
+ *
66
+ * Queries system.processes and system.query_log for all chunk query IDs
67
+ * to discover queries that were submitted but whose status was never
68
+ * persisted locally (e.g. client crash between submit and state write).
69
+ */
70
+ export async function syncProgress(executor, planId, chunks, progress) {
71
+ const prefix = `backfill-${planId}-`;
72
+ // Collect query IDs for non-terminal chunks that need reconciliation
73
+ const chunkIdsToSync = [];
74
+ for (const chunk of chunks) {
75
+ const state = progress[chunk.id];
76
+ if (!state || state.status === 'done')
77
+ continue;
78
+ chunkIdsToSync.push(chunk.id);
79
+ }
80
+ if (chunkIdsToSync.length === 0)
81
+ return progress;
82
+ // Escape single-quotes in the prefix for safe SQL embedding
83
+ const safePrefix = prefix.replace(/'/g, "''").replace(/%/g, '\\%').replace(/_/g, '\\_');
84
+ const runningRows = await executor.query(`SELECT query_id FROM clusterAllReplicas('parallel_replicas', system.processes) WHERE user = currentUser() AND query_id LIKE '${safePrefix}%' SETTINGS skip_unavailable_shards = 1`);
85
+ const runningSet = new Set(runningRows.map((r) => r.query_id));
86
+ const logRows = await executor.query(`SELECT query_id, type, written_rows, written_bytes, query_duration_ms, exception
87
+ FROM clusterAllReplicas('parallel_replicas', system.query_log)
88
+ WHERE user = currentUser()
89
+ AND query_id LIKE '${safePrefix}%'
90
+ AND type IN ('QueryFinish', 'ExceptionWhileProcessing')
91
+ AND is_initial_query = 1
92
+ ORDER BY event_time DESC
93
+ SETTINGS skip_unavailable_shards = 1`);
94
+ // Deduplicate: take the latest log entry per query_id (results are ordered by event_time DESC)
95
+ const latestLogByQueryId = new Map();
96
+ for (const row of logRows) {
97
+ if (!latestLogByQueryId.has(row.query_id)) {
98
+ latestLogByQueryId.set(row.query_id, row);
99
+ }
100
+ }
101
+ let updated = { ...progress };
102
+ for (const chunkId of chunkIdsToSync) {
103
+ const queryId = chunkQueryId(planId, chunkId);
104
+ const current = updated[chunkId];
105
+ if (!current)
106
+ continue;
107
+ if (runningSet.has(queryId)) {
108
+ updated = updateChunk(updated, chunkId, { ...current, status: 'running', queryId });
109
+ }
110
+ else {
111
+ const logEntry = latestLogByQueryId.get(queryId);
112
+ if (logEntry) {
113
+ if (logEntry.type === 'QueryFinish') {
114
+ updated = updateChunk(updated, chunkId, {
115
+ ...current,
116
+ status: 'done',
117
+ queryId,
118
+ finishedAt: new Date().toISOString(),
119
+ writtenRows: Number(logEntry.written_rows),
120
+ writtenBytes: Number(logEntry.written_bytes),
121
+ durationMs: Number(logEntry.query_duration_ms),
122
+ });
123
+ }
124
+ else {
125
+ updated = updateChunk(updated, chunkId, {
126
+ ...current,
127
+ status: 'failed',
128
+ queryId,
129
+ finishedAt: new Date().toISOString(),
130
+ durationMs: Number(logEntry.query_duration_ms),
131
+ error: logEntry.exception,
132
+ });
133
+ }
134
+ }
135
+ }
136
+ }
137
+ return updated;
138
+ }
139
+ async function pollChunk(executor, initial, pollIntervalMs, maxPollErrors, onChanged) {
140
+ let state = initial;
141
+ let consecutiveErrors = 0;
142
+ while (state.status === 'submitted' || state.status === 'running') {
143
+ await sleep(pollIntervalMs);
144
+ if (!state.queryId)
145
+ break;
146
+ let qs;
147
+ try {
148
+ qs = await executor.queryStatus(state.queryId, {
149
+ afterTime: state.submittedAt,
150
+ });
151
+ }
152
+ catch {
153
+ consecutiveErrors++;
154
+ if (consecutiveErrors >= maxPollErrors) {
155
+ state = {
156
+ ...state,
157
+ status: 'failed',
158
+ finishedAt: new Date().toISOString(),
159
+ error: `Lost contact with query after ${consecutiveErrors} consecutive poll errors`,
160
+ };
161
+ await onChanged(state);
162
+ break;
163
+ }
164
+ continue;
165
+ }
166
+ consecutiveErrors = 0;
167
+ const result = applyQueryStatus(state, qs);
168
+ if (result.changed) {
169
+ state = result.state;
170
+ await onChanged(state);
171
+ }
172
+ }
173
+ return state;
174
+ }
175
+ export async function executeBackfill(options) {
176
+ const { executor, planId, chunks, buildQuery, concurrency = 3, pollIntervalMs = 5000, maxPollErrors = 10, onProgress, resumeFrom, replayFailed, } = options;
177
+ let progress = Object.fromEntries(chunks.map((chunk) => {
178
+ const resumed = resumeFrom?.[chunk.id];
179
+ return [chunk.id, resumed ? { ...resumed } : { status: 'pending' }];
180
+ }));
181
+ // When resuming, reconcile local state with the server before processing.
182
+ // This catches queries that were submitted but whose status was never
183
+ // persisted (e.g. client crash between submit() and state file write).
184
+ if (resumeFrom) {
185
+ progress = await syncProgress(executor, planId, chunks, progress);
186
+ }
187
+ // Reset confirmed-failed chunks to pending AFTER sync so we operate on
188
+ // ground truth. The deterministic query_id is reused; the afterTime filter
189
+ // in queryStatus ensures we ignore stale query_log entries from prior attempts.
190
+ if (replayFailed) {
191
+ // Stamp submittedAt with a 60s buffer so the afterTime filter in
192
+ // queryStatus ignores stale query_log entries from the prior failed
193
+ // attempt while tolerating clock skew between client and server.
194
+ const replayAfterTime = new Date(Date.now() - 60_000).toISOString();
195
+ for (const chunk of chunks) {
196
+ const state = progress[chunk.id];
197
+ if (state?.status === 'failed') {
198
+ progress = updateChunk(progress, chunk.id, { status: 'pending', submittedAt: replayAfterTime });
199
+ }
200
+ }
201
+ }
202
+ // Persist the reconciled state so the caller's checkpoint is up to date
203
+ if (resumeFrom || replayFailed) {
204
+ await onProgress?.(progress);
205
+ }
206
+ const setChunk = (id, next) => {
207
+ progress = updateChunk(progress, id, next);
208
+ return onProgress?.(progress);
209
+ };
210
+ await pMap(chunks, async (chunk) => {
211
+ const state = getChunk(progress, chunk.id);
212
+ // Already terminal from a previous run
213
+ if (state.status === 'done' || state.status === 'failed')
214
+ return;
215
+ // Resumed in-flight: poll to completion
216
+ if (state.status === 'submitted' || state.status === 'running') {
217
+ if (!state.queryId) {
218
+ await setChunk(chunk.id, { ...state, status: 'pending' });
219
+ }
220
+ else {
221
+ await pollChunk(executor, state, pollIntervalMs, maxPollErrors, (s) => setChunk(chunk.id, s));
222
+ return;
223
+ }
224
+ }
225
+ // Submit and poll
226
+ // submittedAt is intentionally omitted on first submission — it's only
227
+ // used as an afterTime filter to ignore stale query_log entries when
228
+ // replaying a previously failed chunk with the same deterministic query_id.
229
+ // Setting it to local time here would cause clock-skew issues with the
230
+ // ClickHouse server, making the filter exclude valid entries.
231
+ const queryId = chunkQueryId(planId, chunk.id);
232
+ const sql = buildQuery(chunk);
233
+ await executor.submit(sql, queryId);
234
+ const submitted = {
235
+ ...getChunk(progress, chunk.id),
236
+ status: 'submitted',
237
+ queryId,
238
+ };
239
+ await setChunk(chunk.id, submitted);
240
+ await pollChunk(executor, submitted, pollIntervalMs, maxPollErrors, (s) => setChunk(chunk.id, s));
241
+ }, { concurrency });
242
+ const completed = chunks.filter((c) => getChunk(progress, c.id).status === 'done').length;
243
+ const failed = chunks.filter((c) => getChunk(progress, c.id).status === 'failed').length;
244
+ return {
245
+ total: chunks.length,
246
+ completed,
247
+ failed,
248
+ progress,
249
+ };
250
+ }
251
+ //# sourceMappingURL=async-backfill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async-backfill.js","sourceRoot":"","sources":["../src/async-backfill.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ResolvedChxConfig } from '@chkit/core';
2
+ import type { BackfillPluginCheckResult } from './types.js';
3
+ export declare function evaluateBackfillCheck(input: {
4
+ configPath: string;
5
+ config: Pick<ResolvedChxConfig, 'metaDir'>;
6
+ stateDir?: string;
7
+ failCheckOnRequiredPendingBackfill: boolean;
8
+ }): Promise<BackfillPluginCheckResult>;
9
+ //# sourceMappingURL=check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAOpD,OAAO,KAAK,EACV,yBAAyB,EAC1B,MAAM,YAAY,CAAA;AAEnB,wBAAsB,qBAAqB,CAAC,KAAK,EAAE;IACjD,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAA;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kCAAkC,EAAE,OAAO,CAAA;CAC5C,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAgFrC"}
package/dist/check.js ADDED
@@ -0,0 +1,79 @@
1
+ import { join } from 'node:path';
2
+ import { computeBackfillStateDir, listPlanIds, readRun, } from './state.js';
3
+ export async function evaluateBackfillCheck(input) {
4
+ const stateDir = computeBackfillStateDir(input.config, input.configPath, input.stateDir);
5
+ const plansDir = join(stateDir, 'plans');
6
+ const runsDir = join(stateDir, 'runs');
7
+ const planIds = await listPlanIds(plansDir);
8
+ if (planIds.length === 0) {
9
+ return {
10
+ plugin: 'backfill',
11
+ evaluated: true,
12
+ ok: true,
13
+ findings: [],
14
+ metadata: {
15
+ requiredCount: 0,
16
+ activeRuns: 0,
17
+ failedRuns: 0,
18
+ },
19
+ };
20
+ }
21
+ let requiredCount = 0;
22
+ let activeRuns = 0;
23
+ let failedRuns = 0;
24
+ for (const planId of planIds) {
25
+ const runPath = join(runsDir, `${planId}.json`);
26
+ const run = await readRun(runPath);
27
+ if (!run) {
28
+ requiredCount += 1;
29
+ continue;
30
+ }
31
+ if (run.status === 'running')
32
+ activeRuns += 1;
33
+ if (run.status === 'failed')
34
+ failedRuns += 1;
35
+ if (run.status !== 'completed')
36
+ requiredCount += 1;
37
+ }
38
+ const findings = [];
39
+ if (requiredCount > 0) {
40
+ findings.push({
41
+ code: 'backfill_required_pending',
42
+ message: `Required backfills pending completion: ${requiredCount}`,
43
+ severity: input.failCheckOnRequiredPendingBackfill ? 'error' : 'warn',
44
+ metadata: {
45
+ requiredCount,
46
+ },
47
+ });
48
+ }
49
+ if (failedRuns > 0) {
50
+ findings.push({
51
+ code: 'backfill_chunk_failed_retry_exhausted',
52
+ message: `Backfill runs failed after retry budget: ${failedRuns}`,
53
+ severity: 'error',
54
+ metadata: {
55
+ failedRuns,
56
+ },
57
+ });
58
+ }
59
+ if (!input.failCheckOnRequiredPendingBackfill) {
60
+ findings.push({
61
+ code: 'backfill_policy_relaxed',
62
+ message: 'Backfill check policy is relaxed: failCheckOnRequiredPendingBackfill=false.',
63
+ severity: 'warn',
64
+ });
65
+ }
66
+ const ok = findings.every((finding) => finding.severity !== 'error');
67
+ return {
68
+ plugin: 'backfill',
69
+ evaluated: true,
70
+ ok,
71
+ findings,
72
+ metadata: {
73
+ requiredCount,
74
+ activeRuns,
75
+ failedRuns,
76
+ },
77
+ };
78
+ }
79
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import type { ChunkBoundary, PartitionInfo, PlannedChunk, SortKeyInfo } from './types.js';
2
+ export interface AnalyzeAndChunkInput {
3
+ database: string;
4
+ table: string;
5
+ from?: string;
6
+ to?: string;
7
+ maxChunkBytes: number;
8
+ requireIdempotencyToken: boolean;
9
+ query: <T>(sql: string) => Promise<T[]>;
10
+ }
11
+ export interface AnalyzeAndChunkResult {
12
+ planId: string;
13
+ partitions: PartitionInfo[];
14
+ sortKey?: SortKeyInfo;
15
+ chunks: PlannedChunk[];
16
+ }
17
+ export declare function analyzeAndChunk(input: AnalyzeAndChunkInput): Promise<AnalyzeAndChunkResult>;
18
+ export interface AnalyzeTableInput {
19
+ database: string;
20
+ table: string;
21
+ from?: string;
22
+ to?: string;
23
+ maxChunkBytes: number;
24
+ query: <T>(sql: string) => Promise<T[]>;
25
+ }
26
+ export interface AnalyzeTableResult {
27
+ partitions: PartitionInfo[];
28
+ sortKey?: SortKeyInfo;
29
+ boundaries: ChunkBoundary[];
30
+ }
31
+ export declare function analyzeTable(input: AnalyzeTableInput): Promise<AnalyzeTableResult>;
32
+ export declare function buildPlannedChunks(input: {
33
+ planId: string;
34
+ partitions: PartitionInfo[];
35
+ boundaries: ChunkBoundary[];
36
+ requireIdempotencyToken: boolean;
37
+ }): PlannedChunk[];
38
+ //# sourceMappingURL=analyze.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/chunking/analyze.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAEzF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,MAAM,CAAA;IACrB,uBAAuB,EAAE,OAAO,CAAA;IAChC,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;CACxC;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,aAAa,EAAE,CAAA;IAC3B,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,MAAM,EAAE,YAAY,EAAE,CAAA;CACvB;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAoBjG;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,MAAM,CAAA;IACrB,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;CACxC;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,aAAa,EAAE,CAAA;IAC3B,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,UAAU,EAAE,aAAa,EAAE,CAAA;CAC5B;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAgCxF;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,aAAa,EAAE,CAAA;IAC3B,UAAU,EAAE,aAAa,EAAE,CAAA;IAC3B,uBAAuB,EAAE,OAAO,CAAA;CACjC,GAAG,YAAY,EAAE,CA6BjB"}
@@ -0,0 +1,76 @@
1
+ import { hashId, randomPlanId } from '../state.js';
2
+ import { buildChunkBoundaries } from './build.js';
3
+ import { introspectTable, querySortKeyRanges } from './introspect.js';
4
+ export async function analyzeAndChunk(input) {
5
+ const { partitions, sortKey, boundaries } = await analyzeTable({
6
+ database: input.database,
7
+ table: input.table,
8
+ from: input.from,
9
+ to: input.to,
10
+ maxChunkBytes: input.maxChunkBytes,
11
+ query: input.query,
12
+ });
13
+ const planId = randomPlanId();
14
+ const chunks = buildPlannedChunks({
15
+ planId,
16
+ partitions,
17
+ boundaries,
18
+ requireIdempotencyToken: input.requireIdempotencyToken,
19
+ });
20
+ return { planId, partitions, sortKey, chunks };
21
+ }
22
+ export async function analyzeTable(input) {
23
+ const { partitions, sortKey } = await introspectTable({
24
+ database: input.database,
25
+ table: input.table,
26
+ from: input.from,
27
+ to: input.to,
28
+ query: input.query,
29
+ });
30
+ const oversizedPartitionIds = partitions
31
+ .filter(p => p.bytesOnDisk > input.maxChunkBytes)
32
+ .map(p => p.partitionId);
33
+ let sortKeyRanges;
34
+ if (sortKey && oversizedPartitionIds.length > 0) {
35
+ sortKeyRanges = await querySortKeyRanges({
36
+ database: input.database,
37
+ table: input.table,
38
+ sortKeyColumn: sortKey.column,
39
+ partitionIds: oversizedPartitionIds,
40
+ query: input.query,
41
+ });
42
+ }
43
+ const boundaries = buildChunkBoundaries({
44
+ partitions,
45
+ maxChunkBytes: input.maxChunkBytes,
46
+ sortKey,
47
+ sortKeyRanges,
48
+ });
49
+ return { partitions, sortKey, boundaries };
50
+ }
51
+ export function buildPlannedChunks(input) {
52
+ const chunks = [];
53
+ const partitionIndex = new Map();
54
+ for (const boundary of input.boundaries) {
55
+ const idx = partitionIndex.get(boundary.partitionId) ?? 0;
56
+ partitionIndex.set(boundary.partitionId, idx + 1);
57
+ const idSeed = `${input.planId}:${boundary.partitionId}:${idx}`;
58
+ const chunkId = hashId(`chunk:${idSeed}`).slice(0, 16);
59
+ const token = input.requireIdempotencyToken ? hashId(`token:${idSeed}`) : '';
60
+ const partition = input.partitions.find(p => p.partitionId === boundary.partitionId);
61
+ const from = boundary.sortKeyFrom ?? partition?.minTime ?? '';
62
+ const to = boundary.sortKeyTo ?? partition?.maxTime ?? '';
63
+ chunks.push({
64
+ id: chunkId,
65
+ partitionId: boundary.partitionId,
66
+ sortKeyFrom: boundary.sortKeyFrom,
67
+ sortKeyTo: boundary.sortKeyTo,
68
+ estimatedBytes: boundary.estimatedBytes,
69
+ idempotencyToken: token,
70
+ from,
71
+ to,
72
+ });
73
+ }
74
+ return chunks;
75
+ }
76
+ //# sourceMappingURL=analyze.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/chunking/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAElD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAoBrE,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAA2B;IAC/D,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,YAAY,CAAC;QAC7D,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,KAAK,EAAE,KAAK,CAAC,KAAK;KACnB,CAAC,CAAA;IAEF,MAAM,MAAM,GAAG,YAAY,EAAE,CAAA;IAE7B,MAAM,MAAM,GAAG,kBAAkB,CAAC;QAChC,MAAM;QACN,UAAU;QACV,UAAU;QACV,uBAAuB,EAAE,KAAK,CAAC,uBAAuB;KACvD,CAAC,CAAA;IAEF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,CAAA;AAChD,CAAC;AAiBD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAwB;IACzD,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,MAAM,eAAe,CAAC;QACpD,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,KAAK,EAAE,KAAK,CAAC,KAAK;KACnB,CAAC,CAAA;IAEF,MAAM,qBAAqB,GAAG,UAAU;SACrC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,GAAG,KAAK,CAAC,aAAa,CAAC;SAChD,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAA;IAE1B,IAAI,aAAoE,CAAA;IACxE,IAAI,OAAO,IAAI,qBAAqB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChD,aAAa,GAAG,MAAM,kBAAkB,CAAC;YACvC,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,aAAa,EAAE,OAAO,CAAC,MAAM;YAC7B,YAAY,EAAE,qBAAqB;YACnC,KAAK,EAAE,KAAK,CAAC,KAAK;SACnB,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,oBAAoB,CAAC;QACtC,UAAU;QACV,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,OAAO;QACP,aAAa;KACd,CAAC,CAAA;IAEF,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,CAAA;AAC5C,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAKlC;IACC,MAAM,MAAM,GAAmB,EAAE,CAAA;IACjC,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAA;IAEhD,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;QACzD,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,GAAG,CAAC,CAAC,CAAA;QAEjD,MAAM,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,IAAI,QAAQ,CAAC,WAAW,IAAI,GAAG,EAAE,CAAA;QAC/D,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QACtD,MAAM,KAAK,GAAG,KAAK,CAAC,uBAAuB,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QAE5E,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,QAAQ,CAAC,WAAW,CAAC,CAAA;QACpF,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,IAAI,SAAS,EAAE,OAAO,IAAI,EAAE,CAAA;QAC7D,MAAM,EAAE,GAAG,QAAQ,CAAC,SAAS,IAAI,SAAS,EAAE,OAAO,IAAI,EAAE,CAAA;QAEzD,MAAM,CAAC,IAAI,CAAC;YACV,EAAE,EAAE,OAAO;YACX,WAAW,EAAE,QAAQ,CAAC,WAAW;YACjC,WAAW,EAAE,QAAQ,CAAC,WAAW;YACjC,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,cAAc,EAAE,QAAQ,CAAC,cAAc;YACvC,gBAAgB,EAAE,KAAK;YACvB,IAAI;YACJ,EAAE;SACH,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { ChunkBoundary, PartitionInfo, SortKeyInfo } from './types.js';
2
+ export declare function buildChunkBoundaries(input: {
3
+ partitions: PartitionInfo[];
4
+ maxChunkBytes: number;
5
+ sortKey?: SortKeyInfo;
6
+ sortKeyRanges?: Map<string, {
7
+ min: string;
8
+ max: string;
9
+ }>;
10
+ }): ChunkBoundary[];
11
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/chunking/build.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE3E,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,UAAU,EAAE,aAAa,EAAE,CAAA;IAC3B,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC1D,GAAG,aAAa,EAAE,CAmDlB"}
@@ -0,0 +1,51 @@
1
+ import { splitSortKeyRange } from './splitter.js';
2
+ export function buildChunkBoundaries(input) {
3
+ const boundaries = [];
4
+ for (const partition of input.partitions) {
5
+ if (partition.bytesOnDisk <= input.maxChunkBytes) {
6
+ boundaries.push({
7
+ partitionId: partition.partitionId,
8
+ estimatedBytes: partition.bytesOnDisk,
9
+ });
10
+ }
11
+ else if (input.sortKey && input.sortKeyRanges) {
12
+ const range = input.sortKeyRanges.get(partition.partitionId);
13
+ if (!range) {
14
+ // No range data — emit as single chunk
15
+ boundaries.push({
16
+ partitionId: partition.partitionId,
17
+ estimatedBytes: partition.bytesOnDisk,
18
+ });
19
+ continue;
20
+ }
21
+ // If min === max, splitting would produce empty sub-ranges; emit as single chunk
22
+ if (range.min === range.max) {
23
+ boundaries.push({
24
+ partitionId: partition.partitionId,
25
+ estimatedBytes: partition.bytesOnDisk,
26
+ });
27
+ continue;
28
+ }
29
+ const subCount = Math.ceil(partition.bytesOnDisk / input.maxChunkBytes);
30
+ const subRanges = splitSortKeyRange(input.sortKey.category, range.min, range.max, subCount);
31
+ const estimatedBytesPerSub = Math.ceil(partition.bytesOnDisk / subCount);
32
+ for (const sub of subRanges) {
33
+ boundaries.push({
34
+ partitionId: partition.partitionId,
35
+ sortKeyFrom: sub.from,
36
+ sortKeyTo: sub.to,
37
+ estimatedBytes: estimatedBytesPerSub,
38
+ });
39
+ }
40
+ }
41
+ else {
42
+ // No sort key info — emit as single chunk despite being oversized
43
+ boundaries.push({
44
+ partitionId: partition.partitionId,
45
+ estimatedBytes: partition.bytesOnDisk,
46
+ });
47
+ }
48
+ }
49
+ return boundaries;
50
+ }
51
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/chunking/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAGjD,MAAM,UAAU,oBAAoB,CAAC,KAKpC;IACC,MAAM,UAAU,GAAoB,EAAE,CAAA;IAEtC,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACzC,IAAI,SAAS,CAAC,WAAW,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;YACjD,UAAU,CAAC,IAAI,CAAC;gBACd,WAAW,EAAE,SAAS,CAAC,WAAW;gBAClC,cAAc,EAAE,SAAS,CAAC,WAAW;aACtC,CAAC,CAAA;QACJ,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;YAChD,MAAM,KAAK,GAAG,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAA;YAC5D,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,uCAAuC;gBACvC,UAAU,CAAC,IAAI,CAAC;oBACd,WAAW,EAAE,SAAS,CAAC,WAAW;oBAClC,cAAc,EAAE,SAAS,CAAC,WAAW;iBACtC,CAAC,CAAA;gBACF,SAAQ;YACV,CAAC;YAED,iFAAiF;YACjF,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,EAAE,CAAC;gBAC5B,UAAU,CAAC,IAAI,CAAC;oBACd,WAAW,EAAE,SAAS,CAAC,WAAW;oBAClC,cAAc,EAAE,SAAS,CAAC,WAAW;iBACtC,CAAC,CAAA;gBACF,SAAQ;YACV,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,GAAG,KAAK,CAAC,aAAa,CAAC,CAAA;YACvE,MAAM,SAAS,GAAG,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;YAC3F,MAAM,oBAAoB,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,GAAG,QAAQ,CAAC,CAAA;YAExE,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;gBAC5B,UAAU,CAAC,IAAI,CAAC;oBACd,WAAW,EAAE,SAAS,CAAC,WAAW;oBAClC,WAAW,EAAE,GAAG,CAAC,IAAI;oBACrB,SAAS,EAAE,GAAG,CAAC,EAAE;oBACjB,cAAc,EAAE,oBAAoB;iBACrC,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;aAAM,CAAC;YACN,kEAAkE;YAClE,UAAU,CAAC,IAAI,CAAC;gBACd,WAAW,EAAE,SAAS,CAAC,WAAW;gBAClC,cAAc,EAAE,SAAS,CAAC,WAAW;aACtC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAA;AACnB,CAAC"}
@@ -0,0 +1,34 @@
1
+ import type { PartitionInfo, SortKeyInfo } from './types.js';
2
+ export declare function queryPartitionInfo(input: {
3
+ database: string;
4
+ table: string;
5
+ from?: string;
6
+ to?: string;
7
+ query: <T>(sql: string) => Promise<T[]>;
8
+ }): Promise<PartitionInfo[]>;
9
+ export declare function querySortKeyInfo(input: {
10
+ database: string;
11
+ table: string;
12
+ query: <T>(sql: string) => Promise<T[]>;
13
+ }): Promise<SortKeyInfo | undefined>;
14
+ export declare function querySortKeyRanges(input: {
15
+ database: string;
16
+ table: string;
17
+ sortKeyColumn: string;
18
+ partitionIds: string[];
19
+ query: <T>(sql: string) => Promise<T[]>;
20
+ }): Promise<Map<string, {
21
+ min: string;
22
+ max: string;
23
+ }>>;
24
+ export declare function introspectTable(input: {
25
+ database: string;
26
+ table: string;
27
+ from?: string;
28
+ to?: string;
29
+ query: <T>(sql: string) => Promise<T[]>;
30
+ }): Promise<{
31
+ partitions: PartitionInfo[];
32
+ sortKey?: SortKeyInfo;
33
+ }>;
34
+ //# sourceMappingURL=introspect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"introspect.d.ts","sourceRoot":"","sources":["../../src/chunking/introspect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAkB5D,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;CACxC,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CA4C3B;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE;IAC5C,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;CACxC,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CA6BnC;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;CACxC,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAiBrD;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE;IAC3C,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;CACxC,GAAG,OAAO,CAAC;IAAE,UAAU,EAAE,aAAa,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,WAAW,CAAA;CAAE,CAAC,CASlE"}