Skip to content

Commit 0b61bfa

Browse files
feat(Performance): use pg_stat_statements for PostgreSQL without log source
- Add Analysis tab that uses pg_stat_statements directly - Filter tabs based on whether log source is available - Default to Analysis tab for PostgreSQL connections without log source - Add info banner explaining pg_stat_statements with option to add log source - Keep trends/customers/workload tabs available when log source is configured Co-authored-by: Venkat SF <venkatesh.sakamuri@stayflexi.com>
1 parent d3da0b6 commit 0b61bfa

2 files changed

Lines changed: 90 additions & 14 deletions

File tree

‎src/components/sections/SlowQueriesSection.jsx‎

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
import { useRef, useState } from 'react'
2-
import { Activity, FileText, LineChart, Loader2, Settings, Users } from 'lucide-react'
2+
import { Activity, FileText, LineChart, Loader2, Settings, Users, Database } from 'lucide-react'
33
import { useConnectionManager } from '@/lib/hooks/useConnectionManager'
44
import { useSlowLogSourceConfig } from '@/lib/hooks/queries'
55
import QueryTrendsTab from '@/components/tabs/Performance/QueryTrendsTab'
66
import CustomerExplorer from '@/components/tabs/Performance/CustomerExplorer'
77
import SlowQuerySettingsPanel from '@/components/tabs/Performance/SlowQuerySettingsPanel'
88
import WorkloadAnalysisPanel from '@/components/tabs/Performance/WorkloadAnalysisPanel'
99
import SlowQuerySourceModal from '@/components/SlowQuerySourceModal'
10+
import SlowQueryAnalysisTab from '@/components/tabs/Performance/SlowQueryAnalysisTab'
1011
import { HelpTooltip } from '@/components/tabs/Brain/components/HelpTooltip'
1112
import sectionStyles from './TopLevelSection.module.css'
1213
import styles from './SlowQueriesSection.module.css'
1314

1415
const TABS = [
15-
{ id: 'trends', label: 'Query Trends', icon: LineChart },
16-
{ id: 'customers', label: 'By Customer', icon: Users },
17-
{ id: 'workload', label: 'Workload', icon: Activity },
18-
{ id: 'settings', label: 'Settings', icon: Settings },
16+
{ id: 'analysis', label: 'Analysis', icon: Database, requiresLogSource: false },
17+
{ id: 'trends', label: 'Query Trends', icon: LineChart, requiresLogSource: true },
18+
{ id: 'customers', label: 'By Customer', icon: Users, requiresLogSource: true },
19+
{ id: 'workload', label: 'Workload', icon: Activity, requiresLogSource: true },
20+
{ id: 'settings', label: 'Settings', icon: Settings, requiresLogSource: false },
1921
]
2022

2123
const LOG_SOURCE_HELP = {
@@ -24,6 +26,12 @@ const LOG_SOURCE_HELP = {
2426
'Query trends, per-customer load, and workload analysis all read from ingested slow-query logs. Attach CloudWatch, S3, Azure, GCP, Datadog, Elasticsearch, or a file upload before those views can run.',
2527
}
2628

29+
const PG_STAT_HELP = {
30+
title: 'pg_stat_statements',
31+
description:
32+
'PostgreSQL connections use pg_stat_statements for real-time query analysis. No external log source needed. The Analysis tab shows top queries by execution time with index recommendations.',
33+
}
34+
2735
/**
2836
* Combined Performance section — Slow Queries + Workload Analysis.
2937
*
@@ -32,8 +40,20 @@ const LOG_SOURCE_HELP = {
3240
*/
3341
export default function SlowQueriesSection() {
3442
const { connectionId, selectedConnection, isLoading } = useConnectionManager()
35-
const [tab, setTab] = useState('trends')
3643
const tabRefs = useRef({})
44+
const logSourceQ = useSlowLogSourceConfig(connectionId)
45+
const hasLogSource = Boolean(logSourceQ.data?.id)
46+
47+
// PostgreSQL connections can use pg_stat_statements directly without a log source
48+
const isPostgres = selectedConnection?.dbType?.toLowerCase() === 'postgresql'
49+
const canShowPerformance = hasLogSource || isPostgres
50+
51+
// Filter tabs based on available data sources
52+
const availableTabs = TABS.filter((t) => !t.requiresLogSource || hasLogSource)
53+
54+
// Default to 'analysis' for PostgreSQL without log source, otherwise 'trends'
55+
const defaultTab = (isPostgres && !hasLogSource) ? 'analysis' : 'trends'
56+
const [tab, setTab] = useState(defaultTab)
3757

3858
/** Arrow / Home / End move between tabs, as the ARIA tabs pattern expects. */
3959
const onTabKeyDown = (e) => {
@@ -44,20 +64,18 @@ export default function SlowQueriesSection() {
4464
// handler was created: two keypresses within one render would otherwise both move
4565
// relative to the same starting index and selection would stick after the first.
4666
setTab((current) => {
47-
const i = TABS.findIndex((t) => t.id === current)
67+
const i = availableTabs.findIndex((t) => t.id === current)
4868
const next = e.key === 'Home' ? 0
49-
: e.key === 'End' ? TABS.length - 1
50-
: (i + step + TABS.length) % TABS.length
51-
const id = TABS[next].id
69+
: e.key === 'End' ? availableTabs.length - 1
70+
: (i + step + availableTabs.length) % availableTabs.length
71+
const id = availableTabs[next].id
5272
// Focus follows selection, per the ARIA tabs pattern. Deferred so the tab is
5373
// already rendered with tabIndex=0 when we focus it.
5474
queueMicrotask(() => tabRefs.current[id]?.focus())
5575
return id
5676
})
5777
}
5878
const [logSourceModalOpen, setLogSourceModalOpen] = useState(false)
59-
const logSourceQ = useSlowLogSourceConfig(connectionId)
60-
const hasLogSource = Boolean(logSourceQ.data?.id)
6179

6280
// Wait for connection list to load before rendering anything
6381
if (isLoading) {
@@ -97,7 +115,7 @@ export default function SlowQueriesSection() {
97115
<div className={styles.empty}>
98116
Could not load the slow query log configuration for this connection.
99117
</div>
100-
) : !hasLogSource ? (
118+
) : !canShowPerformance ? (
101119
<div className={styles.setupCard}>
102120
<FileText size={32} className={styles.setupIcon} />
103121
<h2 className={styles.setupTitle}>Configure slow queries</h2>
@@ -131,7 +149,7 @@ export default function SlowQueriesSection() {
131149
aria-label="Performance views"
132150
onKeyDown={onTabKeyDown}
133151
>
134-
{TABS.map((t) => {
152+
{availableTabs.map((t) => {
135153
const Icon = t.icon
136154
const active = tab === t.id
137155
return (
@@ -166,13 +184,33 @@ export default function SlowQueriesSection() {
166184
</button>
167185
</div>
168186

187+
{/* Show pg_stat_statements info banner for PostgreSQL without log source */}
188+
{isPostgres && !hasLogSource && (
189+
<div className={styles.pgStatBanner}>
190+
<HelpTooltip content={PG_STAT_HELP}>
191+
<span className={styles.pgStatBannerText}>
192+
<Database size={14} />
193+
Using pg_stat_statements for real-time query analysis.
194+
</span>
195+
</HelpTooltip>
196+
<button
197+
type="button"
198+
className={styles.pgStatBannerLink}
199+
onClick={() => setLogSourceModalOpen(true)}
200+
>
201+
Add log source for trends &amp; workload
202+
</button>
203+
</div>
204+
)}
205+
169206
<div
170207
className={styles.content}
171208
role="tabpanel"
172209
id={`perf-panel-${tab}`}
173210
aria-labelledby={`perf-tab-${tab}`}
174211
tabIndex={0}
175212
>
213+
{tab === 'analysis' && <SlowQueryAnalysisTab connectionId={connectionId} />}
176214
{tab === 'trends' && <QueryTrendsTab connectionId={connectionId} />}
177215
{tab === 'customers' && <CustomerExplorer connectionId={connectionId} />}
178216
{tab === 'workload' && <WorkloadAnalysisPanel connectionId={connectionId} />}

‎src/components/sections/SlowQueriesSection.module.css‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,41 @@
152152
@keyframes spin {
153153
to { transform: rotate(360deg); }
154154
}
155+
156+
.pgStatBanner {
157+
display: flex;
158+
align-items: center;
159+
justify-content: space-between;
160+
gap: 12px;
161+
padding: 10px 14px;
162+
margin-bottom: 16px;
163+
border: 1px solid #d1fae5;
164+
border-radius: 10px;
165+
background: #ecfdf5;
166+
font-size: 13px;
167+
}
168+
169+
.pgStatBannerText {
170+
display: inline-flex;
171+
align-items: center;
172+
gap: 8px;
173+
color: #065f46;
174+
font-weight: 500;
175+
}
176+
177+
.pgStatBannerLink {
178+
padding: 0;
179+
border: none;
180+
background: none;
181+
color: #059669;
182+
font: inherit;
183+
font-size: 12px;
184+
font-weight: 600;
185+
cursor: pointer;
186+
text-decoration: underline;
187+
text-underline-offset: 2px;
188+
}
189+
190+
.pgStatBannerLink:hover {
191+
color: #047857;
192+
}

0 commit comments

Comments
 (0)