ReadReplicaEligibilityWarnings.tsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import { SupportCategories } from '@supabase/shared-types/out/constants'
  2. import { useParams } from 'common'
  3. import Link from 'next/link'
  4. import { useEffect, useState } from 'react'
  5. import { toast } from 'sonner'
  6. import { Button } from 'ui'
  7. import { Admonition } from 'ui-patterns'
  8. import { useCheckEligibilityDeployReplica } from './useCheckEligibilityDeployReplica'
  9. import { SupportLink } from '@/components/interfaces/Support/SupportLink'
  10. import { DocsButton } from '@/components/ui/DocsButton'
  11. import { UpgradePlanButton } from '@/components/ui/UpgradePlanButton'
  12. import { useEnablePhysicalBackupsMutation } from '@/data/database/enable-physical-backups-mutation'
  13. import { useProjectDetailQuery } from '@/data/projects/project-detail-query'
  14. import { READ_REPLICAS_MAX_COUNT } from '@/data/read-replicas/replicas-query'
  15. import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
  16. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  17. import { DOCS_URL } from '@/lib/constants'
  18. export const ReadReplicaEligibilityWarnings = () => {
  19. const { ref: projectRef } = useParams()
  20. const { data: org } = useSelectedOrganizationQuery()
  21. const { data: project } = useSelectedProjectQuery()
  22. const [refetchInterval, setRefetchInterval] = useState<number | false>(false)
  23. const {
  24. hasOverdueInvoices,
  25. isAWSProvider,
  26. isAwsK8s,
  27. isPgVersionBelow15,
  28. isBelowSmallCompute,
  29. isWalgNotEnabled,
  30. isProWithSpendCapEnabled,
  31. isReachedMaxReplicas,
  32. maxNumberOfReplicas,
  33. } = useCheckEligibilityDeployReplica()
  34. const { data: projectDetail, isSuccess: isProjectDetailSuccess } = useProjectDetailQuery(
  35. { ref: projectRef },
  36. {
  37. refetchInterval,
  38. refetchOnWindowFocus: false,
  39. }
  40. )
  41. const { mutate: enablePhysicalBackups, isPending: isEnabling } = useEnablePhysicalBackupsMutation(
  42. {
  43. onSuccess: () => {
  44. toast.success(
  45. 'Physical backups are currently being enabled, please check back in a few minutes!'
  46. )
  47. setRefetchInterval(5000)
  48. },
  49. }
  50. )
  51. useEffect(() => {
  52. if (!isProjectDetailSuccess) return
  53. if (projectDetail.is_physical_backups_enabled) {
  54. setRefetchInterval(false)
  55. }
  56. }, [projectDetail?.is_physical_backups_enabled, isProjectDetailSuccess])
  57. if (hasOverdueInvoices) {
  58. return (
  59. <Admonition type="warning" title="Your organization has overdue invoices">
  60. <p>Please resolve all outstanding invoices first before deploying a new read replica</p>
  61. <Button asChild type="default" className="mt-2">
  62. <Link href={`/org/${org?.slug}/billing#invoices`}>View invoices</Link>
  63. </Button>
  64. </Admonition>
  65. )
  66. }
  67. if (!isAWSProvider) {
  68. return (
  69. <Admonition
  70. type="warning"
  71. title="Read replicas are only supported for projects provisioned via AWS"
  72. >
  73. <p>
  74. Projects provisioned by other cloud providers currently will not be able to use read
  75. replicas
  76. </p>
  77. <DocsButton
  78. abbrev={false}
  79. className="mt-2"
  80. href={`${DOCS_URL}/guides/platform/read-replicas#prerequisites`}
  81. />
  82. </Admonition>
  83. )
  84. }
  85. if (isAwsK8s) {
  86. return (
  87. <Admonition
  88. type="warning"
  89. title="Read replicas are not supported for AWS (Revamped) projects"
  90. description="Projects provisioned by other cloud providers currently will not be able to use read replicas"
  91. />
  92. )
  93. }
  94. if (isPgVersionBelow15) {
  95. return (
  96. <Admonition
  97. type="warning"
  98. title="Read replicas can only be deployed with projects on Postgres version 15 and above"
  99. >
  100. <p>If you'd like to use read replicas, please contact us via support</p>
  101. <Button asChild type="default" className="mt-2">
  102. <SupportLink
  103. queryParams={{
  104. projectRef,
  105. category: SupportCategories.SALES_ENQUIRY,
  106. subject: 'Enquiry on read replicas',
  107. message: `Project DB version: ${project?.dbVersion}`,
  108. }}
  109. >
  110. Contact support
  111. </SupportLink>
  112. </Button>
  113. </Admonition>
  114. )
  115. }
  116. if (isBelowSmallCompute) {
  117. return (
  118. <Admonition type="warning" title="Project required to at least be on a Small compute">
  119. <p>
  120. This is to ensure that read replicas can keep up with the primary databases' activities.
  121. </p>
  122. <div className="flex items-center gap-x-2 mt-2">
  123. <UpgradePlanButton
  124. variant="default"
  125. plan="Pro"
  126. addon="computeSize"
  127. source="read-replicas"
  128. featureProposition="deploy Read Replicas"
  129. />
  130. <DocsButton href={`${DOCS_URL}/guides/platform/read-replicas#prerequisites`} />
  131. </div>
  132. </Admonition>
  133. )
  134. }
  135. if (isWalgNotEnabled) {
  136. return (
  137. <Admonition
  138. type="warning"
  139. title={
  140. refetchInterval === false
  141. ? 'Physical backups are required to deploy replicas'
  142. : 'Physical backups are currently being enabled'
  143. }
  144. >
  145. {refetchInterval === false ? (
  146. <>
  147. <p>
  148. Physical backups are used under the hood to spin up read replicas for your project.
  149. </p>
  150. <p>
  151. Enabling physical backups will take a few minutes, after which you will be able to
  152. deploy read replicas.
  153. </p>
  154. </>
  155. ) : (
  156. <>
  157. <p>
  158. This warning will go away once physical backups have been enabled - check back in a
  159. few minutes!
  160. </p>
  161. <p>You may start deploying read replicas thereafter once this is completed.</p>
  162. </>
  163. )}
  164. {refetchInterval === false && (
  165. <div className="flex items-center gap-x-2 mt-2">
  166. <Button
  167. type="default"
  168. loading={isEnabling}
  169. disabled={isEnabling}
  170. onClick={() => {
  171. if (projectRef) enablePhysicalBackups({ ref: projectRef })
  172. }}
  173. >
  174. Enable physical backups
  175. </Button>
  176. <DocsButton
  177. abbrev={false}
  178. href={`${DOCS_URL}/guides/platform/read-replicas#how-are-read-replicas-made`}
  179. />
  180. </div>
  181. )}
  182. </Admonition>
  183. )
  184. }
  185. if (isProWithSpendCapEnabled) {
  186. return (
  187. <Admonition type="warning" title="Spend cap needs to be disabled to deploy replicas">
  188. <p>
  189. Launching a replica incurs additional disk size that will exceed the plan's quota. Disable
  190. the spend cap first to allow overages before launching a replica.
  191. </p>
  192. <UpgradePlanButton
  193. variant="default"
  194. source="read-replicas"
  195. addon="spendCap"
  196. className="mt-2"
  197. >
  198. Disable spend cap
  199. </UpgradePlanButton>
  200. </Admonition>
  201. )
  202. }
  203. if (isReachedMaxReplicas) {
  204. return (
  205. <Admonition
  206. type="warning"
  207. title={`You can only deploy up to ${maxNumberOfReplicas} read replicas at once`}
  208. >
  209. <p>If you'd like to spin up another read replica, please drop an existing replica first.</p>
  210. {maxNumberOfReplicas < READ_REPLICAS_MAX_COUNT && (
  211. <>
  212. <p>
  213. Alternatively, you may deploy up to{' '}
  214. <span className="text-foreground">{READ_REPLICAS_MAX_COUNT}</span> replicas if your
  215. project is on an XL compute or higher.
  216. </p>
  217. <UpgradePlanButton
  218. variant="default"
  219. plan="Pro"
  220. addon="computeSize"
  221. source="read-replicas"
  222. featureProposition="deploy Read Replicas"
  223. className="mt-2"
  224. >
  225. Change compute size
  226. </UpgradePlanButton>
  227. </>
  228. )}
  229. </Admonition>
  230. )
  231. }
  232. }