ReadReplicaDetails.tsx179 lines · main
| 1 | import { useParams } from 'common' |
| 2 | import { BarChart2 } from 'lucide-react' |
| 3 | import { useMemo } from 'react' |
| 4 | import { AWS_REGIONS } from 'shared-data' |
| 5 | import { Card, CardContent, CardHeader, CardTitle } from 'ui' |
| 6 | import { |
| 7 | Chart, |
| 8 | ChartCard, |
| 9 | ChartContent, |
| 10 | ChartEmptyState, |
| 11 | ChartHeader, |
| 12 | ChartLine, |
| 13 | ChartLoadingState, |
| 14 | ChartMetric, |
| 15 | GenericSkeletonLoader, |
| 16 | } from 'ui-patterns' |
| 17 | import { Input } from 'ui-patterns/DataInputs/Input' |
| 18 | import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' |
| 19 | |
| 20 | import { REPORT_DATERANGE_HELPER_LABELS } from '@/components/interfaces/Reports/Reports.constants' |
| 21 | import { REPLICA_STATUS } from '@/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.constants' |
| 22 | import { ScaffoldContainer, ScaffoldSection } from '@/components/layouts/Scaffold' |
| 23 | import { useInfraMonitoringAttributesQuery } from '@/data/analytics/infra-monitoring-query' |
| 24 | import { useLoadBalancersQuery } from '@/data/read-replicas/load-balancers-query' |
| 25 | import { useReplicationLagQuery } from '@/data/read-replicas/replica-lag-query' |
| 26 | import { useReadReplicasQuery } from '@/data/read-replicas/replicas-query' |
| 27 | import { useReadReplicasStatusesQuery } from '@/data/read-replicas/replicas-status-query' |
| 28 | import { useReportDateRange } from '@/hooks/misc/useReportDateRange' |
| 29 | import { BASE_PATH } from '@/lib/constants' |
| 30 | |
| 31 | const attribute = 'physical_replication_lag_physical_replication_lag_seconds' |
| 32 | |
| 33 | export 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 | } |