import { InformationCircleIcon } from '@heroicons/react/16/solid' import { X } from 'lucide-react' import { useRouter } from 'next/router' import { useEffect, useMemo, useState } from 'react' import toast from 'react-hot-toast' import { useParams } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { executeSql } from 'data/sql/execute-sql-query' import { useLocalStorageQuery } from 'hooks' import { DbQueryHook } from 'hooks/analytics/useDbQuery' import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { useDatabaseSelectorStateSnapshot } from 'state/database-selector' import { Button, TabsList_Shadcn_, TabsTrigger_Shadcn_, Tabs_Shadcn_, TooltipContent_Shadcn_, TooltipTrigger_Shadcn_, Tooltip_Shadcn_, cn, } from 'ui' import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog' import ShimmeringLoader from 'ui-patterns/ShimmeringLoader' import { Markdown } from '../Markdown' import { useQueryPerformanceQuery } from '../Reports/Reports.queries' import { PresetHookResult } from '../Reports/Reports.utils' import { QUERY_PERFORMANCE_REPORT_TYPES } from './QueryPerformance.constants' import { QueryPerformanceFilterBar } from './QueryPerformanceFilterBar' import { QueryPerformanceGrid } from './QueryPerformanceGrid' interface QueryPerformanceProps { queryHitRate: PresetHookResult queryPerformanceQuery: DbQueryHook } export const QueryPerformance = ({ queryHitRate, queryPerformanceQuery, }: QueryPerformanceProps) => { const router = useRouter() const { ref, preset } = useParams() const { project } = useProjectContext() const state = useDatabaseSelectorStateSnapshot() const [page, setPage] = useState( (preset as QUERY_PERFORMANCE_REPORT_TYPES) ?? QUERY_PERFORMANCE_REPORT_TYPES.MOST_TIME_CONSUMING ) const [showResetgPgStatStatements, setShowResetgPgStatStatements] = useState(false) const [showBottomSection, setShowBottomSection] = useLocalStorageQuery( LOCAL_STORAGE_KEYS.QUERY_PERF_SHOW_BOTTOM_SECTION, true ) const handleRefresh = () => { queryPerformanceQuery.runQuery() queryHitRate.runQuery() } const { data: mostTimeConsumingQueries, isLoading: isLoadingMTC } = useQueryPerformanceQuery({ preset: 'mostTimeConsuming', }) const { data: mostFrequentlyInvoked, isLoading: isLoadingMFI } = useQueryPerformanceQuery({ preset: 'mostFrequentlyInvoked', }) const { data: slowestExecutionTime, isLoading: isLoadingMMF } = useQueryPerformanceQuery({ preset: 'slowestExecutionTime', }) const QUERY_PERFORMANCE_TABS = useMemo(() => { return [ { id: QUERY_PERFORMANCE_REPORT_TYPES.MOST_TIME_CONSUMING, label: 'Most time consuming', description: 'Lists queries ordered by their cumulative total execution time.', isLoading: isLoadingMTC, max: (mostTimeConsumingQueries ?? []).length > 0 ? Math.max(...(mostTimeConsumingQueries ?? []).map((x: any) => x.total_time)).toFixed(2) : undefined, }, { id: QUERY_PERFORMANCE_REPORT_TYPES.MOST_FREQUENT, label: 'Most frequent', description: 'Lists queries in order of their execution count', isLoading: isLoadingMFI, max: (mostFrequentlyInvoked ?? []).length > 0 ? Math.max(...(mostFrequentlyInvoked ?? []).map((x: any) => x.calls)).toFixed(2) : undefined, }, { id: QUERY_PERFORMANCE_REPORT_TYPES.SLOWEST_EXECUTION, label: 'Slowest execution', description: 'Lists queries ordered by their maximum execution time', isLoading: isLoadingMMF, max: (slowestExecutionTime ?? []).length > 0 ? Math.max(...(slowestExecutionTime ?? []).map((x: any) => x.max_time)).toFixed(2) : undefined, }, ] }, [ isLoadingMFI, isLoadingMMF, isLoadingMTC, mostFrequentlyInvoked, mostTimeConsumingQueries, slowestExecutionTime, ]) useEffect(() => { state.setSelectedDatabaseId(ref) }, [ref]) return ( <> { setPage(value as QUERY_PERFORMANCE_REPORT_TYPES) const { sort, search, ...rest } = router.query router.push({ ...router, query: { ...rest, preset: value } }) }} > {QUERY_PERFORMANCE_TABS.map((tab) => ( {tab.id === page && (
)}
{tab.label} {tab.description}
{tab.isLoading ? ( ) : tab.max === undefined ? ( No data yet ) : ( {Number(tab.max).toLocaleString()} {tab.id !== QUERY_PERFORMANCE_REPORT_TYPES.MOST_FREQUENT ? 'ms' : ' calls'} )} {tab.id === page && (
)} ))}
{ setShowResetgPgStatStatements(true) }} />

Reset report

Consider resetting the analysis after optimizing any queries

How is this report generated?

Inspect your database for potential issues

setShowResetgPgStatStatements(false)} onSelectConfirm={async () => { try { await executeSql({ projectRef: project?.ref, connectionString: project?.connectionString, sql: `SELECT pg_stat_statements_reset();`, }) handleRefresh() setShowResetgPgStatStatements(false) } catch (error: any) { toast.error(`Failed to reset analysis: ${error.message}`) } }} /> ) }