Repository navigation
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 5bac570 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
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 |
| // The dictionary is read in sorted token order, so a row cap | ||
| // with read_overflow_mode=break silently leaves every key that | ||
| // sorts after the cutoff with no values. | ||
| max_rows_to_read: '0', |
There was a problem hiding this comment.
🟠 major — Text index value lookups now have no read limit and ignore the team's metadataMaxRowsToRead
Removing the row cap is right, but add a time limit in its place, as getAllKeyValues already does for its uncapped path at metadata.ts:3220-3228 (max_rows_to_read: '0' with max_execution_time: 15, timeout_overflow_mode: 'break'). Without one, both mergeTreeTextIndex queries read every matching dictionary token in the date range, with no limit. The plain-column branch in getTextIndexKeyValues (line 1859) has no key filter, so on a high-cardinality column such as TraceId it scans the whole dictionary when groupUniqArray(limit) only needs limit tokens. Because max_rows_to_read: '0' is spread after getClickHouseSettings(), it also overrides the cap admins set through team.metadataMaxRowsToRead (applied in packages/app/src/hooks/useMetadata.tsx:83-86). If silently overriding that cap is intended until #3617 lands, document it. A long time limit is better, though: when it fires, it stops the query instead of quietly returning only the keys that sort first.
Nothing blocks merge automatically, but a maintainer will expect this fixed if it is a real defect in code this PR changes. If it is about surrounding code, reply and say so instead of patching. Do not widen the PR. How to respond
PR Review
3 finding(s): 🔴 0 critical · 🟠 1 major · 🔵 2 minor 3 posted as inline comment(s) on the changed lines. Severity is the reviewer's own estimate and is used for ordering, not filtering. No finding blocks merge automatically; a maintainer decides. |
Duplicate code analysis5 new clones introduced by this PR.
If one of these duplicates a helper that already exists, import it instead jscpd compared this branch against |
Deep ReviewThe core change is sound: the 🟡 P2 -- recommended
🔵 P3 nitpicks (2)
Reviewers (2 returned + orchestrator verification): performance, learnings-researcher. Correctness, adversarial, testing, api-contract, kieran-typescript, maintainability, project-standards, and agent-native reviewers were dispatched but had not returned when the review was finalized; the orchestrator independently verified the override placement, null/undefined/number semantics, cache-key changes, and API-doc consistency, and found no additional defects in those areas. Testing gaps:
No finding blocks merge automatically. A maintainer will expect P0/P1 findings in code this PR changes to be fixed; P2/P3 are your call -- fix or reply. Never fix findings about surrounding code here; reply instead. Do not widen the PR. How to respond |
E2E Test Results✅ All tests passed • 464 passed • 1 skipped • 1415s
Tests ran across 4 shards in parallel. |
| : undefined); | ||
| return { chartConfig, source, metadataMVs }; | ||
| (isLogOrTrace ? source.metadataMaterializedViews : undefined); | ||
| const mapKeyIndexMaxParts = isLogOrTrace |
There was a problem hiding this comment.
🔵 minor — mapKeyIndexMaxParts now changes the 'all' result but is missing from the React Query key
Add the resolved per-source mapKeyIndexMaxParts (and metadataMVs) to the queryKey at line 534. As it stands, after a user raises "Map key index parts" to recover missing values, the filter panel keeps serving the old result for up to the 5-minute staleTime, because the key only holds chartConfig.source and not the source setting that now changes the answer.
Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond
| const source = chartConfig.source | ||
| ? sources?.find(s => s.id === chartConfig.source) | ||
| : undefined; | ||
| const isLogOrTrace = |
There was a problem hiding this comment.
🔵 minor — Re-derives the log/trace source fields that tcFromSource already provides
tcFromSource in packages/common-utils/src/core/metadata.ts:3542 already returns metadataMVs and mapKeyIndexMaxParts behind the same isLogSource || isTraceSource guard. Use tcFromSource(source) here, as DBSearchPageFilters/hooks.ts does through tableConnection, so there is one place that decides which source fields reach the metadata lookups.
Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond
04d5867 to
b6cb6ba
Compare
| // The dictionary is read in sorted token order, so a row cap | ||
| // with read_overflow_mode=break silently leaves every key that | ||
| // sorts after the cutoff with no values. | ||
| max_rows_to_read: '0', |
There was a problem hiding this comment.
🟠 major — Removing the row cap leaves text index value lookups completely unbounded when timestampValueExpression is empty
partsOverlapFilter (metadata.ts:923) returns 1 when timestampValueExpression is falsy. In that case maxParts has no effect, and with max_rows_to_read: '0' the query reads the whole text index across every part and all time. loadMoreFacetsForKey in packages/app/src/components/DBSearchPageFilters/hooks.ts:263-266 passes '' as a fallback. Key discovery already guards this case with && timestampValueExpression (metadata.ts:1248/1295, #3037). Fix: add the same guard in dispatchKeyValueQueries so these keys fall through to the MV/raw paths, or keep max_rows_to_read/read_overflow_mode when there is no part filter.
Nothing blocks merge automatically, but a maintainer will expect this fixed if it is a real defect in code this PR changes. If it is about surrounding code, reply and say so instead of patching. Do not widen the PR. How to respond
| connectionId: tableConnection.connectionId, | ||
| metadataMVs: tableConnection.metadataMVs, | ||
| // "Load more" wants every value in the time range, not a sample of parts. | ||
| mapKeyIndexMaxParts: null, |
There was a problem hiding this comment.
🟠 major — "Load more" now has no bound at all: every part plus max_rows_to_read=0
Before this PR, load more read every part but stopped at a 3M-row cap. It now passes mapKeyIndexMaxParts: null, so it reads every part, and getMapTextIndexKeyValues/getTextIndexKeyValues (metadata.ts:1820, 1898) force max_rows_to_read: '0' on top of that. Nothing limits the read anymore, and until #3617 there's no per-key LIMIT either. Clicking load more on a high-cardinality key like TraceId or LogAttributes['requestId'] over a wide time range scans that key's whole text-index dictionary in every part. The sort-order problem this PR fixes only happens when several keys share one budget. Load more asks for one key, so when the part limit is null you can keep the team/default row cap with read_overflow_mode: 'break' and drop it only in the multi-key, sampled-parts path.
Nothing blocks merge automatically, but a maintainer will expect this fixed if it is a real defect in code this PR changes. If it is about surrounding code, reply and say so instead of patching. Do not widen the PR. How to respond
| chartConfig, | ||
| source, | ||
| metadataMVs, | ||
| mapKeyIndexMaxParts: isLogOrTrace |
There was a problem hiding this comment.
🔵 minor — Values query key leaves out the source's mapKeyIndexMaxParts/PartSelection, so a settings change serves stale values
useMultipleGetKeyValues now reads source.mapKeyIndexMaxParts/mapKeyIndexPartSelection inside streamFactory, but its queryKey (lines 534-546) contains only the chart configs, keys and limits. If a user raises "Map key index parts" because values are missing, the filter panel keeps the cached values for up to the 5-minute staleTime. Keys do refresh, because useMultipleAllFields keys on tcFromSource(...), which carries both settings (common-utils/src/core/metadata.ts:3590). Add the per-lookup mapKeyIndexMaxParts/mapKeyIndexPartSelection (and metadataMVs) to the query key.
Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond
| maxValuesPerKey: limit, | ||
| connectionId: chartConfig.connection, | ||
| metadataMVs, | ||
| mapKeyIndexMaxParts, |
There was a problem hiding this comment.
🔵 minor — No test checks that the source's part settings reach streamAllKeyValues
The existing streamAllKeyValues assertions in packages/app/src/hooks/__tests__/useMetadata.test.tsx (around lines 338-467) check metadataMVs but not the two new fields. The new hooks.test assertion only covers the null load-more path. Add a case with a log source that sets mapKeyIndexMaxParts: 5, mapKeyIndexPartSelection: 'largest' and assert both are forwarded, and that they are undefined for a non-log/trace source. A dropped field would otherwise silently fall back to the default of 20.
Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond
Summary
Text index "all values" lookups (
getMapTextIndexKeyValuesfor Map key=value indexes,getTextIndexKeyValuesfor plain-column text indexes) ran withmax_rows_to_read(default 3M) andread_overflow_mode = 'break'. That cap drops values silently:mergeTreeTextIndexreads each part's dictionary in sorted token order, and thestartsWith(token, 'key=')filters jump to each key's run of tokens.breakthen stops the read without an error. Every key that sorts after the cutoff comes back with no values, and that partial result is cached.On a real
LogAttributeslookup, only 22 of 100 keys loaded values.This PR:
max_rows_to_read: '0'on both lookups, after the team's ClickHouse settings so a configured cap can't bring the bug back.mapKeyIndexMaxPartsandmapKeyIndexPartSelection. The setting is threaded throughgetAllKeyValues/streamAllKeyValuesfrom the filter panel, "load more" for a single key, and MCPdescribe_source. Both settings are part of both lookups' cache keys.mapKeyIndexMaxParts: null, which reads every part in the time range.nullmeans no part limit; unset still means the default of 20.#3617, next in the stack, adds a per-key
LIMITand 10-key batches so a high-cardinality key stops being expensive within those parts.Trade-offs:
How to reproduce
Starting point: a ClickHouse 26.3+ log table with a Map key=value text index (the default OTel schema has
idx_log_attr_items), with at least one high-cardinality attribute (one distinct value per row) and enough rows that its tokens pass 3M in the selected time range.On
main:LogAttributeskeys load their values.system.query_log, themergeTreeTextIndexquery with OR'dstartsWith(token, …)filters showsread_rowsaround 3,000,000.On this branch:
max_rows_to_read = 0, and its parts subquery ends withORDER BY abs(rn - (cnt + 1) / 2), name LIMIT 20.References