diff --git a/apps/docs/pages/guides/platform/database-size.mdx b/apps/docs/pages/guides/platform/database-size.mdx index ec25d73509e..5d92884eb5f 100644 --- a/apps/docs/pages/guides/platform/database-size.mdx +++ b/apps/docs/pages/guides/platform/database-size.mdx @@ -9,7 +9,7 @@ export const meta = { Database size refers to the _monthly average storage usage_, as reported by Postgres. This metric is reported in your project's [billing usage](https://app.supabase.com/project/_/settings/billing/usage) and is updated daily. As you read this document we will refer to "database size" and "disk size": - "Database size" is the total size of used storage from your database. -- "Disk size" describes the size of the underlying available storage. +- "Disk Storage size" describes the size of the underlying available storage. ## Database space management @@ -23,7 +23,7 @@ select from pg_database; ``` -This value is reported in the [database settings page](https://app.supabase.com/project/_/settings/database). +This value is reported in the [database report page](https://app.supabase.com/project/_/reports/database). Database Space is consumed primarily by your data, indexes, and materialized views. You can reduce your disk size by removing any of these and running a Vacuum operation. @@ -61,16 +61,18 @@ Supabase uses network-attached storage to balance performance with scalability. Pro and Enterprise projects have auto-scaling Disk Storage. -Disk storage expands automatically when the database reaches 90% of the disk size. The disk is expanded to be 50% larger (e.g., 8GB -> 12GB). Auto-scaling can only take place once every 6 hours. If within those 6 hours you reach 95% of the disk space, your project will enter read-only mode. +Disk Storage expands automatically when the database reaches 90% of the disk size. The disk is expanded to be 50% larger (e.g., 8GB -> 12GB). Auto-scaling can only take place once every 6 hours. If within those 6 hours you reach 95% of the disk space, your project will enter read-only mode. - +The maximum Disk Storage Size for Pro Tier is 64TB. If you need more than this, [contact us](https://app.supabase.com/support/new) to learn more about the Enterprise plan. -If you intend to import a lot of data into your database which requires multiple disk expansions then [reach out to our team](https://app.supabase.com/support/new). For example, uploading more than 1.5x the current size of your database storage will put your database into [read-only mode](#read-only-mode). +The Disk Storage Size can also be manually expanded in [Database settings page](https://app.supabase.com/project/_/settings/database). The maximum default you can expand the Disk Storage to is 200GB. If you wish to manually expand it any more than 200B, please [contact us](https://app.supabase.com/support/new) to discuss expanding the 200GB limit. + + + +You may want to import a lot of data into your database which requires multiple disk expansions; for example, uploading more than 1.5x the current size of your database storage will put your database into [read-only mode](#read-only-mode). If so, it is highly recommended you increase the Disk Storage Size manually on the [Database settings page](https://app.supabase.com/project/_/settings/database). -The maximum Disk Size for Pro Tier is 64TB. If you need more than this, [contact us](https://app.supabase.com/support/new) to learn more about the Enterprise plan. - ### Free Tier Behavior Free Tier projects enter [read-only](#read-only-mode) mode when you exceed the 500mb limit. Once in read-only mode, you have several options: diff --git a/studio/components/interfaces/BillingV2/Subscription/Tier/SubscriptionTier.tsx b/studio/components/interfaces/BillingV2/Subscription/Tier/SubscriptionTier.tsx index 4fcdf9405d2..e61b6193a97 100644 --- a/studio/components/interfaces/BillingV2/Subscription/Tier/SubscriptionTier.tsx +++ b/studio/components/interfaces/BillingV2/Subscription/Tier/SubscriptionTier.tsx @@ -89,7 +89,7 @@ const SubscriptionTier = ({}: SubscriptionTierProps) => {

When this project exceeds its included usage quotas, it may become unresponsive. {currentPlan?.id === 'free' - ? 'If you wish to exceed the included usage, it is advised you upgrade to a paid plan.' + ? 'If you wish to exceed the included usage, you should upgrade to a paid plan.' : 'You can change the Cost Control settings if you plan on exceeding the included usage quotas.'}

@@ -98,7 +98,7 @@ const SubscriptionTier = ({}: SubscriptionTierProps) => { type="horizontal" value={daysWithinCycle - daysToCycleEnd} max={daysWithinCycle} - barClass="bg-scale-1100" + barClass="bg-scale-1200" labelBottom={`Current billing cycle (${billingCycleStart.format( 'MMM DD' )} - ${billingCycleEnd.format('MMM DD')})`} diff --git a/studio/components/interfaces/Settings/Database/DiskSizeConfiguration.tsx b/studio/components/interfaces/Settings/Database/DiskSizeConfiguration.tsx new file mode 100644 index 00000000000..34d0dea0b29 --- /dev/null +++ b/studio/components/interfaces/Settings/Database/DiskSizeConfiguration.tsx @@ -0,0 +1,225 @@ +import * as Tooltip from '@radix-ui/react-tooltip' +import { PermissionAction } from '@supabase/shared-types/out/constants' +import { useParams } from 'common/hooks' +import { FormHeader } from 'components/ui/Forms' +import Panel from 'components/ui/Panel' +import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query' +import { useProjectDiskResizeMutation } from 'data/config/project-disk-resize-mutation' +import { useProjectUsageQuery } from 'data/usage/project-usage-query' +import { checkPermissions, useStore } from 'hooks' +import Link from 'next/link' +import { useEffect, useState } from 'react' +import { Alert, Button, Form, InputNumber, Modal } from 'ui' +import { number, object } from 'yup' + +export interface DiskSizeConfigurationProps { + disabled?: boolean +} + +const DiskSizeConfiguration = ({ disabled = false }: DiskSizeConfigurationProps) => { + const { ui } = useStore() + const { ref: projectRef } = useParams() + + const canUpdateDiskSizeConfig = checkPermissions(PermissionAction.UPDATE, 'projects') + + const [showResetDbPass, setShowResetDbPass] = useState(false) + const [isUpdatingDiskSize, setIsUpdatingDiskSize] = useState(false) + + const { data: projectUsage } = useProjectUsageQuery({ projectRef }) + const { data: projectSubscriptionData } = useProjectSubscriptionQuery({ projectRef }) + const { mutateAsync: updateProjectUsage } = useProjectDiskResizeMutation() + + useEffect(() => { + if (showResetDbPass) { + setIsUpdatingDiskSize(false) + } + }, [showResetDbPass]) + + const confirmResetDbPass = async (values: { [prop: string]: any }) => { + const volumeSize = values['new-disk-size'] + if (!projectRef) return + + try { + setIsUpdatingDiskSize(true) + await updateProjectUsage({ projectRef, volumeSize }) + ui.setNotification({ + category: 'success', + message: `Successfully updated disk size to ${values['new-disk-size']} GB`, + }) + setIsUpdatingDiskSize(false) + setShowResetDbPass(false) + } catch (error: any) { + ui.setNotification({ category: 'error', message: error.message }) + setIsUpdatingDiskSize(false) + } + } + + const currentDiskSize = projectUsage?.disk_volume_size_gb ?? 0 + // to do, update with max_disk_volume_size_gb + const maxDiskSize = 200 + + const INITIAL_VALUES = { + 'new-disk-size': currentDiskSize, + } + + const diskSizeValidationSchema = object({ + 'new-disk-size': number() + .required('Please enter a GB amount you want to resize the disk up to.') + .moreThan(Number(currentDiskSize ?? 0), `Must be more than ${currentDiskSize} GB`) + // to do, update with max_disk_volume_size_gb + .lessThan(Number(maxDiskSize), 'Must be no more than 200 GB'), + }) + + return ( +
+ + {projectSubscriptionData?.tier.supabase_prod_id != 'tier_free' ? ( +
+ + +
+
+ {projectUsage?.disk_volume_size_gb && ( + + Current Disk Storage Size: + + {currentDiskSize} + GB + + + )} +

+ Supabase employs auto-scaling storage and allows for manual disk size
{' '} + adjustments when necessary +

+
+
+ + + + + {!canUpdateDiskSizeConfig && ( + + + +
+ + You need additional permissions to increase the disk size + +
+
+
+ )} +
+
+
+
+
+ +

+ We auto-scale your disk as you need more storage, but can only do this every 6 hours. + If you upload more than 1.5x the current size of your storage, your database will go + into read-only mode. If you know how big your database is going to be, you can + manually increase the size here. +

+ +

+ Read more about{' '} + + disk management + + . +

+
+
+ ) : ( + + + + } + > +
+ If you are intending to use more than 500MB of disk space, then you will need to upgrade + to at least the Pro tier. +
+
+ )} + + Increase Disk Storage Size} + size="small" + visible={showResetDbPass} + loading={isUpdatingDiskSize} + onCancel={() => setShowResetDbPass(false)} + hideFooter + > +
+ {({ isSubmitting }: { isSubmitting: boolean }) => + currentDiskSize >= maxDiskSize ? ( + <> + + You cannot manually expand the disk size any more than {maxDiskSize} GB. If you + need more than this, contact us to learn more about the Enterprise plan. + + + ) : ( + <> + +
+ +
+
+ + +
+ + +
+
+ + ) + } + +
+
+ ) +} + +export default DiskSizeConfiguration diff --git a/studio/data/config/project-disk-resize-mutation.ts b/studio/data/config/project-disk-resize-mutation.ts new file mode 100644 index 00000000000..97a8b4e9a46 --- /dev/null +++ b/studio/data/config/project-disk-resize-mutation.ts @@ -0,0 +1,60 @@ +import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' +import { post } from 'lib/common/fetch' +import { API_URL } from 'lib/constants' +import { usageKeys } from '../usage/keys' + +export type ProjectDiskResizeVariables = { + projectRef: string + volumeSize: number +} + +export type ProjectDiskResizeResponse = { + error?: any +} + +export async function resizeProjectDisk({ projectRef, volumeSize }: ProjectDiskResizeVariables) { + if (!projectRef) throw new Error('projectRef is required') + + const payload = { volume_size_gb: volumeSize } + + const response = (await post( + `${API_URL}/projects/${projectRef}/resize`, + payload + )) as ProjectDiskResizeResponse + if (response.error) throw response.error + + return response +} + +type ProjectDiskResizeData = Awaited> + +export const useProjectDiskResizeMutation = ({ + onSuccess, + ...options +}: Omit< + UseMutationOptions, + 'mutationFn' +> = {}) => { + const queryClient = useQueryClient() + + return useMutation( + (vars) => resizeProjectDisk(vars), + { + async onSuccess(data, variables, context) { + const { projectRef } = variables + + queryClient.setQueriesData(usageKeys.usage(projectRef), (prev) => { + if (!prev) return prev + + return { + ...prev, + disk_volume_size_gb: variables.volumeSize, + } + }) + + await onSuccess?.(data, variables, context) + }, + ...options, + } + ) +} diff --git a/studio/pages/project/[ref]/settings/database.tsx b/studio/pages/project/[ref]/settings/database.tsx index d9b90a1cecb..f4ce38c156c 100644 --- a/studio/pages/project/[ref]/settings/database.tsx +++ b/studio/pages/project/[ref]/settings/database.tsx @@ -10,6 +10,7 @@ import { } from 'components/interfaces/Settings/Database' import SSLConfiguration from 'components/interfaces/Settings/Database/SSLConfiguration' +import DiskSizeConfiguration from 'components/interfaces/Settings/Database/DiskSizeConfiguration' const ProjectSettings: NextPageWithLayout = () => { const { ref: projectRef } = useParams() @@ -21,6 +22,7 @@ const ProjectSettings: NextPageWithLayout = () => { + {networkRestrictions && }