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