@gscdump/engine 0.33.7 → 0.33.9
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/dist/rollups.d.mts +14 -13
- package/dist/rollups.mjs +25 -20
- package/package.json +3 -3
package/dist/rollups.d.mts
CHANGED
|
@@ -15,7 +15,7 @@ interface RollupEngine {
|
|
|
15
15
|
runSQL: (opts: {
|
|
16
16
|
ctx: TenantCtx;
|
|
17
17
|
fileSets: Record<string, FileSetRef>;
|
|
18
|
-
table?:
|
|
18
|
+
table?: TableName$1;
|
|
19
19
|
sql: string;
|
|
20
20
|
params?: unknown[];
|
|
21
21
|
/**
|
|
@@ -26,7 +26,7 @@ interface RollupEngine {
|
|
|
26
26
|
*/
|
|
27
27
|
searchType?: SearchType;
|
|
28
28
|
}) => Promise<{
|
|
29
|
-
rows:
|
|
29
|
+
rows: Row$1[];
|
|
30
30
|
}>;
|
|
31
31
|
/**
|
|
32
32
|
* Read the live manifest for a (tenant, table[, searchType]) cohort —
|
|
@@ -37,7 +37,7 @@ interface RollupEngine {
|
|
|
37
37
|
*/
|
|
38
38
|
listPartitions: (opts: {
|
|
39
39
|
ctx: TenantCtx;
|
|
40
|
-
table:
|
|
40
|
+
table: TableName$1;
|
|
41
41
|
searchType?: SearchType;
|
|
42
42
|
}) => Promise<Array<{
|
|
43
43
|
partition: string;
|
|
@@ -385,14 +385,13 @@ declare const queryCanonicalDailyRollup: RollupDef;
|
|
|
385
385
|
* returning `{ done:true }`. `builtAt` MUST be stable across the continuation chain
|
|
386
386
|
* (it versions both the part keys and the final rollup key).
|
|
387
387
|
*
|
|
388
|
-
* INTRA-WINDOW resumability: the deadline is checked between
|
|
389
|
-
* between windows. A single high-cardinality
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
* its continuation write disjoint, non-colliding files.
|
|
388
|
+
* INTRA-WINDOW resumability: the deadline is checked between raw-query hash shards,
|
|
389
|
+
* not just between date windows. A single high-cardinality day can spend a full
|
|
390
|
+
* reservation inside one grouped/sorted aggregate before the deadline check gets
|
|
391
|
+
* control back. `pageOffset` is the next shard index for the current window, so a
|
|
392
|
+
* continuation resumes the SAME day at the next shard. Parts are keyed by
|
|
393
|
+
* `(windowOffset, pageOffset)`; multiple parts may contain the same canonical/date
|
|
394
|
+
* from different raw-query shards, and rollup reads sum over the union.
|
|
396
395
|
*/
|
|
397
396
|
declare function rebuildCanonicalDailyResumable(opts: {
|
|
398
397
|
engine: RollupEngine;
|
|
@@ -400,9 +399,11 @@ declare function rebuildCanonicalDailyResumable(opts: {
|
|
|
400
399
|
dataSource: DataSource;
|
|
401
400
|
searchType?: SearchType;
|
|
402
401
|
builtAt: number;
|
|
403
|
-
windowOffset: number; /** Resume the `windowOffset` window at this
|
|
402
|
+
windowOffset: number; /** Resume the `windowOffset` window at this shard offset (0 = window start). */
|
|
404
403
|
pageOffset?: number; /** Output rows per page (default `ROLLUP_PAGE_ROWS_DAILY`). Injectable for tests. */
|
|
405
|
-
pageRows?: number;
|
|
404
|
+
pageRows?: number; /** Cap each input window's day span (default `DAILY_MAX_WINDOW_DAYS`). */
|
|
405
|
+
maxWindowDays?: number; /** Split each date window by raw-query hash before grouping (1 = no sharding). */
|
|
406
|
+
shardCount?: number;
|
|
406
407
|
deadlineMs: number;
|
|
407
408
|
}): Promise<{
|
|
408
409
|
done: boolean;
|
package/dist/rollups.mjs
CHANGED
|
@@ -728,8 +728,11 @@ const queryCanonicalDailyRollup = {
|
|
|
728
728
|
})).map(mapDailyRow);
|
|
729
729
|
}
|
|
730
730
|
};
|
|
731
|
+
function canonicalDailyShardPredicate(queryExpr, shardIndex, shardCount) {
|
|
732
|
+
return shardCount <= 1 ? "" : ` AND (hash(${queryExpr}) % ${shardCount}) = ${shardIndex}`;
|
|
733
|
+
}
|
|
731
734
|
function dailyWindowSqlFor(useDim, canonExpr) {
|
|
732
|
-
return useDim ? (w) => `
|
|
735
|
+
return useDim ? (w, shard) => `
|
|
733
736
|
SELECT
|
|
734
737
|
${canonExpr} AS query_canonical,
|
|
735
738
|
CAST(q.date AS VARCHAR) AS date,
|
|
@@ -738,18 +741,18 @@ function dailyWindowSqlFor(useDim, canonExpr) {
|
|
|
738
741
|
SUM(q.sum_position)::DOUBLE AS sum_position
|
|
739
742
|
FROM read_parquet({{FILES}}, union_by_name = true) q
|
|
740
743
|
LEFT JOIN read_parquet({{QUERY_DIM}}, union_by_name = true) qd ON q.query = qd.query
|
|
741
|
-
WHERE q.date >= '${w.start}' AND q.date <= '${w.end}'
|
|
744
|
+
WHERE q.date >= '${w.start}' AND q.date <= '${w.end}'${canonicalDailyShardPredicate("q.query", shard?.index ?? 0, shard?.count ?? 1)}
|
|
742
745
|
GROUP BY ${canonExpr}, q.date
|
|
743
|
-
` : (w) => `
|
|
746
|
+
` : (w, shard) => `
|
|
744
747
|
SELECT
|
|
745
748
|
${canonExpr} AS query_canonical,
|
|
746
|
-
CAST(date AS VARCHAR) AS date,
|
|
747
|
-
SUM(clicks)::BIGINT AS clicks,
|
|
748
|
-
SUM(impressions)::BIGINT AS impressions,
|
|
749
|
-
SUM(sum_position)::DOUBLE AS sum_position
|
|
750
|
-
FROM read_parquet({{FILES}}, union_by_name = true)
|
|
751
|
-
WHERE date >= '${w.start}' AND date <= '${w.end}'
|
|
752
|
-
GROUP BY ${canonExpr}, date
|
|
749
|
+
CAST(q.date AS VARCHAR) AS date,
|
|
750
|
+
SUM(q.clicks)::BIGINT AS clicks,
|
|
751
|
+
SUM(q.impressions)::BIGINT AS impressions,
|
|
752
|
+
SUM(q.sum_position)::DOUBLE AS sum_position
|
|
753
|
+
FROM read_parquet({{FILES}}, union_by_name = true) q
|
|
754
|
+
WHERE q.date >= '${w.start}' AND q.date <= '${w.end}'${canonicalDailyShardPredicate("q.query", shard?.index ?? 0, shard?.count ?? 1)}
|
|
755
|
+
GROUP BY ${canonExpr}, q.date
|
|
753
756
|
`;
|
|
754
757
|
}
|
|
755
758
|
function mapDailyRow(r) {
|
|
@@ -766,6 +769,8 @@ async function rebuildCanonicalDailyResumable(opts) {
|
|
|
766
769
|
const sType = searchType !== void 0 ? { searchType } : {};
|
|
767
770
|
const startPageOffset = opts.pageOffset ?? 0;
|
|
768
771
|
const pageRows = opts.pageRows ?? 7e4;
|
|
772
|
+
const maxWindowDays = opts.maxWindowDays ?? 7;
|
|
773
|
+
const shardCount = Math.max(1, Math.floor(opts.shardCount ?? 1));
|
|
769
774
|
const windows = planRollupWindows((await engine.listPartitions({
|
|
770
775
|
ctx,
|
|
771
776
|
table: "queries",
|
|
@@ -773,7 +778,7 @@ async function rebuildCanonicalDailyResumable(opts) {
|
|
|
773
778
|
})).map((p) => ({
|
|
774
779
|
partition: p.partition,
|
|
775
780
|
bytes: p.bytes
|
|
776
|
-
})), void 0,
|
|
781
|
+
})), void 0, maxWindowDays);
|
|
777
782
|
const windowsTotal = windows.length;
|
|
778
783
|
const dimStore = createQueryDimStore({ dataSource });
|
|
779
784
|
const useDim = await dimStore.loadMeta(ctx) !== null;
|
|
@@ -802,8 +807,11 @@ async function rebuildCanonicalDailyResumable(opts) {
|
|
|
802
807
|
};
|
|
803
808
|
for (; i < windowsTotal; i++) {
|
|
804
809
|
const w = windows[i];
|
|
805
|
-
|
|
806
|
-
|
|
810
|
+
for (; page < shardCount;) {
|
|
811
|
+
const coreSql = sqlFor(w, {
|
|
812
|
+
index: page,
|
|
813
|
+
count: shardCount
|
|
814
|
+
});
|
|
807
815
|
const result = await engine.runSQL({
|
|
808
816
|
ctx,
|
|
809
817
|
table: "queries",
|
|
@@ -815,15 +823,11 @@ async function rebuildCanonicalDailyResumable(opts) {
|
|
|
815
823
|
},
|
|
816
824
|
...extraFileSets
|
|
817
825
|
},
|
|
818
|
-
sql: `${coreSql}\nORDER BY date, query_canonical\nLIMIT ${pageRows}
|
|
826
|
+
sql: `${coreSql}\nORDER BY date, query_canonical\nLIMIT ${pageRows}`
|
|
819
827
|
});
|
|
828
|
+
if (result.rows.length >= pageRows) throw new Error(`query_canonical_daily shard overflow: window=${i} shard=${page}/${shardCount} returned >= ${pageRows} rows; increase shardCount or pageRows`);
|
|
820
829
|
await flushPage(i, page, result.rows.map(mapDailyRow));
|
|
821
|
-
|
|
822
|
-
page += pageRows;
|
|
823
|
-
if (windowDone) {
|
|
824
|
-
page = 0;
|
|
825
|
-
break;
|
|
826
|
-
}
|
|
830
|
+
page += 1;
|
|
827
831
|
if (Date.now() > deadlineMs) {
|
|
828
832
|
nextWindowOffset = i;
|
|
829
833
|
nextPageOffset = page;
|
|
@@ -832,6 +836,7 @@ async function rebuildCanonicalDailyResumable(opts) {
|
|
|
832
836
|
}
|
|
833
837
|
}
|
|
834
838
|
if (pausedMidWindow) break;
|
|
839
|
+
page = 0;
|
|
835
840
|
if (Date.now() > deadlineMs) {
|
|
836
841
|
nextWindowOffset = i + 1;
|
|
837
842
|
nextPageOffset = 0;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gscdump/engine",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.33.
|
|
4
|
+
"version": "0.33.9",
|
|
5
5
|
"description": "Append-only Parquet/DuckDB storage engine + planner + adapters for the gscdump pipeline. Node + edge runtimes; opt-in heavy peers.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -181,8 +181,8 @@
|
|
|
181
181
|
"dependencies": {
|
|
182
182
|
"drizzle-orm": "1.0.0-rc.3",
|
|
183
183
|
"proper-lockfile": "^4.1.2",
|
|
184
|
-
"@gscdump/contracts": "0.33.
|
|
185
|
-
"gscdump": "0.33.
|
|
184
|
+
"@gscdump/contracts": "0.33.9",
|
|
185
|
+
"gscdump": "0.33.9"
|
|
186
186
|
},
|
|
187
187
|
"devDependencies": {
|
|
188
188
|
"@duckdb/duckdb-wasm": "^1.32.0",
|