From ed8a5ea11ba747fe0b786cc2f9a35493d1087571 Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Tue, 8 Apr 2025 20:37:06 +0800 Subject: [PATCH] single queries shown on click --- .../QueryInsights/components/MetricsChart.tsx | 88 +++++++++++-------- .../single-query-latency-query.ts | 54 ++++++++++++ 2 files changed, 106 insertions(+), 36 deletions(-) create mode 100644 apps/studio/data/query-insights/single-query-latency-query.ts diff --git a/apps/studio/components/interfaces/QueryInsights/components/MetricsChart.tsx b/apps/studio/components/interfaces/QueryInsights/components/MetricsChart.tsx index a625450de3a..1c7dcc5911f 100644 --- a/apps/studio/components/interfaces/QueryInsights/components/MetricsChart.tsx +++ b/apps/studio/components/interfaces/QueryInsights/components/MetricsChart.tsx @@ -15,6 +15,7 @@ import { useState } from 'react' import { useParams } from 'common' import { Button } from 'ui' import { MetricType } from '../QueryInsights' +import { useSingleQueryLatency } from 'data/query-insights/single-query-latency-query' interface MetricsChartProps { data: QueryInsightsMetric[] @@ -33,12 +34,22 @@ export function MetricsChart({ endTime, selectedQuery, }: MetricsChartProps) { + const { ref } = useParams() const [visibleMetrics, setVisibleMetrics] = useState({ p50: true, p95: true, p99: true, p99_9: true, - query_exec_time: true, + query_latency: true, + }) + + // Fetch single query latency data if a query is selected + const { + data: queryLatencyData, + isLoading: isLoadingQueryLatency, + error: queryLatencyError, + } = useSingleQueryLatency(ref, selectedQuery?.query_id, startTime, endTime, { + enabled: !!selectedQuery, }) if (isLoading) { @@ -67,8 +78,8 @@ export function MetricsChart({ p95: point.p95, p99: point.p99, p99_9: point.p99_9, - // Add a placeholder for the query execution time - query_exec_time: null as unknown as number | undefined, + // Initialize query latency as 0 instead of null + query_latency: 0, } }) @@ -77,26 +88,22 @@ export function MetricsChart({ return dayjs(a.timestamp, 'HH:mm').diff(dayjs(b.timestamp, 'HH:mm')) }) - // If we have a selected query, add its execution time as a consistent line - if (selectedQuery) { - // For visualizing the query, use its mean execution time as a flat line - const queryExecTime = selectedQuery.mean_exec_time - - // Log for debugging - console.log( - 'Selected Query:', - selectedQuery.query_id, - selectedQuery.query.substring(0, 30) + '...' - ) - console.log('Query Execution Time:', queryExecTime) - - // Set the query execution time for all data points to create a flat line - chartData.forEach((dataPoint) => { - dataPoint.query_exec_time = queryExecTime + // If we have query latency data, add it to the chart + if (selectedQuery && queryLatencyData && queryLatencyData.length > 0) { + // Create a map of timestamps to query latency values + const queryLatencyMap = new Map() + queryLatencyData.forEach((point) => { + const timestamp = dayjs(point.timestamp).format('HH:mm') + queryLatencyMap.set(timestamp, point.value || 0) }) - // Log the chart data for debugging - console.log('Chart Data with Query:', chartData.slice(0, 2)) + // Add query latency data to each chart data point + // Points without data will keep their 0 value + chartData.forEach((point) => { + if (queryLatencyMap.has(point.timestamp)) { + point.query_latency = queryLatencyMap.get(point.timestamp)! + } + }) } const config = { @@ -116,9 +123,9 @@ export function MetricsChart({ label: 'p99.9', color: 'hsl(var(--destructive-default))', }, - query_exec_time: { - label: 'Query Execution Time', - color: 'hsl(var(--red-500))', + query_latency: { + label: 'Query Latency', + color: 'hsl(var(--foreground-default))', }, } satisfies ChartConfig @@ -138,14 +145,18 @@ export function MetricsChart({ Total Calls: {selectedQuery.calls.toLocaleString()} + {isLoadingQueryLatency && Loading query latency data...} + {Boolean(queryLatencyError) && ( + Error loading query latency + )} )}
{Object.entries(config).map(([key, value]) => { - // Only show query execution time button if we have a selected query - if (key === 'query_exec_time' && !selectedQuery) { + // Only show query latency button if we have a selected query + if (key === 'query_latency' && !selectedQuery) { return null } @@ -216,8 +227,9 @@ export function MetricsChart({ type="monotone" dataKey="p50" fill={config.p50.color} - fillOpacity={selectedQuery ? 0.2 : 0.4} + fillOpacity={selectedQuery ? 0.05 : 0.4} stroke={config.p50.color} + strokeOpacity={selectedQuery ? 0.1 : 0.8} strokeWidth={2} dot={false} animationDuration={100} @@ -228,8 +240,9 @@ export function MetricsChart({ type="monotone" dataKey="p95" fill={config.p95.color} - fillOpacity={selectedQuery ? 0.2 : 0.4} + fillOpacity={selectedQuery ? 0.05 : 0.4} stroke={config.p95.color} + strokeOpacity={selectedQuery ? 0.1 : 0.8} strokeWidth={2} dot={false} animationDuration={100} @@ -240,8 +253,9 @@ export function MetricsChart({ type="monotone" dataKey="p99" fill={config.p99.color} - fillOpacity={selectedQuery ? 0.2 : 0.4} + fillOpacity={selectedQuery ? 0.05 : 0.4} stroke={config.p99.color} + strokeOpacity={selectedQuery ? 0.1 : 0.8} strokeWidth={2} dot={false} animationDuration={100} @@ -252,21 +266,23 @@ export function MetricsChart({ type="monotone" dataKey="p99_9" fill={config.p99_9.color} - fillOpacity={selectedQuery ? 0.2 : 0.4} + fillOpacity={selectedQuery ? 0.05 : 0.4} stroke={config.p99_9.color} + strokeOpacity={selectedQuery ? 0.1 : 0.8} strokeWidth={2} dot={false} animationDuration={100} /> )} - {selectedQuery && visibleMetrics.query_exec_time && ( + {selectedQuery && visibleMetrics.query_latency && ( diff --git a/apps/studio/data/query-insights/single-query-latency-query.ts b/apps/studio/data/query-insights/single-query-latency-query.ts new file mode 100644 index 00000000000..6f6a6a2b3ca --- /dev/null +++ b/apps/studio/data/query-insights/single-query-latency-query.ts @@ -0,0 +1,54 @@ +import { useQuery, UseQueryOptions } from '@tanstack/react-query' +import { executeSql } from '../sql/execute-sql-query' +import { QueryInsightsMetric } from './query-insights-query' + +const singleQueryLatencyKeys = { + base: ['single-query-latency'] as const, + latency: ( + projectRef: string | undefined, + queryId: string | undefined, + startTime: string, + endTime: string + ) => [...singleQueryLatencyKeys.base, projectRef, queryId, startTime, endTime] as const, +} + +const getSingleQueryLatencySql = ( + queryId: string, + startTime: string, + endTime: string +) => /* SQL */ ` + SELECT + bucket_start_time as timestamp, + mean_exec_time as value, + datname as database + FROM pg_stat_monitor + WHERE queryid = '${queryId}' + AND bucket_start_time >= '${startTime}' + AND bucket_start_time <= '${endTime}' + ORDER BY bucket_start_time ASC +` + +export function useSingleQueryLatency( + projectRef: string | undefined, + queryId: string | undefined, + startTime: string, + endTime: string, + options?: UseQueryOptions +) { + return useQuery({ + queryKey: singleQueryLatencyKeys.latency(projectRef, queryId, startTime, endTime), + queryFn: async () => { + if (!projectRef) throw new Error('Project ref is required') + if (!queryId) return [] + + const { result } = await executeSql({ + projectRef, + sql: getSingleQueryLatencySql(queryId, startTime, endTime), + }) + + return result as QueryInsightsMetric[] + }, + enabled: !!queryId && !!projectRef, + ...options, + }) +}