| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- import { useParams } from 'common'
- import dayjs from 'dayjs'
- import Link from 'next/link'
- import { useMemo } from 'react'
- import {
- MetricCard,
- MetricCardContent,
- MetricCardHeader,
- MetricCardLabel,
- MetricCardValue,
- } from 'ui-patterns/MetricCard'
- import {
- parseConnectionsData,
- parseInfrastructureMetrics,
- } from './DatabaseInfrastructureSection.utils'
- import { useInfraMonitoringAttributesQuery } from '@/data/analytics/infra-monitoring-query'
- import { useMaxConnectionsQuery } from '@/data/database/max-connections-query'
- import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
- type DatabaseInfrastructureSectionProps = {
- interval: '1hr' | '1day' | '7day'
- refreshKey: number
- dbErrorRate: number
- isLoading: boolean
- slowQueriesCount?: number
- slowQueriesLoading?: boolean
- }
- export const DatabaseInfrastructureSection = ({
- interval,
- refreshKey,
- dbErrorRate: _dbErrorRate,
- isLoading: _dbLoading,
- slowQueriesCount = 0,
- slowQueriesLoading = false,
- }: DatabaseInfrastructureSectionProps) => {
- const { ref: projectRef } = useParams()
- const { data: project } = useSelectedProjectQuery()
- // refreshKey forces date recalculation when user clicks refresh button
- // eslint-disable-next-line react-hooks/exhaustive-deps
- const { startDate, endDate, infraInterval } = useMemo(() => {
- const now = dayjs()
- const end = now.toISOString()
- let start: string
- let infraInterval: '1h' | '1d'
- switch (interval) {
- case '1hr':
- start = now.subtract(1, 'hour').toISOString()
- infraInterval = '1h'
- break
- case '1day':
- start = now.subtract(1, 'day').toISOString()
- infraInterval = '1h'
- break
- case '7day':
- start = now.subtract(7, 'day').toISOString()
- infraInterval = '1d'
- break
- default:
- start = now.subtract(1, 'hour').toISOString()
- infraInterval = '1h'
- }
- return { startDate: start, endDate: end, infraInterval }
- }, [interval, refreshKey])
- const {
- data: infraData,
- isLoading: infraLoading,
- error: infraError,
- } = useInfraMonitoringAttributesQuery({
- projectRef,
- attributes: [
- 'avg_cpu_usage',
- 'ram_usage',
- 'disk_fs_used_system',
- 'disk_fs_used_wal',
- 'pg_database_size',
- 'disk_fs_size',
- 'disk_io_consumption',
- 'pg_stat_database_num_backends',
- ],
- startDate,
- endDate,
- interval: infraInterval,
- })
- const { data: maxConnectionsData } = useMaxConnectionsQuery({
- projectRef,
- connectionString: project?.connectionString,
- })
- const metrics = useMemo(() => parseInfrastructureMetrics(infraData), [infraData])
- const connections = useMemo(
- () => parseConnectionsData(infraData, maxConnectionsData),
- [infraData, maxConnectionsData]
- )
- const errorMessage =
- infraError && typeof infraError === 'object' && 'message' in infraError
- ? String(infraError.message)
- : 'Error loading data'
- // Generate database report URL with time range parameters
- const getDatabaseReportUrl = () => {
- const now = dayjs()
- let its: string
- let helperText: string
- switch (interval) {
- case '1hr':
- its = now.subtract(1, 'hour').toISOString()
- helperText = 'Last 60 minutes'
- break
- case '1day':
- its = now.subtract(24, 'hour').toISOString()
- helperText = 'Last 24 hours'
- break
- case '7day':
- its = now.subtract(7, 'day').toISOString()
- helperText = 'Last 7 days'
- break
- default:
- its = now.subtract(24, 'hour').toISOString()
- helperText = 'Last 24 hours'
- }
- const ite = now.toISOString()
- const params = new URLSearchParams({
- its,
- ite,
- isHelper: 'true',
- helperText,
- })
- return `/project/${projectRef}/observability/database?${params.toString()}`
- }
- const databaseReportUrl = getDatabaseReportUrl()
- return (
- <div>
- <h2 className="mb-4">Database</h2>
- {/* First row: Metrics */}
- <div className="grid grid-cols-3 gap-2">
- <Link
- href={`/project/${projectRef}/observability/query-performance?totalTimeFilter=${encodeURIComponent(JSON.stringify({ operator: '>', value: 1000 }))}`}
- className="block group"
- >
- <MetricCard isLoading={slowQueriesLoading}>
- <MetricCardHeader
- href={`/project/${projectRef}/observability/query-performance?totalTimeFilter=${encodeURIComponent(JSON.stringify({ operator: '>', value: 1000 }))}`}
- linkTooltip="Go to query performance"
- >
- <MetricCardLabel tooltip="Queries with total execution time (execution time + planning time) greater than 1000ms. High values may indicate query optimization opportunities">
- Slow Queries
- </MetricCardLabel>
- </MetricCardHeader>
- <MetricCardContent>
- <MetricCardValue>{slowQueriesCount}</MetricCardValue>
- </MetricCardContent>
- </MetricCard>
- </Link>
- <Link href={databaseReportUrl} className="block group">
- <MetricCard isLoading={infraLoading}>
- <MetricCardHeader href={databaseReportUrl} linkTooltip="Go to database report">
- <MetricCardLabel tooltip="Active database connections (current/max). Monitor to avoid connection exhaustion">
- Connections
- </MetricCardLabel>
- </MetricCardHeader>
- <MetricCardContent>
- {infraError ? (
- <div className="text-xs text-destructive wrap-break-word">{errorMessage}</div>
- ) : connections.max > 0 ? (
- <MetricCardValue>
- {connections.current}/{connections.max}
- </MetricCardValue>
- ) : (
- <MetricCardValue>--</MetricCardValue>
- )}
- </MetricCardContent>
- </MetricCard>
- </Link>
- <Link href={databaseReportUrl} className="block group">
- <MetricCard isLoading={infraLoading}>
- <MetricCardHeader href={databaseReportUrl} linkTooltip="Go to database report">
- <MetricCardLabel tooltip="Disk usage percentage of total disk space used">
- Disk Usage
- </MetricCardLabel>
- </MetricCardHeader>
- <MetricCardContent>
- {infraError ? (
- <div className="text-xs text-destructive wrap-break-word">{errorMessage}</div>
- ) : metrics ? (
- <MetricCardValue>{metrics.disk.current.toFixed(0)}%</MetricCardValue>
- ) : (
- <MetricCardValue>--</MetricCardValue>
- )}
- </MetricCardContent>
- </MetricCard>
- </Link>
- <Link href={databaseReportUrl} className="block group">
- <MetricCard isLoading={infraLoading}>
- <MetricCardHeader href={databaseReportUrl} linkTooltip="Go to database report">
- <MetricCardLabel tooltip="Disk I/O consumption percentage. High values may indicate disk bottlenecks">
- Disk IO
- </MetricCardLabel>
- </MetricCardHeader>
- <MetricCardContent>
- {infraError ? (
- <div className="text-xs text-destructive wrap-break-word">{errorMessage}</div>
- ) : metrics ? (
- <MetricCardValue>{metrics.diskIo.current.toFixed(0)}%</MetricCardValue>
- ) : (
- <MetricCardValue>--</MetricCardValue>
- )}
- </MetricCardContent>
- </MetricCard>
- </Link>
- <Link href={databaseReportUrl} className="block group">
- <MetricCard isLoading={infraLoading}>
- <MetricCardHeader href={databaseReportUrl} linkTooltip="Go to database report">
- <MetricCardLabel tooltip="RAM usage percentage. Sustained high usage may indicate memory pressure">
- Memory
- </MetricCardLabel>
- </MetricCardHeader>
- <MetricCardContent>
- {infraError ? (
- <div className="text-xs text-destructive wrap-break-word">{errorMessage}</div>
- ) : metrics ? (
- <MetricCardValue>{metrics.ram.current.toFixed(0)}%</MetricCardValue>
- ) : (
- <MetricCardValue>--</MetricCardValue>
- )}
- </MetricCardContent>
- </MetricCard>
- </Link>
- <Link href={databaseReportUrl} className="block group">
- <MetricCard isLoading={infraLoading}>
- <MetricCardHeader href={databaseReportUrl} linkTooltip="Go to database report">
- <MetricCardLabel tooltip="CPU usage percentage. High values may suggest CPU-intensive queries or workloads">
- CPU
- </MetricCardLabel>
- </MetricCardHeader>
- <MetricCardContent>
- {infraError ? (
- <div className="text-xs text-destructive wrap-break-word">{errorMessage}</div>
- ) : metrics ? (
- <MetricCardValue>{metrics.cpu.current.toFixed(0)}%</MetricCardValue>
- ) : (
- <MetricCardValue>--</MetricCardValue>
- )}
- </MetricCardContent>
- </MetricCard>
- </Link>
- </div>
- </div>
- )
- }
|