ReadReplicaDetails.tsx 6.8 KB

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