From 04bf3005e9589c94e815c351868ffb7450bfe5a6 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Tue, 11 Nov 2025 14:28:11 -0500 Subject: [PATCH 1/5] fix: optimize query key for aliasMap to prevent jitter Fixes: HDX-2787 During live tail, the date range changes every few seconds (e.g., from 9:00-9:15 to 9:02-9:17, etc...). The original aliasMap query key included the entire config object, which contains the dateRange property. Every date range change triggered a refetch of the alias map, even though aliases are derived from the SELECT statement and not from the date range. While refetching, react-query sets aliasMap to undefined. This caused column IDs to change. React-table uses column IDs as keys to track resize state, so when the ID changes, it loses the stored width and resets to the default size, causing the visible jitter. Now we have a consistent aliasMap with the added benefit of less network requests. --- packages/app/src/DBSearchPage.tsx | 7 ++----- packages/app/src/hooks/useChartConfig.tsx | 12 +++++++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/app/src/DBSearchPage.tsx b/packages/app/src/DBSearchPage.tsx index 10142ec80..bc757bb05 100644 --- a/packages/app/src/DBSearchPage.tsx +++ b/packages/app/src/DBSearchPage.tsx @@ -57,7 +57,7 @@ import { useDocumentVisibility, } from '@mantine/hooks'; import { notifications } from '@mantine/notifications'; -import { keepPreviousData, useIsFetching } from '@tanstack/react-query'; +import { useIsFetching } from '@tanstack/react-query'; import { SortingState } from '@tanstack/react-table'; import CodeMirror from '@uiw/react-codemirror'; @@ -1147,10 +1147,7 @@ function DBSearchPage() { } }, [isReady, queryReady, isChartConfigLoading, onSearch]); - const { data: aliasMap } = useAliasMapFromChartConfig(dbSqlRowTableConfig, { - placeholderData: keepPreviousData, - queryKey: ['aliasMap', dbSqlRowTableConfig, 'withPlaceholder'], - }); + const { data: aliasMap } = useAliasMapFromChartConfig(dbSqlRowTableConfig); const aliasWith = useMemo( () => diff --git a/packages/app/src/hooks/useChartConfig.tsx b/packages/app/src/hooks/useChartConfig.tsx index 54fa77f32..facac9983 100644 --- a/packages/app/src/hooks/useChartConfig.tsx +++ b/packages/app/src/hooks/useChartConfig.tsx @@ -1,3 +1,4 @@ +import { omit } from 'lodash'; import { chSqlToAliasMap, ClickHouseQueryError, @@ -287,7 +288,16 @@ export function useAliasMapFromChartConfig( options?: UseQueryOptions>, ) { return useQuery>({ - queryKey: ['aliasMap', config], + // Only include config properties that affect SELECT aliases + // (not dateRange, where, orderBy, limit which don't affect aliases) + queryKey: [ + 'aliasMap', + config?.select, + config?.from, + config?.connection, + config?.with, + config?.groupBy, + ], queryFn: async () => { if (config == null) { return {}; From 10345a99cc46179308fa07cf6897722a8f542e38 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Tue, 11 Nov 2025 14:28:29 -0500 Subject: [PATCH 2/5] omit omit --- packages/app/src/hooks/useChartConfig.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/app/src/hooks/useChartConfig.tsx b/packages/app/src/hooks/useChartConfig.tsx index facac9983..09ec99125 100644 --- a/packages/app/src/hooks/useChartConfig.tsx +++ b/packages/app/src/hooks/useChartConfig.tsx @@ -1,4 +1,3 @@ -import { omit } from 'lodash'; import { chSqlToAliasMap, ClickHouseQueryError, From 9809568329d02f180ed34ba38013178c3df02b21 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Tue, 11 Nov 2025 20:29:18 +0100 Subject: [PATCH 3/5] changeset Optimize query key for aliasMap to prevent jitter. --- .changeset/pretty-sloths-admire.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/pretty-sloths-admire.md diff --git a/.changeset/pretty-sloths-admire.md b/.changeset/pretty-sloths-admire.md new file mode 100644 index 000000000..a878c9bf5 --- /dev/null +++ b/.changeset/pretty-sloths-admire.md @@ -0,0 +1,5 @@ +--- +"@hyperdx/app": patch +--- + +fix: optimize query key for aliasMap to prevent jitter From 61fa70e58d5dcf389a7b4ba6ca2c009e7fddee56 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Tue, 11 Nov 2025 14:34:28 -0500 Subject: [PATCH 4/5] switch to omit --- packages/app/src/hooks/useChartConfig.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/app/src/hooks/useChartConfig.tsx b/packages/app/src/hooks/useChartConfig.tsx index 09ec99125..0b4d59f59 100644 --- a/packages/app/src/hooks/useChartConfig.tsx +++ b/packages/app/src/hooks/useChartConfig.tsx @@ -1,3 +1,4 @@ +import { omit } from 'lodash'; import { chSqlToAliasMap, ClickHouseQueryError, @@ -287,15 +288,19 @@ export function useAliasMapFromChartConfig( options?: UseQueryOptions>, ) { return useQuery>({ - // Only include config properties that affect SELECT aliases - // (not dateRange, where, orderBy, limit which don't affect aliases) + // Omit properties that don't affect SELECT aliases (time filters, result modifiers) + // to prevent unnecessary refetches during live tail when only dateRange changes. + // Everything else (select, from, with, groupBy, selectGroupBy, granularity, etc.) is kept. queryKey: [ 'aliasMap', - config?.select, - config?.from, - config?.connection, - config?.with, - config?.groupBy, + omit(config, [ + 'dateRange', + 'dateRangeEndInclusive', + 'where', + 'orderBy', + 'limit', + 'timestampValueExpression', + ]), ], queryFn: async () => { if (config == null) { From adf297ad70a558d18c1c13e403746ad51d9a2b92 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 12 Nov 2025 12:58:41 -0500 Subject: [PATCH 5/5] pr feedback --- packages/app/src/hooks/useChartConfig.tsx | 32 ++++++++++++++--------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/packages/app/src/hooks/useChartConfig.tsx b/packages/app/src/hooks/useChartConfig.tsx index 0b4d59f59..591f27de2 100644 --- a/packages/app/src/hooks/useChartConfig.tsx +++ b/packages/app/src/hooks/useChartConfig.tsx @@ -1,4 +1,3 @@ -import { omit } from 'lodash'; import { chSqlToAliasMap, ClickHouseQueryError, @@ -287,20 +286,29 @@ export function useAliasMapFromChartConfig( config: ChartConfigWithOptDateRange | undefined, options?: UseQueryOptions>, ) { + // For granularity: 'auto', the bucket size depends on dateRange duration (not absolute times). + // Include duration in key to detect when bucket size changes, but omit absolute times + // to prevent refetches when the time window just slides forward (e.g., live tail). + const dateRangeDuration = + config?.dateRange && isUsingGranularity(config) + ? config.dateRange[1].getTime() - config.dateRange[0].getTime() + : undefined; + return useQuery>({ - // Omit properties that don't affect SELECT aliases (time filters, result modifiers) - // to prevent unnecessary refetches during live tail when only dateRange changes. - // Everything else (select, from, with, groupBy, selectGroupBy, granularity, etc.) is kept. + // Only include config properties that affect SELECT structure and aliases. + // When adding new ChartConfig fields, check renderChartConfig.ts to see if they + // affect the SELECT clause. If yes, add them here to avoid stale alias maps. queryKey: [ 'aliasMap', - omit(config, [ - 'dateRange', - 'dateRangeEndInclusive', - 'where', - 'orderBy', - 'limit', - 'timestampValueExpression', - ]), + config?.select, + config?.from, + config?.connection, + config?.with, + config?.groupBy, + config?.selectGroupBy, + config?.granularity, + config?.seriesReturnType, + dateRangeDuration, ], queryFn: async () => { if (config == null) {