Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-describe-source-timeout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hyperdx/api": patch
---

fix(mcp): reduce describe_source timeouts by using rollup tables for map key discovery
27 changes: 16 additions & 11 deletions packages/api/src/mcp/tools/sources/describeSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,19 @@ async function describeSourceSchema(
// Track which sampling stages were skipped due to timeout
const skippedStages: string[] = [];

// Shared by stages 2–4 so map-key discovery can use rollup tables
// instead of falling back to expensive main-table scans.
const metadataMVs =
'metadataMaterializedViews' in source
? source.metadataMaterializedViews
: undefined;

const now = new Date();
const dateRange: [Date, Date] = [
new Date(now.getTime() - VALUE_SAMPLE_LOOKBACK_MS),
now,
];

// ── 1. Column schema ──────────────────────────────────────────────────
const columns = await metadata.getColumns({
databaseName,
Expand Down Expand Up @@ -170,6 +183,9 @@ async function describeSourceSchema(
column: col.name,
maxKeys: 50,
connectionId,
metadataMVs,
dateRange,
signal,
});
mapKeysResults[col.name] = keys;
} catch (e) {
Expand Down Expand Up @@ -200,17 +216,6 @@ async function describeSourceSchema(

const lowCardinalityValues: Record<string, string[]> = {};

const metadataMVs =
'metadataMaterializedViews' in source
? source.metadataMaterializedViews
: undefined;

const now = new Date();
const dateRange: [Date, Date] = [
new Date(now.getTime() - VALUE_SAMPLE_LOOKBACK_MS),
now,
];

if (lcColumns.length > 0 && !signal.aborted) {
try {
const results = await metadata.getAllKeyValues({
Expand Down
9 changes: 9 additions & 0 deletions packages/common-utils/src/core/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ export class Metadata {
metadataMVs,
dateRange,
timestampValueExpression,
signal,
}: {
databaseName: string;
tableName: string;
Expand All @@ -361,6 +362,7 @@ export class Metadata {
metadataMVs?: MetadataMaterializedViews;
dateRange?: [Date, Date];
timestampValueExpression?: string;
signal?: AbortSignal;
}) {
// Align date range to rollup granularity for consistent cache keys
const alignedDateRange =
Expand Down Expand Up @@ -419,6 +421,7 @@ export class Metadata {
max_execution_time: 15,
max_rows_to_read: '0',
},
abort_signal: signal,
})
.then(res => res.json<{ Key: string }>())
.then(d => d.data.map(row => row.Key).filter(k => k));
Expand Down Expand Up @@ -524,6 +527,7 @@ export class Metadata {
// Set the value to 0 (unlimited) so that the LIMIT is used instead
max_rows_to_read: '0',
},
abort_signal: signal,
})
.then(res => res.json<{ keysArr?: string[]; key?: string }>())
.then(d => {
Expand Down Expand Up @@ -640,6 +644,7 @@ export class Metadata {
connectionId,
dateRange,
timestampValueExpression,
signal,
}: {
databaseName: string;
tableName: string;
Expand All @@ -649,6 +654,7 @@ export class Metadata {
dateRange?: [Date, Date];
timestampValueExpression?: string;
connectionId: string;
signal?: AbortSignal;
}) {
const dateRangeCacheSuffix =
dateRange && timestampValueExpression
Expand Down Expand Up @@ -719,6 +725,7 @@ export class Metadata {
read_overflow_mode: 'break',
...this.getClickHouseSettings(),
},
abort_signal: signal,
})
.then(res => res.json<{ value: string }>())
.then(d => d.data.map(row => row.value));
Expand Down Expand Up @@ -1464,6 +1471,7 @@ export class Metadata {
connectionId,
dateRange,
timestampValueExpression,
signal,
});
return { key: p.keyExpression, value: fallback };
}),
Expand All @@ -1482,6 +1490,7 @@ export class Metadata {
connectionId,
dateRange,
timestampValueExpression,
signal,
});
return { key: p.keyExpression, value };
}),
Expand Down
Loading