From 6e89fb2306126a7aae894478fa6e60d9d82c9d04 Mon Sep 17 00:00:00 2001 From: Duyet Le Date: Mon, 25 Nov 2024 18:06:15 +0700 Subject: [PATCH 1/2] feat: enhance query handling with cluster support and improve UI components --- app/[host]/query/[query_id]/config.ts | 8 +- .../query/[query_id]/dropdown-cluster.tsx | 108 +++++ app/[host]/query/[query_id]/page.tsx | 14 +- .../query/[query_id]/query-detail-badge.tsx | 58 +++ .../query/[query_id]/query-detail-card.tsx | 356 ++++++++++++++++ app/[host]/query/[query_id]/query-detail.tsx | 395 ++---------------- app/[host]/query/[query_id]/types.ts | 2 +- components/clickhouse-host.tsx | 20 +- components/skeleton.tsx | 1 + components/table.tsx | 5 +- lib/clickhouse.ts | 43 +- 11 files changed, 622 insertions(+), 388 deletions(-) create mode 100644 app/[host]/query/[query_id]/dropdown-cluster.tsx create mode 100644 app/[host]/query/[query_id]/query-detail-badge.tsx create mode 100644 app/[host]/query/[query_id]/query-detail-card.tsx diff --git a/app/[host]/query/[query_id]/config.ts b/app/[host]/query/[query_id]/config.ts index 0a0f6dcc..dfe9e03a 100644 --- a/app/[host]/query/[query_id]/config.ts +++ b/app/[host]/query/[query_id]/config.ts @@ -65,6 +65,8 @@ export const config: QueryConfig = { query_cache_usage, query_duration_ms as duration_ms, query_duration_ms / 1000 as duration, + event_time_microseconds, + query_start_time_microseconds, -- The time in seconds since this stage started (now() - query_start_time) as elapsed, @@ -136,12 +138,14 @@ export const config: QueryConfig = { toString(transaction_id) as transaction_id FROM system.query_log WHERE - query_id = {query_id: String} - ORDER BY query_start_time DESC + initial_query_id = {query_id: String} + ORDER BY event_time_microseconds LIMIT 1000 `, columns: [ + 'hostname', 'type', + 'query_start_time_microseconds', 'readable_elapsed', 'duration_ms', 'readable_memory_usage', diff --git a/app/[host]/query/[query_id]/dropdown-cluster.tsx b/app/[host]/query/[query_id]/dropdown-cluster.tsx new file mode 100644 index 00000000..10e9260a --- /dev/null +++ b/app/[host]/query/[query_id]/dropdown-cluster.tsx @@ -0,0 +1,108 @@ +import { ErrorAlert } from '@/components/error-alert' +import { Button } from '@/components/ui/button' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuLabel, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu' +import { fetchData } from '@/lib/clickhouse' + +import { getHostIdCookie } from '@/lib/scoped-link' +import { CircleCheckIcon, CombineIcon } from 'lucide-react' +import { PageProps } from './types' + +interface RowData { + cluster: string + replica_count: number +} + +const sql = ` + SELECT DISTINCT + cluster, + count(replica_num) AS replica_count + FROM system.clusters + GROUP BY 1 +` + +export async function DropdownCluster({ + params, + searchParams, + className, +}: { + params: Awaited + searchParams: Awaited + className?: string +}) { + const { query_id } = params + const { cluster } = searchParams + const hostId = await getHostIdCookie() + const path = `/${hostId}/query/${query_id}/` + + try { + const { data } = await fetchData({ + query: sql, + format: 'JSONEachRow', + clickhouse_settings: { + use_query_cache: 0, + }, + hostId, + }) + + if (!data.length) { + return null + } + + return ( +
+ + + + + + Query Across Cluster + + + {data.map((row) => ( + + + {row.cluster} ({row.replica_count} replicas) + + + ))} + + + +
+ ) + } catch (error) { + return ( + + ) + } +} diff --git a/app/[host]/query/[query_id]/page.tsx b/app/[host]/query/[query_id]/page.tsx index c9011cdf..8fc1318d 100644 --- a/app/[host]/query/[query_id]/page.tsx +++ b/app/[host]/query/[query_id]/page.tsx @@ -12,6 +12,7 @@ export const revalidate = 300 export default async function Page({ params, searchParams }: PageProps) { const { query_id } = await params + const { cluster } = await searchParams // Binding the query_id to the config const queryConfig = { @@ -21,6 +22,13 @@ export default async function Page({ params, searchParams }: PageProps) { }, } + if (cluster) { + queryConfig.sql = queryConfig.sql.replace( + 'FROM system.query_log', + `FROM clusterAllReplicas('${cluster}', system.query_log)` + ) + } + return (
}> @@ -28,7 +36,11 @@ export default async function Page({ params, searchParams }: PageProps) { }> - + }> diff --git a/app/[host]/query/[query_id]/query-detail-badge.tsx b/app/[host]/query/[query_id]/query-detail-badge.tsx new file mode 100644 index 00000000..7aca6d32 --- /dev/null +++ b/app/[host]/query/[query_id]/query-detail-badge.tsx @@ -0,0 +1,58 @@ +import { Badge } from '@/components/ui/badge' +import { fetchData } from '@/lib/clickhouse' +import { getHostIdCookie } from '@/lib/scoped-link' +import { QueryConfig } from '@/types/query-config' +import { type RowData } from './config' +import { PageProps } from './types' + +export async function QueryDetailBadge({ + queryConfig, + params, +}: { + queryConfig: QueryConfig + params: Awaited + searchParams: Awaited +}) { + try { + const queryParams = { + ...queryConfig.defaultParams, + ...params, + } + const { data } = await fetchData({ + query: queryConfig.sql, + format: 'JSONEachRow', + query_params: queryParams, + clickhouse_settings: { + use_query_cache: 0, + ...queryConfig.clickhouseSettings, + }, + hostId: await getHostIdCookie(), + }) + + if (!data.length) { + return
No data
+ } + + const { user } = data[0] + const finalType = data[data.length - 1].type + const query_duration_ms = data + .map((row) => parseInt(row.duration_ms)) + .reduce((a, b) => a + b, 0) + + return ( + <> + + {query_duration_ms} ms + + + {finalType || 'Unknown'} + + + {user || 'Unknown'} + + + ) + } catch (error) { + return null + } +} diff --git a/app/[host]/query/[query_id]/query-detail-card.tsx b/app/[host]/query/[query_id]/query-detail-card.tsx new file mode 100644 index 00000000..5b85c6b4 --- /dev/null +++ b/app/[host]/query/[query_id]/query-detail-card.tsx @@ -0,0 +1,356 @@ +import { ErrorAlert } from '@/components/error-alert' +import { TruncatedList } from '@/components/truncated-list' +import { TruncatedParagraph } from '@/components/truncated-paragraph' +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion' +import { fetchData } from '@/lib/clickhouse' +import { formatQuery } from '@/lib/format-readable' +import { getHostIdCookie, getScopedLink } from '@/lib/scoped-link' +import { dedent } from '@/lib/utils' +import { QueryConfig } from '@/types/query-config' +import { ExternalLinkIcon } from 'lucide-react' +import Link from 'next/link' +import { type RowData } from './config' +import { PageProps } from './types' + +export async function QueryDetailCard({ + queryConfig, + params, +}: { + queryConfig: QueryConfig + params: Awaited + searchParams: Awaited +}) { + try { + const queryParams = { + ...queryConfig.defaultParams, + ...params, + } + const { data } = await fetchData({ + query: queryConfig.sql, + format: 'JSONEachRow', + query_params: queryParams, + clickhouse_settings: { + use_query_cache: 0, + ...queryConfig.clickhouseSettings, + }, + hostId: await getHostIdCookie(), + }) + + if (!data.length) { + return
No data
+ } + + const { query } = data[0] + + // Details + const { + hostname, + client_hostname, + client_name, + client_revision, + initial_user, + initial_query_id, + initial_address, + initial_port, + initial_query_start_time, + databases, + tables, + columns, + partitions, + projections, + views, + exception_code, + exception, + interface_query_initial_from, + stack_trace, + http_method, + http_user_agent, + http_referer, + forwarded_for, + quota_key, + distributed_depth, + revision, + log_comment, + ProfileEvents, + Settings, + used_aggregate_functions, + used_aggregate_function_combinators, + used_database_engines, + used_data_type_families, + used_dictionaries, + used_formats, + used_functions, + used_storages, + used_table_functions, + used_row_policies, + used_privileges, + missing_privileges, + } = data.find((row) => row.type == 'QueryFinish') || data[0] + + return ( +
+
+ + + +
+ + {formatQuery({ + query, + comment_remove: true, + trim: true, + truncate: 100, + })} + +
+
+ +
{dedent(formatQuery({ query, trim: false }))}
+
+
+
+
+ +
+ {( + [ + ['Hostname of the server executing the query', hostname], + ['Client host name', client_hostname], + ['Client name', client_name], + ['Client revision', client_revision], + [ + 'Initial user (for distributed query execution)', + initial_user ? ( + + {initial_user} + + + ) : ( + '' + ), + ], + [ + 'Initial query id (for distributed query execution)', + + {initial_query_id} + + , + ], + [ + 'initial_address (IP address that the parent query was launched from)', + initial_address, + ], + [ + 'initial_port (The client port that was used to make the parent query)', + initial_port, + ], + [ + 'Initial query starting time (for distributed query execution)', + initial_query_start_time, + ], + ['Databases', bindingDatabaseLink(databases)], + ['Tables', bindingTableLink(tables)], + ['Columns', JSON.stringify(columns, null, 2)], + ['Partitions', JSON.stringify(partitions, null, 2)], + ['Projections', JSON.stringify(projections, null, 2)], + ['Views', JSON.stringify(views, null, 2)], + ['Exception code', exception_code], + ['Exception', exception], + ['Stack trace', stack_trace], + [ + 'Interface that the query was initiated from (1 — TCP, 2 — HTTP)', + interface_query_initial_from, + ], + ['HTTP method (0 = TCP, 1 = GET, 2 = POST)', http_method], + ['HTTP user agent', http_user_agent], + ['HTTP referer', http_referer], + ['Forwarded for', forwarded_for], + ['Quota key', quota_key], + ['Distributed depth', distributed_depth], + ['Revision', revision], + ['Log Comment', log_comment], + [ + { + key: 'Profile events', + link: 'https://clickhouse.com/docs/en/operations/system-tables/metrics', + }, + JSON.stringify(ProfileEvents, null, 2), + ], + [ + { + key: 'Settings', + link: 'https://clickhouse.com/docs/en/operations/server-configuration-parameters/settings', + }, + JSON.stringify(Settings, null, 2), + ], + [ + 'Used aggregate functions', + bindingReference(used_aggregate_functions), + ], + [ + 'Used aggregate function combinators', + JSON.stringify(used_aggregate_function_combinators, null, 2), + ], + [ + { + key: 'Used database engines', + link: 'https://clickhouse.com/docs/en/chdb/data-formats', + }, + JSON.stringify(used_database_engines, null, 2), + ], + [ + 'Used data type families', + JSON.stringify(used_data_type_families, null, 2), + ], + ['Used dictionaries', JSON.stringify(used_dictionaries, null, 2)], + [ + { + key: 'Used formats', + link: 'https://clickhouse.com/docs/en/chdb/data-formats', + }, + JSON.stringify(used_formats, null, 2), + ], + ['Used functions', bindingReference(used_functions)], + ['Used storages', JSON.stringify(used_storages, null, 2)], + [ + 'Used table functions', + JSON.stringify(used_table_functions, null, 2), + ], + ['Used row policies', JSON.stringify(used_row_policies, null, 2)], + ['Used privileges', JSON.stringify(used_privileges, null, 2)], + [ + 'Missing privileges', + JSON.stringify(missing_privileges, null, 2), + ], + ] as Array< + | [string, string | React.ReactNode[]] + | [{ key: string; link: string }, string | React.ReactNode[]] + > + ) + .filter( + ([_, value]) => + (Array.isArray(value) && value.length) || + (!!value && value !== '[]' && value !== '{}') + ) + .map(([key, value]) => ( +
+
+

+ {typeof key === 'string' ? ( + key + ) : ( + + {key.key} + + )} +

+ {typeof value === 'string' ? ( + + {value} + + ) : ( +
+ {value} +
+ )} +
+
+ ))} +
+
+ ) + } catch (error) { + return ( + + ) + } +} + +function bindingDatabaseLink(databases: Array): React.ReactNode[] { + return databases.map(async (database) => { + return ( + + {database} + + ) + }) +} + +function bindingTableLink(tables: Array): React.ReactNode[] { + return tables.map(async (databaseTable) => { + const [database, table] = databaseTable.split('.') + return ( + + {databaseTable} + + ) + }) +} + +function bindingReference(value: Array): React.ReactNode[] { + console.log('used_functionsused_functionsused_functions', value) + + const getSearchLink = (item: string) => { + const searchParams = new URLSearchParams({ + q: `repo:ClickHouse/ClickHouse path:docs/en/sql-reference path:*.md "# ${item}"`, + }) + let url = new URL(`https://github.com/search`) + url.search = searchParams.toString() + + return url.toString() + } + + return value.map((item) => { + return ( + + {item} + + ) + }) +} diff --git a/app/[host]/query/[query_id]/query-detail.tsx b/app/[host]/query/[query_id]/query-detail.tsx index a61ec596..57e11551 100644 --- a/app/[host]/query/[query_id]/query-detail.tsx +++ b/app/[host]/query/[query_id]/query-detail.tsx @@ -1,372 +1,61 @@ -import { ErrorAlert } from '@/components/error-alert' -import { TruncatedList } from '@/components/truncated-list' -import { TruncatedParagraph } from '@/components/truncated-paragraph' -import { - Accordion, - AccordionContent, - AccordionItem, - AccordionTrigger, -} from '@/components/ui/accordion' -import { Badge } from '@/components/ui/badge' -import { fetchData } from '@/lib/clickhouse' -import { formatQuery } from '@/lib/format-readable' -import { getScopedLink } from '@/lib/scoped-link' -import { dedent } from '@/lib/utils' +import { MultiLineSkeleton, SingleLineSkeleton } from '@/components/skeleton' import { QueryConfig } from '@/types/query-config' -import { ExternalLinkIcon } from 'lucide-react' -import Link from 'next/link' -import { type RowData } from './config' +import { Suspense } from 'react' +import { DropdownCluster } from './dropdown-cluster' +import { QueryDetailBadge } from './query-detail-badge' +import { QueryDetailCard } from './query-detail-card' import { PageProps } from './types' export async function QueryDetail({ queryConfig, params, + searchParams, }: { queryConfig: QueryConfig params: Awaited + searchParams: Awaited }) { - try { - const queryParams = { - ...queryConfig.defaultParams, - ...params, - } - const { data } = await fetchData({ - query: queryConfig.sql, - format: 'JSONEachRow', - query_params: queryParams, - clickhouse_settings: { - use_query_cache: 0, - ...queryConfig.clickhouseSettings, - }, - }) + const { query_id } = params - if (!data.length) { - return 'No data' - } - - const { query_id, query, user } = data[0] - const finalType = data[data.length - 1].type - const query_duration_ms = data - .map((row) => parseInt(row.duration_ms)) - .reduce((a, b) => a + b, 0) - - // Details - const { - hostname, - client_hostname, - client_name, - client_revision, - initial_user, - initial_query_id, - initial_address, - initial_port, - initial_query_start_time, - databases, - tables, - columns, - partitions, - projections, - views, - exception_code, - exception, - interface_query_initial_from, - stack_trace, - http_method, - http_user_agent, - http_referer, - forwarded_for, - quota_key, - distributed_depth, - revision, - log_comment, - ProfileEvents, - Settings, - used_aggregate_functions, - used_aggregate_function_combinators, - used_database_engines, - used_data_type_families, - used_dictionaries, - used_formats, - used_functions, - used_storages, - used_table_functions, - used_row_policies, - used_privileges, - missing_privileges, - } = data.find((row) => row.type == 'QueryFinish') || data[0] - - return ( -
+ return ( +
+

{query_id} - - {query_duration_ms} ms - - - {finalType || 'Unknown'} - - - {user || 'Unknown'} - -

-
- + } > - - -
- - {formatQuery({ - query, - comment_remove: true, - trim: true, - truncate: 100, - })} - -
-
- -
{dedent(formatQuery({ query, trim: false }))}
-
-
-
-
+ + + -
- {( - [ - ['Hostname of the server executing the query', hostname], - ['Client host name', client_hostname], - ['Client name', client_name], - ['Client revision', client_revision], - [ - 'Initial user (for distributed query execution)', - initial_user ? ( - - {initial_user} - - - ) : ( - '' - ), - ], - [ - 'Initial query id (for distributed query execution)', - - {initial_query_id} - - , - ], - [ - 'initial_address (IP address that the parent query was launched from)', - initial_address, - ], - [ - 'initial_port (The client port that was used to make the parent query)', - initial_port, - ], - [ - 'Initial query starting time (for distributed query execution)', - initial_query_start_time, - ], - ['Databases', bindingDatabaseLink(databases)], - ['Tables', bindingTableLink(tables)], - ['Columns', JSON.stringify(columns, null, 2)], - ['Partitions', JSON.stringify(partitions, null, 2)], - ['Projections', JSON.stringify(projections, null, 2)], - ['Views', JSON.stringify(views, null, 2)], - ['Exception code', exception_code], - ['Exception', exception], - ['Stack trace', stack_trace], - [ - 'Interface that the query was initiated from (1 — TCP, 2 — HTTP)', - interface_query_initial_from, - ], - ['HTTP method (0 = TCP, 1 = GET, 2 = POST)', http_method], - ['HTTP user agent', http_user_agent], - ['HTTP referer', http_referer], - ['Forwarded for', forwarded_for], - ['Quota key', quota_key], - ['Distributed depth', distributed_depth], - ['Revision', revision], - ['Log Comment', log_comment], - [ - { - key: 'Profile events', - link: 'https://clickhouse.com/docs/en/operations/system-tables/metrics', - }, - JSON.stringify(ProfileEvents, null, 2), - ], - [ - { - key: 'Settings', - link: 'https://clickhouse.com/docs/en/operations/server-configuration-parameters/settings', - }, - JSON.stringify(Settings, null, 2), - ], - [ - 'Used aggregate functions', - bindingReference(used_aggregate_functions), - ], - [ - 'Used aggregate function combinators', - JSON.stringify(used_aggregate_function_combinators, null, 2), - ], - [ - { - key: 'Used database engines', - link: 'https://clickhouse.com/docs/en/chdb/data-formats', - }, - JSON.stringify(used_database_engines, null, 2), - ], - [ - 'Used data type families', - JSON.stringify(used_data_type_families, null, 2), - ], - ['Used dictionaries', JSON.stringify(used_dictionaries, null, 2)], - [ - { - key: 'Used formats', - link: 'https://clickhouse.com/docs/en/chdb/data-formats', - }, - JSON.stringify(used_formats, null, 2), - ], - ['Used functions', bindingReference(used_functions)], - ['Used storages', JSON.stringify(used_storages, null, 2)], - [ - 'Used table functions', - JSON.stringify(used_table_functions, null, 2), - ], - ['Used row policies', JSON.stringify(used_row_policies, null, 2)], - ['Used privileges', JSON.stringify(used_privileges, null, 2)], - [ - 'Missing privileges', - JSON.stringify(missing_privileges, null, 2), - ], - ] as Array< - | [string, string | React.ReactNode[]] - | [{ key: string; link: string }, string | React.ReactNode[]] - > - ) - .filter( - ([_, value]) => - (Array.isArray(value) && value.length) || - (!!value && value !== '[]' && value !== '{}') - ) - .map(([key, value]) => ( -
-
-

- {typeof key === 'string' ? ( - key - ) : ( - - {key.key} - - )} -

- {typeof value === 'string' ? ( - - {value} - - ) : ( -
- {value} -
- )} -
-
- ))} -
+ + } + > + +
- ) - } catch (error) { - return ( - - ) - } -} - -function bindingDatabaseLink(databases: Array): React.ReactNode[] { - return databases.map(async (database) => { - return ( - - {database} - - ) - }) -} - -function bindingTableLink(tables: Array): React.ReactNode[] { - return tables.map(async (databaseTable) => { - const [database, table] = databaseTable.split('.') - return ( - - {databaseTable} - - ) - }) -} - -function bindingReference(value: Array): React.ReactNode[] { - console.log('used_functionsused_functionsused_functions', value) - - const getSearchLink = (item: string) => { - const searchParams = new URLSearchParams({ - q: `repo:ClickHouse/ClickHouse path:docs/en/sql-reference path:*.md "# ${item}"`, - }) - let url = new URL(`https://github.com/search`) - url.search = searchParams.toString() - - return url.toString() - } - return value.map((item) => { - return ( - - {item} - - ) - }) + }> + + +
+ ) } diff --git a/app/[host]/query/[query_id]/types.ts b/app/[host]/query/[query_id]/types.ts index 50bd6305..77b8d232 100644 --- a/app/[host]/query/[query_id]/types.ts +++ b/app/[host]/query/[query_id]/types.ts @@ -2,5 +2,5 @@ export interface PageProps { params: Promise<{ query_id: string }> - searchParams: Promise<{ [key: string]: string | string[] | undefined }> + searchParams: Promise<{ cluster?: string }> } diff --git a/components/clickhouse-host.tsx b/components/clickhouse-host.tsx index fa761f5c..445aff26 100644 --- a/components/clickhouse-host.tsx +++ b/components/clickhouse-host.tsx @@ -12,17 +12,15 @@ async function fetchHostStatus(hostId: number) { try { const { data: detail } = await fetchData< { uptime: string; hostName: string; version: string }[] - >( - { - query: ` - SELECT - formatReadableTimeDelta(uptime()) as uptime, - hostName() as hostName, - version() as version - `, - }, - hostId - ) + >({ + query: ` + SELECT + formatReadableTimeDelta(uptime()) as uptime, + hostName() as hostName, + version() as version + `, + hostId, + }) return detail[0] } catch (e) { diff --git a/components/skeleton.tsx b/components/skeleton.tsx index 3a36bd9f..f582abff 100644 --- a/components/skeleton.tsx +++ b/components/skeleton.tsx @@ -70,6 +70,7 @@ export function MultiLineSkeleton({
+
) diff --git a/components/table.tsx b/components/table.tsx index 5ddf866c..76ee0a3e 100644 --- a/components/table.tsx +++ b/components/table.tsx @@ -21,6 +21,8 @@ export async function Table({ searchParams, className, }: TableProps) { + const hostId = await getHostIdCookie() + // Filters valid query parameters from the URL. const validQueryParams = Object.entries(searchParams).filter(([key, _]) => { return ( @@ -44,6 +46,7 @@ export async function Table({ max_execution_time: 300, ...queryConfig.clickhouseSettings, }, + hostId, }) const footerText = `${metadata.rows} row(s) in ${metadata.duration} seconds.` @@ -55,7 +58,7 @@ export async function Table({ queryConfig={queryConfig} queryParams={queryParams} data={data} - context={{ ...queryParams, hostId: '' + (await getHostIdCookie()) }} + context={{ ...queryParams, hostId: '' + hostId }} footnote={footerText} className={className} /> diff --git a/lib/clickhouse.ts b/lib/clickhouse.ts index 2266f772..bb7a4f3b 100644 --- a/lib/clickhouse.ts +++ b/lib/clickhouse.ts @@ -69,15 +69,19 @@ export const getClickHouseConfigs = (): ClickHouseConfig[] => { export const getClient = async ({ web, - clickhouse_settings, + clickhouseSettings, + clientConfig, hostId, }: { web?: B - clickhouse_settings?: ClickHouseSettings + clickhouseSettings?: ClickHouseSettings + clientConfig?: ClickHouseConfig hostId?: number }): Promise => { const client = web === true ? createClientWeb : createClient - const config = getClickHouseConfigs()[hostId || getHostId()] + const config = clientConfig + ? clientConfig + : getClickHouseConfigs()[hostId || getHostId()] const c = client({ host: config.host, @@ -88,7 +92,7 @@ export const getClient = async ({ process.env.CLICKHOUSE_MAX_EXECUTION_TIME ?? DEFAULT_CLICKHOUSE_MAX_EXECUTION_TIME ), - ...clickhouse_settings, + ...clickhouseSettings, }, }) @@ -103,25 +107,26 @@ export const fetchData = async < | object[] // format = '*EachRow' | Record // format = 'JSONObjectEachRow' | 'JSONColumns | { length: number; rows: number; statistics: Record }, // format = 'JSON' | 'JSONStrings' | 'JSONCompact' | 'JSONColumnsWithMetadata' | ... ->( - { - query, - query_params, - format = 'JSONEachRow', - clickhouse_settings, - }: QueryParams & - Partial<{ - clickhouse_settings: QuerySettings - }>, - hostId?: number | string -): Promise<{ +>({ + query, + query_params, + format = 'JSONEachRow', + clickhouse_settings, + hostId, +}: QueryParams & + Partial<{ + clickhouse_settings: QuerySettings + hostId?: number | string + }>): Promise<{ data: T metadata: Record }> => { const start = new Date() + const currentHostId = hostId ? Number(hostId) : getHostId() + const clientConfig = getClickHouseConfigs()[currentHostId] const client = await getClient({ web: false, - hostId: hostId ? Number(hostId) : undefined, + clientConfig, }) const resultSet = await client.query({ @@ -139,7 +144,7 @@ export const fetchData = async < let rows: number = 0 console.debug( - `--> Query (${query_id}):`, + `--> Query (${query_id}, host: ${clientConfig.host}):`, query.replace(/(\n|\s+)/g, ' ').replace(/\s+/g, ' ') ) @@ -175,7 +180,7 @@ export const query = async ( ) => { const client = await getClient({ web: false, - clickhouse_settings: {}, + clickhouseSettings: {}, }) const resultSet = await client.query({ query: QUERY_COMMENT + query, From d3afc9af89f25b77efa6148070d2cd656e251104 Mon Sep 17 00:00:00 2001 From: Duyet Le Date: Mon, 25 Nov 2024 21:58:44 +0700 Subject: [PATCH 2/2] fix: fix build --- app/[host]/query/[query_id]/query-detail-card.tsx | 2 -- lib/clickhouse-cache.ts | 7 +++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/[host]/query/[query_id]/query-detail-card.tsx b/app/[host]/query/[query_id]/query-detail-card.tsx index 5b85c6b4..c0998fb3 100644 --- a/app/[host]/query/[query_id]/query-detail-card.tsx +++ b/app/[host]/query/[query_id]/query-detail-card.tsx @@ -329,8 +329,6 @@ function bindingTableLink(tables: Array): React.ReactNode[] { } function bindingReference(value: Array): React.ReactNode[] { - console.log('used_functionsused_functionsused_functions', value) - const getSearchLink = (item: string) => { const searchParams = new URLSearchParams({ q: `repo:ClickHouse/ClickHouse path:docs/en/sql-reference path:*.md "# ${item}"`, diff --git a/lib/clickhouse-cache.ts b/lib/clickhouse-cache.ts index f63cd273..d0d6c642 100644 --- a/lib/clickhouse-cache.ts +++ b/lib/clickhouse-cache.ts @@ -1,4 +1,4 @@ -import { unstable_cache } from 'next/cache' +import { unstable_cache as cache } from 'next/cache' import { fetchData } from './clickhouse' export const CLICKHOUSE_CACHE_TAG = 'clickhouse_results' @@ -8,9 +8,8 @@ const NEXT_QUERY_CACHE_TTL = parseInt( 10 // Specify radix parameter ) // 60 minutes by default -export const fetchDataWithCache = unstable_cache( - (param: (typeof fetchData.arguments)[0], id?: number | string) => - fetchData(param, id), +export const fetchDataWithCache = cache( + (param: (typeof fetchData.arguments)[0]) => fetchData(param), undefined, { tags: [CLICKHOUSE_CACHE_TAG],