Disable disk management for HA projects (#49633)

## Context

As per PR title, disables disk management for HA projects, which
involves
- Disabling "Increase disk size" CTAs in reports/database and the old
disk config settings in database/settings
- Disabling all input fields related to disk management in
settings/infrastructure
<img width="1076" height="857" alt="image"
src="https://github.com/user-attachments/assets/bfbdfc36-8ae3-41ce-af12-c05b46e620f4"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added High Availability notices and restrictions throughout disk
management settings.
* Disabled disk size, IOPS, throughput, and autoscaling controls where
High Availability limits changes.
  * Added explanatory tooltips for restricted disk-size actions.
* Updated database observability controls to reflect High Availability
restrictions.

* **Accessibility**
* Added an accessible label to the database observability refresh
button.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Joshen Lim authored and GitHub committed 2026-08-27 22:17:34 +08:00
1 parent df002b4018
commit 102d3d1df5
11 files changed
+85 -62

No files matched your search

@@ -150,6 +150,9 @@ export function DiskSection({
<DocsButton href={`${DOCS_URL}/guides/platform/database-size`} />
</PageSectionAside>
</PageSectionMeta>
<HighAvailabilityDisabledSectionNotice title="Disk management is unavailable for High Availability projects" />
<PageSectionContent ref={settingsRef} className="flex flex-col gap-4 scroll-mt-24">
{isAws && <DiskSpaceBar form={form} />}
@@ -255,7 +258,7 @@ export function AdvancedSection({
<PageSectionContent className="flex flex-col gap-4">
<Card ref={autoscaleSettingsRef} className="scroll-mt-24">
<CardContent className="flex flex-col gap-y-8">
<AutoScaleFields form={form} />
<AutoScaleFields form={form} disableInput={disableDiskInputs && disableDiskSizeInput} />
</CardContent>
</Card>
@@ -55,12 +55,12 @@ import { AddonVariantId } from '@/data/subscriptions/types'
import { useResourceWarningsQuery } from '@/data/usage/resource-warnings-query'
import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { useHighAvailability } from '@/hooks/misc/useHighAvailability'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
import {
useIsAwsCloudProvider,
useIsAwsK8sCloudProvider,
useIsAwsNimbusCloudProvider,
useIsHighAvailability,
useSelectedProjectQuery,
} from '@/hooks/misc/useSelectedProject'
import { GB, PROJECT_STATUS } from '@/lib/constants'
@@ -102,7 +102,7 @@ export function DiskManagementForm({
const isAws = useIsAwsCloudProvider()
const isAwsK8s = useIsAwsK8sCloudProvider()
const isAwsNimbus = useIsAwsNimbusCloudProvider()
const { isHighAvailability } = useHighAvailability()
const isHighAvailability = useIsHighAvailability()
const { can: canUpdateDiskConfiguration, isSuccess: isPermissionsLoaded } =
useAsyncCheckPermissions(PermissionAction.UPDATE, 'projects', {
@@ -209,9 +209,10 @@ export function DiskManagementForm({
const usedPercentage = (usedSize / totalSize) * 100
const disableIopsThroughputConfig =
modifiedComputeSize &&
!isSpendCapEnabled &&
RESTRICTED_COMPUTE_FOR_THROUGHPUT_ON_GP3.includes(modifiedComputeSize)
isHighAvailability ||
(modifiedComputeSize &&
!isSpendCapEnabled &&
RESTRICTED_COMPUTE_FOR_THROUGHPUT_ON_GP3.includes(modifiedComputeSize))
const watchedTotalSize = useWatch({ control: form.control, name: 'totalSize' }) ?? 0
const watchedStorageType = useWatch({ control: form.control, name: 'storageType' })
@@ -231,10 +232,11 @@ export function DiskManagementForm({
isRequestingChanges ||
isPlanUpgradeRequired ||
isWithinCooldownWindow ||
isHighAvailability ||
!canUpdateDiskConfiguration ||
!isAws
const disableDiskInputs = disableDiskSizeInput || isSpendCapEnabled
const disableDiskInputs = disableDiskSizeInput || isSpendCapEnabled || isHighAvailability
// Compute resizing is not supported for High Availability projects during Alpha
const disableComputeInputs = isPlanUpgradeRequired || isHighAvailability
@@ -17,9 +17,10 @@ import { useDiskAutoscaleCustomConfigQuery } from '@/data/config/disk-autoscale-
type AutoScaleFieldProps = {
form: UseFormReturn<DiskStorageSchemaType>
disableInput?: boolean
}
export const AutoScaleFields = ({ form }: AutoScaleFieldProps) => {
export const AutoScaleFields = ({ form, disableInput = false }: AutoScaleFieldProps) => {
const { ref: projectRef } = useParams()
const {
control,
@@ -76,7 +77,7 @@ export const AutoScaleFields = ({ form }: AutoScaleFieldProps) => {
id={field.name}
type="number"
value={field.value ?? undefined}
disabled={isError}
disabled={disableInput || isError}
onChange={(e) => {
setValue(
'growthPercent',
@@ -123,7 +124,7 @@ export const AutoScaleFields = ({ form }: AutoScaleFieldProps) => {
id={field.name}
type="number"
value={field.value ?? undefined}
disabled={isError}
disabled={disableInput || isError}
onChange={(e) => {
setValue(
'minIncrementGb',
@@ -165,7 +166,7 @@ export const AutoScaleFields = ({ form }: AutoScaleFieldProps) => {
id={field.name}
type="number"
value={field.value ?? undefined}
disabled={isError}
disabled={disableInput || isError}
onChange={(e) => {
setValue('maxSizeGb', e.target.value === '' ? null : e.target.valueAsNumber, {
shouldDirty: true,
@@ -40,6 +40,7 @@ vi.mock('@/hooks/misc/useSelectedProject', () => ({
data: { ref: 'default', connectionString: 'postgres://localhost' },
}),
useIsOrioleDb: () => false,
useIsHighAvailability: () => false,
}))
vi.mock('common', async (importOriginal) => {
@@ -15,6 +15,7 @@ vi.mock('@/hooks/misc/useSelectedProject', () => ({
useSelectedProjectQuery: () => ({
data: { ref: 'default', connectionString: 'postgres://localhost' },
}),
useIsHighAvailability: () => false,
}))
vi.mock('@/hooks/useProtectedSchemas', () => ({
@@ -45,6 +45,7 @@ vi.mock('@/hooks/misc/useHighAvailability', () => ({
vi.mock('@/hooks/misc/useSelectedProject', () => ({
useSelectedProjectQuery: mockUseSelectedProjectQuery,
useIsHighAvailability: () => mockUseHighAvailability().isHighAvailability ?? false,
}))
vi.mock('@/data/database/max-connections-query', () => ({
@@ -2,7 +2,7 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import { ExternalLink, Info } from 'lucide-react'
import Link from 'next/link'
import { SetStateAction } from 'react'
import { parseAsBoolean, useQueryState } from 'nuqs'
import { toast } from 'sonner'
import { Alert, AlertDescription, AlertTitle, Button, InfoIcon } from 'ui'
import {
@@ -26,9 +26,9 @@ import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
import {
useIsAwsNimbusCloudProvider,
useIsHighAvailability,
useSelectedProjectQuery,
} from '@/hooks/misc/useSelectedProject'
import { useUrlState } from '@/hooks/ui/useUrlState'
import { DOCS_URL } from '@/lib/constants'
import { formatBytes } from '@/lib/helpers'
@@ -42,14 +42,13 @@ export const DiskSizeConfiguration = ({ disabled = false }: DiskSizeConfiguratio
const { data: organization } = useSelectedOrganizationQuery()
const isAwsNimbus = useIsAwsNimbusCloudProvider()
const isHighAvailability = useIsHighAvailability()
const { reportsAll } = useIsFeatureEnabled(['reports:all'])
const [{ show_increase_disk_size_modal }, setUrlParams] = useUrlState()
const showIncreaseDiskSizeModal = show_increase_disk_size_modal === 'true'
const setShowIncreaseDiskSizeModal = (value: SetStateAction<boolean>) => {
const show = typeof value === 'function' ? value(showIncreaseDiskSizeModal) : value
setUrlParams({ show_increase_disk_size_modal: show ? 'true' : undefined })
}
const [showIncreaseDiskSizeModal, setShowIncreaseDiskSizeModal] = useQueryState(
'show_increase_disk_size_modal',
parseAsBoolean.withDefault(false)
)
const { can: canUpdateDiskSizeConfig } = useAsyncCheckPermissions(
PermissionAction.UPDATE,
@@ -110,14 +109,16 @@ export const DiskSizeConfiguration = ({ disabled = false }: DiskSizeConfiguratio
<ButtonTooltip
variant="default"
className="w-min ml-auto"
disabled={!canUpdateDiskSizeConfig || disabled}
disabled={!canUpdateDiskSizeConfig || isHighAvailability || disabled}
onClick={() => setShowIncreaseDiskSizeModal(true)}
tooltip={{
content: {
side: 'bottom',
text: !canUpdateDiskSizeConfig
? 'You need additional permissions to increase the disk size'
: undefined,
: isHighAvailability
? 'Disk size management is unavailable for High Availability projects'
: undefined,
},
}}
>
@@ -219,11 +220,13 @@ Read more about [disk management](${DOCS_URL}/guides/platform/database-size#disk
</PageSectionContent>
</PageSection>
<DiskSizeConfigurationModal
visible={showIncreaseDiskSizeModal}
loading={isUpdatingDiskSize}
hideModal={setShowIncreaseDiskSizeModal}
/>
{!isHighAvailability && (
<DiskSizeConfigurationModal
visible={showIncreaseDiskSizeModal}
loading={isUpdatingDiskSize}
hideModal={setShowIncreaseDiskSizeModal}
/>
)}
</>
)
}
@@ -1,7 +1,7 @@
import type { ReactNode } from 'react'
import { Admonition } from 'ui-patterns/Admonition'
import { useHighAvailability } from '@/hooks/misc/useHighAvailability'
import { useIsHighAvailability } from '@/hooks/misc/useSelectedProject'
const DEFAULT_TITLE = 'This feature is unavailable on High Availability projects'
const DEFAULT_DESCRIPTION =
@@ -16,7 +16,7 @@ export function HighAvailabilityDisabledSectionNotice({
title = DEFAULT_TITLE,
description = DEFAULT_DESCRIPTION,
}: HighAvailabilityDisabledSectionNoticeProps) {
const { isHighAvailability } = useHighAvailability()
const isHighAvailability = useIsHighAvailability()
if (!isHighAvailability) return null
@@ -1,14 +1,13 @@
import { useMemo } from 'react'
import { MULTIGRES_SCHEMA_NAME, resolveHighAvailability } from './useHighAvailability.constants'
import { useSelectedProjectQuery } from './useSelectedProject'
import { useIsHighAvailability, useSelectedProjectQuery } from './useSelectedProject'
export { MULTIGRES_SCHEMA_NAME, resolveHighAvailability }
export function useHighAvailability() {
const { data: project, isPending } = useSelectedProjectQuery()
const isHighAvailability = resolveHighAvailability(project)
const isHighAvailability = useIsHighAvailability()
const { isPending } = useSelectedProjectQuery()
return {
isHighAvailability,
@@ -54,6 +54,7 @@ export const useIsOrioleDbInAws = () => {
return isOrioleDbInAws
}
// [Joshen TODO] There's a duplicate method `resolveHighAvailability` in `useHighAvailability.constants`
export const useIsHighAvailability = () => {
const { data: project } = useSelectedProjectQuery()
return project?.high_availability ?? false
@@ -43,7 +43,7 @@ import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { useRefreshHandler, useReportDateRange } from '@/hooks/misc/useReportDateRange'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
import { useIsHighAvailability, useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
import { DOCS_URL } from '@/lib/constants'
import { formatBytes } from '@/lib/helpers'
import { useDatabaseSelectorStateSnapshot } from '@/state/database-selector'
@@ -74,6 +74,7 @@ const DatabaseUsage = () => {
const { db, chart, ref } = useParams()
const { data: project } = useSelectedProjectQuery()
const { data: org } = useSelectedOrganizationQuery()
const isHighAvailability = useIsHighAvailability()
const {
selectedDateRange,
@@ -240,6 +241,7 @@ const DatabaseUsage = () => {
side="bottom"
>
<Button
aria-label="Refresh report"
variant="default"
disabled={isRefreshing}
icon={<RefreshCw className={isRefreshing ? 'animate-spin' : ''} />}
@@ -369,21 +371,27 @@ const DatabaseUsage = () => {
</div>
<div className="ml-auto">
{project?.cloud_provider === 'AWS' ? (
{/*
[Joshen] TODO: Check if this check is still relevant
The DiskSizeConfigurationModal is old and might be obsolete
*/}
{project?.cloud_provider === 'AWS' && !isHighAvailability ? (
<Button asChild variant="default">
<Link href={getInfrastructurePath(ref)}>Increase disk size</Link>
</Button>
) : (
<ButtonTooltip
variant="default"
disabled={!canUpdateDiskSizeConfig}
disabled={!canUpdateDiskSizeConfig || isHighAvailability}
onClick={() => setshowIncreaseDiskSizeModal(true)}
tooltip={{
content: {
side: 'bottom',
text: !canUpdateDiskSizeConfig
? 'You need additional permissions to increase the disk size'
: undefined,
: isHighAvailability
? 'Disk size management is unavailable for High Availability projects'
: undefined,
},
}}
>
@@ -432,32 +440,7 @@ const DatabaseUsage = () => {
</div>
)
}}
append={() => (
<div className="px-6 pb-6">
<Alert variant="default" className="mt-4">
<AlertDescription>
<div className="space-y-2">
<p>
New Supabase projects have a database size of ~40-60mb. This space includes
pre-installed extensions, schemas, and default Postgres data. Additional
database size is used when installing extensions, even if those extensions are
inactive.
</p>
<Button asChild variant="default" icon={<ExternalLink />}>
<Link
href={`${DOCS_URL}/guides/platform/database-size#disk-space-usage`}
target="_blank"
rel="noreferrer"
>
Read about database size
</Link>
</Button>
</div>
</AlertDescription>
</Alert>
</div>
)}
append={renderDatabaseSizeAdditionalInfo}
/>
<DiskSizeConfigurationModal
visible={showIncreaseDiskSizeModal}
@@ -471,3 +454,31 @@ const DatabaseUsage = () => {
</>
)
}
const renderDatabaseSizeAdditionalInfo = () => {
return (
<div className="px-6 pb-6">
<Alert variant="default" className="mt-4">
<AlertDescription>
<div className="space-y-2">
<p>
New Supabase projects have a database size of ~40-60mb. This space includes
pre-installed extensions, schemas, and default Postgres data. Additional database size
is used when installing extensions, even if those extensions are inactive.
</p>
<Button asChild variant="default" icon={<ExternalLink />}>
<Link
href={`${DOCS_URL}/guides/platform/database-size#disk-space-usage`}
target="_blank"
rel="noreferrer"
>
Read about database size
</Link>
</Button>
</div>
</AlertDescription>
</Alert>
</div>
)
}