ReadReplicaDetails.tsx179 lines · main
1import { useParams } from 'common'
2import { BarChart2 } from 'lucide-react'
3import { useMemo } from 'react'
4import { AWS_REGIONS } from 'shared-data'
5import { Card, CardContent, CardHeader, CardTitle } from 'ui'
6import {
7 Chart,
8 ChartCard,
9 ChartContent,
10 ChartEmptyState,
11 ChartHeader,
12 ChartLine,
13 ChartLoadingState,
14 ChartMetric,
15 GenericSkeletonLoader,
16} from 'ui-patterns'
17import { Input } from 'ui-patterns/DataInputs/Input'
18import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
19
20import { REPORT_DATERANGE_HELPER_LABELS } from '@/components/interfaces/Reports/Reports.constants'
21import { REPLICA_STATUS } from '@/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.constants'
22import { ScaffoldContainer, ScaffoldSection } from '@/components/layouts/Scaffold'
23import { useInfraMonitoringAttributesQuery } from '@/data/analytics/infra-monitoring-query'
24import { useLoadBalancersQuery } from '@/data/read-replicas/load-balancers-query'
25import { useReplicationLagQuery } from '@/data/read-replicas/replica-lag-query'
26import { useReadReplicasQuery } from '@/data/read-replicas/replicas-query'
27import { useReadReplicasStatusesQuery } from '@/data/read-replicas/replicas-status-query'
28import { useReportDateRange } from '@/hooks/misc/useReportDateRange'
29import { BASE_PATH } from '@/lib/constants'
30
31const attribute = 'physical_replication_lag_physical_replication_lag_seconds'
32
33export const ReadReplicaDetails = () => {
34 const { ref: projectRef, replicaId } = useParams()
35
36 const { data = [], isPending: isLoadingDatabases } = useReadReplicasQuery({ projectRef })
37 const replica = data.find((x) => x.identifier === replicaId)
38 const { identifier, connectionString, status: baseStatus, restUrl, region, size } = replica ?? {}
39 const regionLabel = Object.values(AWS_REGIONS).find((x) => x.code === region)?.displayName
40
41 const { data: statuses = [] } = useReadReplicasStatusesQuery({ projectRef })
42 const replicaStatus = statuses.find((x) => x.identifier === identifier)
43 const status = replicaStatus?.status ?? baseStatus
44
45 const { data: loadBalancers = [] } = useLoadBalancersQuery({ projectRef })
46 const loadBalancer = loadBalancers.find((x) =>
47 x.databases.some((x) => x.identifier === identifier)
48 )
49
50 const { data: lagDuration, isPending: isLoadingLag } = useReplicationLagQuery(
51 {
52 id: identifier ?? '',
53 projectRef,
54 connectionString,
55 },
56 { enabled: status === REPLICA_STATUS.ACTIVE_HEALTHY }
57 )
58
59 const { selectedDateRange } = useReportDateRange(REPORT_DATERANGE_HELPER_LABELS.LAST_60_MINUTES)
60 // [Joshen] This is unused but intentional to scaffold the usage for now, refer to comment below
61 const { data: infraMonitoringData, isPending: isFetchingInfraMonitoring } =
62 useInfraMonitoringAttributesQuery(
63 {
64 projectRef,
65 attributes: [attribute],
66 databaseIdentifier: identifier,
67 startDate: selectedDateRange.period_start.date,
68 endDate: selectedDateRange.period_end.date,
69 interval: selectedDateRange.interval,
70 },
71 { enabled: !!replica }
72 )
73
74 const chartData = useMemo(
75 () =>
76 infraMonitoringData?.data.map((x) => ({
77 timestamp: x.period_start,
78 [attribute]: (x as Record<string, string | number>)[attribute],
79 })) ?? [],
80 [infraMonitoringData?.data]
81 )
82
83 return (
84 <>
85 <Chart className="mt-6" isLoading={isLoadingLag || isFetchingInfraMonitoring}>
86 <ChartCard className="rounded-none border-x-0">
87 <ChartHeader className="px-10">
88 <ChartMetric
89 label="Replication lag"
90 value={lagDuration !== undefined ? `${lagDuration}s` : '-'}
91 />
92 </ChartHeader>
93 <ChartContent
94 isEmpty={chartData.length === 0}
95 emptyState={
96 <ChartEmptyState
97 icon={<BarChart2 size={16} />}
98 title="No data to show"
99 description="It may take up to 24 hours for data to refresh"
100 />
101 }
102 loadingState={<ChartLoadingState className="h-[228px]" />}
103 >
104 <div className="h-56 px-5">
105 <ChartLine
106 showGrid
107 showYAxis
108 isFullHeight
109 data={chartData}
110 dataKey={attribute}
111 YAxisProps={{
112 tickFormatter: (value) => `${value}s`,
113 width: 80,
114 }}
115 config={{
116 [attribute]: { label: 'Replication lag' },
117 }}
118 />
119 </div>
120 </ChartContent>
121 </ChartCard>
122 </Chart>
123 <ScaffoldContainer>
124 <ScaffoldSection isFullWidth>
125 <Card>
126 <CardHeader>
127 <CardTitle>Replica Information</CardTitle>
128 </CardHeader>
129 {isLoadingDatabases ? (
130 <CardContent>
131 <GenericSkeletonLoader />
132 </CardContent>
133 ) : (
134 <>
135 <CardContent>
136 <FormItemLayout
137 isReactForm={false}
138 layout="horizontal"
139 label="Load Balancer URL"
140 description="RESTful endpoint for querying and managing your databases through your load balancer"
141 >
142 <Input readOnly copy className="input-mono" value={loadBalancer?.endpoint} />
143 </FormItemLayout>
144 </CardContent>
145 <CardContent className="flex flex-col gap-y-4">
146 <FormItemLayout isReactForm={false} layout="horizontal" label="Replica URL">
147 <Input readOnly copy className="input-mono" value={restUrl} />
148 </FormItemLayout>
149 <FormItemLayout isReactForm={false} layout="horizontal" label="Region">
150 <Input
151 readOnly
152 className="input-mono"
153 value={regionLabel}
154 icon={
155 <img
156 alt="region icon"
157 className="w-5 rounded-xs"
158 src={`${BASE_PATH}/img/regions/${region ?? ''}.svg`}
159 />
160 }
161 />
162 </FormItemLayout>
163 <FormItemLayout
164 isReactForm={false}
165 layout="horizontal"
166 label="Compute Size"
167 description="Size of replica will be identical to the primary database"
168 >
169 <Input readOnly className="input-mono" value={size} />
170 </FormItemLayout>
171 </CardContent>
172 </>
173 )}
174 </Card>
175 </ScaffoldSection>
176 </ScaffoldContainer>
177 </>
178 )
179}