fix: don't scan the whole table to discover Map keys - #3082
Conversation
🦋 Changeset detectedLatest commit: 5a32e22 The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
@niladrix719 is attempting to deploy a commit to the HyperDX Team on Vercel. A member of the Team first needs to authorize it. |
Greptile SummaryThis PR prevents unbounded ClickHouse scans during Map-key discovery and propagates source and date-range context through autocomplete consumers.
Confidence Score: 5/5The PR appears safe to merge; no outstanding blocking findings remain in the current code. All previous review threads are resolved, including the explicitly deferred editor-integration coverage, and the latest change only corrects explanatory text without changing behavior. No actionable regression or repository-rule violation was introduced since the previous review.
|
| Filename | Overview |
|---|---|
| packages/common-utils/src/core/metadata.ts | Bounds Map-key discovery, introduces aligned default windows, and separates cache keys by discovery path and scope. |
| packages/common-utils/src/tests/metadata.test.ts | Adds regression coverage for bounded scans, text-index and rollup windows, and exact-range cache behavior. |
| packages/app/src/hooks/useMetadata.tsx | Supplies a bounded default range to field metadata requests. |
| packages/app/src/hooks/useAutoCompleteOptions.tsx | Replaces the static application-time fallback with the shared aligned metadata range. |
| packages/api/src/controllers/ai.ts | Bounds AI metadata discovery and forwards source timestamp and materialized-view metadata. |
| packages/app/src/components/DBEditTimeChartForm/ChartEditorControls.tsx | Propagates source and date-range scope to chart autocomplete editors. |
| packages/app/src/components/DashboardFiltersModal/QueryExpressionFilterEditForm.tsx | Supplies source identity to dashboard-filter autocomplete. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Autocomplete requests field metadata] --> B{Date range available?}
B -- No --> C[Use aligned 24-hour default]
B -- Yes --> D[Use caller range]
C --> E[Widen and align discovery range]
D --> E
E --> F{Text index available?}
F -- Yes --> G[Query overlapping ClickHouse parts]
F -- No --> H{Metadata rollup available?}
H -- Yes --> I[Query bounded rollup]
H -- No --> J{Caller supplied range and timestamp expression?}
J -- Yes --> K[Run bounded raw-table sample]
J -- No --> L[Skip Map-key discovery]
G --> M[Cache and return keys]
I --> M
K --> M
Reviews (29): Last reviewed commit: "fix: don't scan the whole table to disco..." | Re-trigger Greptile
Deep Review✅ No critical issues found. No P0/P1 defects were introduced by this diff. The change does what it claims: every Map-key discovery path is now bounded (text-index and rollup require an aligned/widened 🟡 P2 — recommended
🔵 P3 nitpicks (3)
Reviewers (8): correctness, adversarial, performance, maintainability, testing, project-standards, typescript, previous-comments. Testing gaps: No integration/e2e coverage for the editor Prior-comment status: The "unrounded |
a3fb411 to
02b87c1
Compare
1712d3d to
bc6864f
Compare
bc6864f to
fc153e3
Compare
PR Review2 finding(s): 🔴 0 critical · 🟠 0 major · 🔵 2 minor 1 posted as inline comment(s) on the changed lines. 1 listed below. Findings outside the changed lines1 minor
Severity is the reviewer's own estimate and is used for ordering, not filtering. |
fc153e3 to
1a29213
Compare
1a29213 to
001e0be
Compare
001e0be to
b3d622e
Compare
b3d622e to
518c53b
Compare
518c53b to
78143e9
Compare
78143e9 to
51a52da
Compare
51a52da to
91d76d3
Compare
8332dd3 to
e01769f
Compare
e01769f to
c4467b1
Compare
c4467b1 to
9964632
Compare
9964632 to
8b628b4
Compare
8b628b4 to
79d3499
Compare
|
This looks like a good change, I'll review it in depth tomorrow. In the meantime feel free to fix the lint error |
knudtty
left a comment
There was a problem hiding this comment.
Just one request, the rest looks good
| async getOrFetch<T>( | ||
| key: string, | ||
| query: (signal?: AbortSignal) => Promise<T>, | ||
| signal?: AbortSignal, | ||
| ): Promise<T> { |
There was a problem hiding this comment.
Let's limit the scope of this PR to just focus on passing through the daterange and sourceids, if we want to handle abort signals like this is should be a separate PR that works for all metadata queries
There was a problem hiding this comment.
Done, abort-signal handling removed, scoped back to dateRange/sourceId
79d3499 to
21b9bb5
Compare
21b9bb5 to
69aa081
Compare
69aa081 to
e39a725
Compare
e39a725 to
3a9a623
Compare
3a9a623 to
6ea1fa1
Compare
Signed-off-by: Niladri Adhikary <niladrix719@gmail.com>
6ea1fa1 to
5a32e22
Compare
| // result a rollup miss falls through on), so it's always safe to reuse here. | ||
| const cachedKeys = | ||
| this.cache.get<string[]>(indexCacheKey) ?? | ||
| this.cache.get<string[]>(cacheKey); |
There was a problem hiding this comment.
🔵 minor — Early cache read can serve a raw-scan sample to a rollup-capable caller
cachedKeys = cache.get(indexCacheKey) ?? cache.get(cacheKey) makes the raw-scan entry visible to callers that do have metadataMVs, which the old code never did (it selected exactly one of the metric / MV-aligned / date-suffix keys). Concretely: the dashboard-filter expression editor builds a TableConnection with no metadataMVs (QueryExpressionFilterEditForm.tsx:72) and passes no dateRange, so useMultipleAllFields substitutes defaultFieldMetadataDateRange(); its raw scan writes a sampled key list to ${keyPrefix}.${scoped0}-${scoped1}-${tve}.keys. Open the alert modal for the same source in the same hour (EditAlertModal.tsx:401 uses tcFromSource(source), so MVs are set, same tve, same default window, same maxKeys) and it computes the identical cacheKey, hits it, and returns the unordered sample instead of consulting the rollup's ranked top-N. Gate the ?? cache.get(cacheKey) fallback on !metadataMVs (or include an MV marker in keyPrefix) so the two branches stay disjoint as they were before.
Fixes #3037
Summary
getMapKeysonly added a time filter when the caller happened to pass both a date range and a timestamp expression. Several UI autocomplete call sites (chart editor, alert modal, dashboard filters) passed neither. When that happened, it fell back to an unbounded scan across the whole table instead of skipping or narrowing the querySteps to reproduce
open the chart editor, don't touch the time picker, and start typing a Group By expression on a Map column (
ResourceAttributes[). that keystroke sent ClickHouse a query with no time filter, reading every part of the table, not just recent onesThe fix
Both the raw-table scan and the text-index lookup now refuse to run without something to bound them. Field autocomplete falls back to a 24h window when it
knows the timestamp column but not the range, and the call sites that had a source/date range in scope but weren't passing them now do
Before / after
Before:
No
WHERE/predicate, full-table scanAfter:
Known gap
The SQL editors in source-configuration forms still don't pass scope, so Map keys won't autocomplete there. Left as a follow-up, needs the form's in-progress
timestampValueExpression, which isn't available the same way