Update disk size reduction validation (#30070)

* Update disk size reduction validation

* Fix

* Add docs

* Small fix
This commit is contained in:
Joshen Lim authored and GitHub committed 2024-10-25 09:36:27 +08:00
1 parent 9a3d0992c3
commit fa4ce4a949
4 files changed
+110 -73

No files matched your search

@@ -132,3 +132,5 @@ set default_transaction_read_only = 'off';
### Reducing disk size
Disks don't automatically downsize during normal operation. Once you have [reduced your database size](/docs/guides/platform/database-size#database-size), they _will_ automatically "right-size" during a [project upgrade](/docs/guides/platform/upgrading). The final disk size after the upgrade is 1.2x the size of the database with a minimum of 8 GB. For example, if your database size is 100GB, and you have a 200GB disk, the size after a project upgrade will be 120 GB.
In the event that your project is already on the latest version of Postgres and cannot be upgraded, a new version of Postgres will be released approximately every week which you can then upgrade to once it becomes available.
@@ -1,7 +1,7 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { AnimatePresence, motion } from 'framer-motion'
import { HelpCircle, InfoIcon, RotateCcw } from 'lucide-react'
import { ExternalLink, HelpCircle, InfoIcon, RotateCcw } from 'lucide-react'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
@@ -60,7 +60,7 @@ import {
calculateIOPSPrice,
calculateThroughputPrice,
} from './DiskManagement.utils'
import { DiskStorageSchema, DiskStorageSchemaType } from './DiskManagementPanelSchema'
import { getDiskStorageSchema, DiskStorageSchemaType } from './DiskManagementPanelSchema'
import { DiskManagementPlanUpgradeRequired } from './DiskManagementPlanUpgradeRequired'
import {
DiskManagementDiskSizeReadReplicas,
@@ -68,6 +68,8 @@ import {
DiskManagementThroughputReadReplicas,
} from './DiskManagementReadReplicas'
import { DiskManagementReviewAndSubmitDialog } from './DiskManagementReviewAndSubmitDialog'
import { Admonition } from 'ui-patterns'
import { Markdown } from '../Markdown'
export function DiskManagementPanelForm() {
const { project } = useProjectContext()
@@ -161,6 +163,7 @@ export function DiskManagementPanelForm() {
throughput: throughput_mbps,
totalSize: size_gb,
}
const DiskStorageSchema = getDiskStorageSchema(size_gb)
const form = useForm<DiskStorageSchemaType>({
resolver: zodResolver(DiskStorageSchema),
defaultValues,
@@ -616,8 +619,31 @@ export function DiskManagementPanelForm() {
label="Disk Size"
layout="horizontal"
description={
includedDiskGB > 0 &&
`Your plan includes ${includedDiskGB} GB of disk size for ${watchedStorageType}.`
<>
{includedDiskGB > 0 &&
`Your plan includes ${includedDiskGB} GB of disk size for ${watchedStorageType}.`}
{field.value < size_gb && (
<Admonition
className="mt-2"
type="default"
title="Reducing your project's disk size?"
description={
<Markdown
className="[&>p]:!leading-normal"
content={`Your disk size will automatically "right-size" when you [upgrade your project](/project/${projectRef}/settings/infrastructure).`}
/>
}
>
<div className="flex items-center gap-x-2">
<Button asChild type="default" icon={<ExternalLink />}>
<Link href="https://supabase.com/docs/guides/platform/database-size#reducing-disk-size">
Documentation
</Link>
</Button>
</div>
</Admonition>
)}
</>
}
>
<div className="mt-1 relative flex gap-2 items-center">
@@ -11,89 +11,98 @@ const baseSchema = z.object({
throughput: z.number().optional().describe('Throughput in MB/s for gp3'),
})
export const DiskStorageSchema = baseSchema.superRefine((data, ctx) => {
const { storageType, totalSize, provisionedIOPS, throughput } = data
export const getDiskStorageSchema = (currentDiskSize: number) =>
baseSchema.superRefine((data, ctx) => {
const { storageType, totalSize, provisionedIOPS, throughput } = data
if (storageType === 'io2') {
// Validation rules for io2
const maxIOPS = Math.min(1000 * totalSize, 256000)
if (provisionedIOPS < 100) {
if (totalSize < currentDiskSize) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Provisioned IOPS must be at least 100',
path: ['provisionedIOPS'],
})
} else if (provisionedIOPS > maxIOPS) {
if (totalSize >= 8) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Provisioned IOPS must be at most ${maxIOPS}.`,
path: ['provisionedIOPS'],
})
} else {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Invalid IOPS value due to invalid disk size`,
path: ['provisionedIOPS'],
})
}
}
if (throughput !== undefined) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Throughput is not configurable for io2.',
path: ['throughput'],
})
}
if (totalSize > DISK_LIMITS[DiskType.IO2].maxStorage) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Allocated disksize must not exceed 61,440 GB',
message: 'Disk size cannot be reduced from current configuration',
path: ['totalSize'],
})
}
} else if (storageType === 'gp3') {
// Validation rules for gp3
const maxIOPS = Math.min(500 * totalSize, 16000)
const maxThroughput = Math.min(0.25 * provisionedIOPS, 1000)
if (provisionedIOPS < 3000) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Provisioned IOPS must be at least 3000`,
path: ['provisionedIOPS'],
})
} else if (provisionedIOPS > maxIOPS) {
if (totalSize >= 8) {
if (storageType === 'io2') {
// Validation rules for io2
const maxIOPS = Math.min(1000 * totalSize, 256000)
if (provisionedIOPS < 100) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Provisioned IOPS must be at most ${maxIOPS}`,
message: 'Provisioned IOPS must be at least 100',
path: ['provisionedIOPS'],
})
} else {
} else if (provisionedIOPS > maxIOPS) {
if (totalSize >= 8) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Provisioned IOPS must be at most ${maxIOPS}.`,
path: ['provisionedIOPS'],
})
} else {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Invalid IOPS value due to invalid disk size`,
path: ['provisionedIOPS'],
})
}
}
if (throughput !== undefined) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Invalid IOPS value due to invalid disk size`,
message: 'Throughput is not configurable for io2.',
path: ['throughput'],
})
}
if (totalSize > DISK_LIMITS[DiskType.IO2].maxStorage) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Allocated disk size must not exceed 61,440 GB',
path: ['totalSize'],
})
}
} else if (storageType === 'gp3') {
// Validation rules for gp3
const maxIOPS = Math.min(500 * totalSize, 16000)
const maxThroughput = Math.min(0.25 * provisionedIOPS, 1000)
if (provisionedIOPS < 3000) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Provisioned IOPS must be at least 3000`,
path: ['provisionedIOPS'],
})
} else if (provisionedIOPS > maxIOPS) {
if (totalSize >= 8) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Provisioned IOPS must be at most ${maxIOPS}`,
path: ['provisionedIOPS'],
})
} else {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Invalid IOPS value due to invalid disk size`,
path: ['provisionedIOPS'],
})
}
}
if (throughput !== undefined && (throughput < 125 || throughput > maxThroughput)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Throughput must be set between 125 and ${maxThroughput?.toLocaleString()} MB/s.`,
path: ['throughput'],
})
}
if (totalSize > DISK_LIMITS[DiskType.GP3].maxStorage) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Allocated disk size must not exceed 16,384 GB',
path: ['totalSize'],
})
}
}
})
if (throughput !== undefined && (throughput < 125 || throughput > maxThroughput)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Throughput must be set between 125 and ${maxThroughput?.toLocaleString()} MB/s.`,
path: ['throughput'],
})
}
if (totalSize > DISK_LIMITS[DiskType.GP3].maxStorage) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Allocated disksize must not exceed 16,384 GB',
path: ['totalSize'],
})
}
}
})
export type DiskStorageSchemaType = z.infer<typeof DiskStorageSchema>
export type DiskStorageSchemaType = z.infer<ReturnType<typeof getDiskStorageSchema>>
@@ -207,7 +207,7 @@ const InstanceConfigurationUI = () => {
}, [isSuccessReplicas, isSuccessLoadBalancers, nodes, edges, view])
return (
<div className="nowheel">
<div className="nowheel border-y">
<div
className={`h-[500px] w-full relative ${
isSuccessReplicas && !isLoadingProject ? '' : 'flex items-center justify-center px-28'