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
7 changes: 7 additions & 0 deletions .changeset/afraid-rocks-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@hyperdx/common-utils": minor
"@hyperdx/api": minor
"@hyperdx/app": minor
---

added team level queryTimeout to ClickHouse client
2 changes: 2 additions & 0 deletions packages/api/src/models/team.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type ObjectId = mongoose.Types.ObjectId;
export type TeamCHSettings = {
metadataMaxRowsToRead?: number;
searchRowLimit?: number;
queryTimeout?: number;
fieldMetadataDisabled?: boolean;
};

Expand Down Expand Up @@ -46,6 +47,7 @@ export default mongoose.model<ITeam>(
// CH Client Settings
metadataMaxRowsToRead: Number,
searchRowLimit: Number,
queryTimeout: Number,
fieldMetadataDisabled: Boolean,
},
{
Expand Down
13 changes: 11 additions & 2 deletions packages/api/src/routers/api/team.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ router.patch(
body: z.object({
fieldMetadataDisabled: z.boolean().optional(),
searchRowLimit: z.number().optional(),
queryTimeout: z.number().optional(),
metadataMaxRowsToRead: z.number().optional(),
}),
}),
Expand All @@ -104,11 +105,16 @@ router.patch(
throw new Error(`User ${req.user?._id} not associated with a team`);
}

const { fieldMetadataDisabled, metadataMaxRowsToRead, searchRowLimit } =
req.body;
const {
fieldMetadataDisabled,
metadataMaxRowsToRead,
searchRowLimit,
queryTimeout,
} = req.body;

const settings = {
...(searchRowLimit !== undefined && { searchRowLimit }),
...(queryTimeout !== undefined && { queryTimeout }),
...(fieldMetadataDisabled !== undefined && { fieldMetadataDisabled }),
...(metadataMaxRowsToRead !== undefined && { metadataMaxRowsToRead }),
};
Expand All @@ -123,6 +129,9 @@ router.patch(
...(searchRowLimit !== undefined && {
searchRowLimit: team?.searchRowLimit,
}),
...(queryTimeout !== undefined && {
queryTimeout: team?.queryTimeout,
}),
...(fieldMetadataDisabled !== undefined && {
fieldMetadataDisabled: team?.fieldMetadataDisabled,
}),
Expand Down
8 changes: 4 additions & 4 deletions packages/app/src/BenchmarkPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from '@mantine/core';
import { useQuery, UseQueryOptions } from '@tanstack/react-query';

import { getClickhouseClient } from '@/clickhouse';
import { useClickhouseClient } from '@/clickhouse';

import { ConnectionSelectControlled } from './components/ConnectionSelect';
import DBTableChart from './components/DBTableChart';
Expand All @@ -38,7 +38,7 @@ function useBenchmarkQueryIds({
iterations?: number;
}) {
const enabled = queries.length > 0 && connections.length > 0;
const clickhouseClient = getClickhouseClient();
const clickhouseClient = useClickhouseClient();

return useQuery({
enabled,
Expand Down Expand Up @@ -95,7 +95,7 @@ function useEstimates(
},
options: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'> = {},
) {
const clickhouseClient = getClickhouseClient();
const clickhouseClient = useClickhouseClient();
return useQuery({
queryKey: ['estimate', queries, connections],
queryFn: async () => {
Expand Down Expand Up @@ -125,7 +125,7 @@ function useIndexes(
},
options: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'> = {},
) {
const clickhouseClient = getClickhouseClient();
const clickhouseClient = useClickhouseClient();
return useQuery({
queryKey: ['indexes', queries, connections],
queryFn: async () => {
Expand Down
39 changes: 26 additions & 13 deletions packages/app/src/TeamPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import { IS_LOCAL_MODE } from '@/config';
import { PageHeader } from './components/PageHeader';
import api from './api';
import { useConnections } from './connection';
import { DEFAULT_SEARCH_ROW_LIMIT } from './defaults';
import { DEFAULT_QUERY_TIMEOUT, DEFAULT_SEARCH_ROW_LIMIT } from './defaults';
import { withAppNav } from './layout';
import { useSources } from './source';
import { useConfirm } from './useConfirm';
Expand Down Expand Up @@ -1069,6 +1069,7 @@ type ClickhouseSettingType = 'number' | 'boolean';
interface ClickhouseSettingFormProps {
settingKey:
| 'searchRowLimit'
| 'queryTimeout'
| 'metadataMaxRowsToRead'
| 'fieldMetadataDisabled';
label: string;
Expand All @@ -1078,7 +1079,7 @@ interface ClickhouseSettingFormProps {
placeholder?: string;
min?: number;
max?: number;
displayValue?: (value: any) => string;
displayValue?: (value: any, defaultValue?: any) => string;
options?: string[]; // For boolean settings displayed as select
}

Expand Down Expand Up @@ -1237,7 +1238,7 @@ function ClickhouseSettingForm({
<Group>
<Text className="text-white">
{displayValue
? displayValue(currentValue)
? displayValue(currentValue, defaultValue)
: currentValue?.toString() || 'Not set'}
</Text>
{hasAdminAccess && (
Expand All @@ -1257,6 +1258,14 @@ function ClickhouseSettingForm({
}

function TeamQueryConfigSection() {
const displayValueWithUnit =
(unit: string) => (value: any, defaultValue?: any) =>
value === undefined || value === defaultValue
? `${defaultValue.toLocaleString()} ${unit} (System Default)`
: value === 0
? 'Unlimited'
: `${value.toLocaleString()} ${unit}`;

return (
<Box id="team_name">
<Text size="md" c="gray.4">
Expand All @@ -1271,26 +1280,30 @@ function TeamQueryConfigSection() {
tooltip="The number of rows per query for the Search page or search dashboard tiles"
type="number"
defaultValue={DEFAULT_SEARCH_ROW_LIMIT}
placeholder={`Enter value (default: ${DEFAULT_SEARCH_ROW_LIMIT})`}
placeholder={`default = ${DEFAULT_SEARCH_ROW_LIMIT}, 0 = unlimited`}
min={1}
max={100000}
displayValue={value => value ?? 'System Default'}
displayValue={displayValueWithUnit('rows')}
/>
<ClickhouseSettingForm
settingKey="queryTimeout"
label="Query Timeout (seconds)"
tooltip="Sets the max execution time of a query in seconds."
type="number"
defaultValue={DEFAULT_QUERY_TIMEOUT}
placeholder={`default = ${DEFAULT_QUERY_TIMEOUT}, 0 = unlimited`}
min={0}
displayValue={displayValueWithUnit('seconds')}
/>
<ClickhouseSettingForm
settingKey="metadataMaxRowsToRead"
label="Max Rows to Read (METADATA ONLY)"
tooltip="The maximum number of rows that can be read from a table when running a query"
type="number"
defaultValue={DEFAULT_METADATA_MAX_ROWS_TO_READ}
placeholder={`Enter value (default: ${DEFAULT_METADATA_MAX_ROWS_TO_READ.toLocaleString()}, 0 = unlimited)`}
placeholder={`default = ${DEFAULT_METADATA_MAX_ROWS_TO_READ.toLocaleString()}, 0 = unlimited`}
min={0}
displayValue={value =>
value == null
? `System Default (${DEFAULT_METADATA_MAX_ROWS_TO_READ.toLocaleString()})`
: value === 0
? 'Unlimited'
: value.toLocaleString()
}
displayValue={displayValueWithUnit('rows')}
/>
<ClickhouseSettingForm
settingKey="fieldMetadataDisabled"
Expand Down
25 changes: 24 additions & 1 deletion packages/app/src/clickhouse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import {
chSql,
ClickhouseClientOptions,
ColumnMeta,
ResponseJSON,
} from '@hyperdx/common-utils/dist/clickhouse';
Expand All @@ -16,28 +17,50 @@ import { useQuery, UseQueryOptions } from '@tanstack/react-query';
import { IS_LOCAL_MODE } from '@/config';
import { getLocalConnections } from '@/connection';

import api from './api';
import { DEFAULT_QUERY_TIMEOUT } from './defaults';

const PROXY_CLICKHOUSE_HOST = '/api/clickhouse-proxy';

export const getClickhouseClient = () => {
export const getClickhouseClient = (
options: ClickhouseClientOptions = {},
): ClickhouseClient => {
if (IS_LOCAL_MODE) {
const localConnections = getLocalConnections();
if (localConnections.length === 0) {
console.warn('No local connection found');
return new ClickhouseClient({
host: '',
...options,
});
}
return new ClickhouseClient({
host: localConnections[0].host,
username: localConnections[0].username,
password: localConnections[0].password,
...options,
});
}
return new ClickhouseClient({
host: PROXY_CLICKHOUSE_HOST,
...options,
});
};

export const useClickhouseClient = (
options: ClickhouseClientOptions = {},
): ClickhouseClient => {
const { data: me } = api.useMe();
const teamQueryTimeout = me?.team?.queryTimeout;
if (teamQueryTimeout !== undefined) {
options.queryTimeout = teamQueryTimeout;
} else {
options.queryTimeout = DEFAULT_QUERY_TIMEOUT;
}

return getClickhouseClient(options);
};

export function useDatabasesDirect(
{ connectionId }: { connectionId: string },
options?: Omit<UseQueryOptions<any, Error>, 'queryKey'>,
Expand Down
4 changes: 2 additions & 2 deletions packages/app/src/components/KubeComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import { Anchor, Badge, Group, Text, Timeline } from '@mantine/core';
import { useQuery, UseQueryOptions } from '@tanstack/react-query';

import { getClickhouseClient } from '@/clickhouse';
import { useClickhouseClient } from '@/clickhouse';
import { getMetadata } from '@/metadata';
import { getDisplayedTimestampValueExpression, getEventBody } from '@/source';

Expand Down Expand Up @@ -56,7 +56,7 @@ export const useV2LogBatch = <T = any,>(
},
options?: Omit<UseQueryOptions<any>, 'queryKey' | 'queryFn'>,
) => {
const clickhouseClient = getClickhouseClient();
const clickhouseClient = useClickhouseClient();
return useQuery<ResponseJSON<T>, Error>({
queryKey: [
'v2LogBatch',
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ChartConfigWithDateRange } from '@hyperdx/common-utils/dist/types'

// Limit defaults
export const DEFAULT_SEARCH_ROW_LIMIT = 200;
export const DEFAULT_QUERY_TIMEOUT = 60; // max_execution_time, seconds

export function searchChartConfigDefaults(
team: any | undefined | null,
Expand Down
4 changes: 2 additions & 2 deletions packages/app/src/hooks/useChartConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { format } from '@hyperdx/common-utils/dist/sqlFormatter';
import { ChartConfigWithOptDateRange } from '@hyperdx/common-utils/dist/types';
import { useQuery, UseQueryOptions } from '@tanstack/react-query';

import { getClickhouseClient } from '@/clickhouse';
import { useClickhouseClient } from '@/clickhouse';
import { IS_MTVIEWS_ENABLED } from '@/config';
import { buildMTViewSelectQuery } from '@/hdxMTViews';
import { getMetadata } from '@/metadata';
Expand All @@ -29,7 +29,7 @@ export function useQueriedChartConfig(
options?: Partial<UseQueryOptions<ResponseJSON<any>>> &
AdditionalUseQueriedChartConfigOptions,
) {
const clickhouseClient = getClickhouseClient();
const clickhouseClient = useClickhouseClient();
const query = useQuery<ResponseJSON<any>, ClickHouseQueryError | Error>({
queryKey: [config],
queryFn: async ({ signal }) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/app/src/hooks/useExplainQuery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { renderChartConfig } from '@hyperdx/common-utils/dist/renderChartConfig'
import { ChartConfigWithDateRange } from '@hyperdx/common-utils/dist/types';
import { useQuery, UseQueryOptions } from '@tanstack/react-query';

import { getClickhouseClient } from '@/clickhouse';
import { useClickhouseClient } from '@/clickhouse';
import { getMetadata } from '@/metadata';

export function useExplainQuery(
Expand All @@ -13,7 +13,7 @@ export function useExplainQuery(
..._config,
with: undefined,
};
const clickhouseClient = getClickhouseClient();
const clickhouseClient = useClickhouseClient();
const { data, isLoading, error } = useQuery({
queryKey: ['explain', config],
queryFn: async ({ signal }) => {
Expand Down
33 changes: 23 additions & 10 deletions packages/app/src/hooks/useOffsetPaginatedQuery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,22 @@ import {
useQueryClient,
} from '@tanstack/react-query';

import api from '@/api';
import { getClickhouseClient } from '@/clickhouse';
import { getMetadata } from '@/metadata';
import { omit } from '@/utils';

function queryKeyFn(prefix: string, config: ChartConfigWithDateRange) {
return [prefix, config] as const;
type TQueryKey = readonly [
string,
ChartConfigWithDateRange,
number | undefined,
];
function queryKeyFn(
prefix: string,
config: ChartConfigWithDateRange,
queryTimeout?: number,
): TQueryKey {
return [prefix, config, queryTimeout];
}

type TPageParam = number;
Expand All @@ -34,11 +44,12 @@ type TData = {
pageParams: TPageParam[];
};

const queryFn: QueryFunction<
TQueryFnData,
readonly [string, ChartConfigWithDateRange],
number
> = async ({ queryKey, pageParam, signal, meta }) => {
const queryFn: QueryFunction<TQueryFnData, TQueryKey, number> = async ({
queryKey,
pageParam,
signal,
meta,
}) => {
if (meta == null) {
throw new Error('Query missing client meta');
}
Expand All @@ -60,7 +71,8 @@ const queryFn: QueryFunction<
getMetadata(),
);

const clickhouseClient = getClickhouseClient();
const queryTimeout = queryKey[2];
const clickhouseClient = getClickhouseClient({ queryTimeout });
const resultSet =
await clickhouseClient.query<'JSONCompactEachRowWithNamesAndTypes'>({
query: query.sql,
Expand Down Expand Up @@ -247,7 +259,8 @@ export default function useOffsetPaginatedQuery(
queryKeyPrefix?: string;
} = {},
) {
const key = queryKeyFn(queryKeyPrefix, config);
const { data: meData } = api.useMe();
const key = queryKeyFn(queryKeyPrefix, config, meData?.team?.queryTimeout);
const queryClient = useQueryClient();
const matchedQueries = queryClient.getQueriesData<TData>({
queryKey: [queryKeyPrefix, omit(config, ['dateRange'])],
Expand All @@ -268,7 +281,7 @@ export default function useOffsetPaginatedQuery(
TQueryFnData,
Error | ClickHouseQueryError,
TData,
Readonly<[string, typeof config]>,
TQueryKey,
TPageParam
>({
queryKey: key,
Expand Down
5 changes: 4 additions & 1 deletion packages/app/src/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ import { getMetadata as _getMetadata } from '@hyperdx/common-utils/dist/metadata

import { getClickhouseClient } from '@/clickhouse';

import { DEFAULT_QUERY_TIMEOUT } from './defaults';

// TODO: Get rid of this function and convert to singleton
export const getMetadata = () => _getMetadata(getClickhouseClient());
export const getMetadata = () =>
_getMetadata(getClickhouseClient({ queryTimeout: DEFAULT_QUERY_TIMEOUT }));
Loading