From fa4ce4a9496c8b63fd8bfd52cafce5d03f6efda3 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 25 Oct 2024 09:36:27 +0800 Subject: [PATCH] Update disk size reduction validation (#30070) * Update disk size reduction validation * Fix * Add docs * Small fix --- .../content/guides/platform/database-size.mdx | 2 + .../DiskManagementPanelForm.tsx | 34 +++- .../DiskManagementPanelSchema.ts | 145 ++++++++++-------- .../InstanceConfiguration.tsx | 2 +- 4 files changed, 110 insertions(+), 73 deletions(-) diff --git a/apps/docs/content/guides/platform/database-size.mdx b/apps/docs/content/guides/platform/database-size.mdx index 3415a649820..3ddc6cb224c 100644 --- a/apps/docs/content/guides/platform/database-size.mdx +++ b/apps/docs/content/guides/platform/database-size.mdx @@ -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. diff --git a/apps/studio/components/interfaces/DiskManagement/DiskManagementPanelForm.tsx b/apps/studio/components/interfaces/DiskManagement/DiskManagementPanelForm.tsx index 5e58cd95843..e9e82e8e642 100644 --- a/apps/studio/components/interfaces/DiskManagement/DiskManagementPanelForm.tsx +++ b/apps/studio/components/interfaces/DiskManagement/DiskManagementPanelForm.tsx @@ -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({ 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 && ( + + } + > +
+ +
+
+ )} + } >
diff --git a/apps/studio/components/interfaces/DiskManagement/DiskManagementPanelSchema.ts b/apps/studio/components/interfaces/DiskManagement/DiskManagementPanelSchema.ts index ca1db6987fc..37bf8f73fb9 100644 --- a/apps/studio/components/interfaces/DiskManagement/DiskManagementPanelSchema.ts +++ b/apps/studio/components/interfaces/DiskManagement/DiskManagementPanelSchema.ts @@ -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 +export type DiskStorageSchemaType = z.infer> diff --git a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.tsx b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.tsx index 81db395954a..6399894149f 100644 --- a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.tsx +++ b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.tsx @@ -207,7 +207,7 @@ const InstanceConfigurationUI = () => { }, [isSuccessReplicas, isSuccessLoadBalancers, nodes, edges, view]) return ( -
+