From 68426bcd086dfe448239eca5b8c405ee856f1925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20Gr=C3=BCneberg?= Date: Wed, 31 May 2023 17:12:04 +0200 Subject: [PATCH] chore: improve usage v2 page --- .../pages/guides/platform/performance.mdx | 2 +- .../pages/guides/platform/troubleshooting.mdx | 2 +- .../BillingV2/Subscription/AddOns/AddOns.tsx | 4 +- .../Subscription/Tier/EnterpriseCard.tsx | 2 +- .../interfaces/BillingV2/Usage/Activity.tsx | 13 ++--- .../interfaces/BillingV2/Usage/Bandwidth.tsx | 13 ++--- .../BillingV2/Usage/Infrastructure.tsx | 38 +++++-------- .../BillingV2/Usage/SizeAndCounts.tsx | 16 +++--- .../BillingV2/Usage/Usage.constants.ts | 56 ++++++++++--------- .../interfaces/BillingV2/Usage/Usage.tsx | 3 +- .../BillingV2/Usage/UsageBarChart.tsx | 43 +------------- .../data/analytics/infra-monitoring-query.ts | 3 +- 12 files changed, 78 insertions(+), 117 deletions(-) diff --git a/apps/docs/pages/guides/platform/performance.mdx b/apps/docs/pages/guides/platform/performance.mdx index 609a303d26f..8dbe236cc52 100644 --- a/apps/docs/pages/guides/platform/performance.mdx +++ b/apps/docs/pages/guides/platform/performance.mdx @@ -124,7 +124,7 @@ Queries which are the most time consuming are not necessarily bad, you may have Generally for most applications a small percentage of data is accessed more regularly than the rest. To make sure that your regularly accessed data is available, Postgres tracks your data access patterns and keeps this in its [shared_buffers](https://www.postgresql.org/docs/15/runtime-config-resource.html#RUNTIME-CONFIG-RESOURCE-MEMORY) cache. -Applications with lower cache hit rates generally perform more poorly since they have to hit the disk to get results rather than serving them from memory. Very poor hit rates can also cause you to burst past your [Disk I/O limits](https://supabase.com/docs/guides/platform/compute-add-ons#disk-io-bandwidth) causing significant performance issues. +Applications with lower cache hit rates generally perform more poorly since they have to hit the disk to get results rather than serving them from memory. Very poor hit rates can also cause you to burst past your [Disk I/O limits](https://supabase.com/docs/guides/platform/compute-add-ons#disk-throughput-and-iops) causing significant performance issues. You can view your cache and index hit rate by executing the following query: diff --git a/apps/docs/pages/guides/platform/troubleshooting.mdx b/apps/docs/pages/guides/platform/troubleshooting.mdx index 5ac27b8b71d..5e4b244192b 100644 --- a/apps/docs/pages/guides/platform/troubleshooting.mdx +++ b/apps/docs/pages/guides/platform/troubleshooting.mdx @@ -31,7 +31,7 @@ Some common solutions for this issue are: - [Using fewer Postgres connections](../platform/performance#configuring-clients-to-use-fewer-connections) can reduce the amount of resources needed on the project. - [Restarting](https://app.supabase.com/project/_/settings/general) the database. This only temporarily solves the issue by terminating any ongoing workloads that might be tying up your compute resources. -If your [Daily Disk IO budget](../platform/compute-add-ons#disk-io-bandwidth) has been drained, you will need to either wait for it to be replenished the next day, or upgrade to a larger compute add-on to increase the budget available to your project. +If your [Daily Disk IO budget](../platform/compute-add-ons#disk-throughput-and-iops) has been drained, you will need to either wait for it to be replenished the next day, or upgrade to a larger compute add-on to increase the budget available to your project. ## Unable to connect to your Supabase Project diff --git a/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx b/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx index b66ef1fbe3c..86352c7d1e4 100644 --- a/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx +++ b/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx @@ -221,7 +221,7 @@ const AddOns = ({}: AddOnsProps) => {

{computeInstanceSpecs?.connections_pooler ?? 200}

- +

@@ -240,7 +240,7 @@ const AddOns = ({}: AddOnsProps) => {

- +

diff --git a/studio/components/interfaces/BillingV2/Subscription/Tier/EnterpriseCard.tsx b/studio/components/interfaces/BillingV2/Subscription/Tier/EnterpriseCard.tsx index f087cf8687d..f3ef23e63be 100644 --- a/studio/components/interfaces/BillingV2/Subscription/Tier/EnterpriseCard.tsx +++ b/studio/components/interfaces/BillingV2/Subscription/Tier/EnterpriseCard.tsx @@ -39,7 +39,7 @@ const EnterpriseCard = ({ plan, isCurrentPlan, isTeamTierEnabled }: EnterpriseCa diff --git a/studio/components/interfaces/BillingV2/Usage/Activity.tsx b/studio/components/interfaces/BillingV2/Usage/Activity.tsx index a5e7ef7af2a..987f30d3fff 100644 --- a/studio/components/interfaces/BillingV2/Usage/Activity.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Activity.tsx @@ -176,7 +176,7 @@ const Activity = ({ projectRef }: ActivityProps) => {

-

{attribute.name} quota usage

+

{attribute.name} usage

{!usageBasedBilling && usageRatio >= 1 ? (
{ )}
-

Used

+

+ {attribute.chartPrefix || 'Used '}in period +

{(usageMeta?.usage ?? 0).toLocaleString()}

{usageMeta.limit > 0 && ( @@ -254,7 +256,7 @@ const Activity = ({ projectRef }: ActivityProps) => {
-

{attribute.name} over time

+

{attribute.name} per day

{attribute.chartDescription.split('\n').map((paragraph, idx) => (

{paragraph} @@ -269,15 +271,12 @@ const Activity = ({ projectRef }: ActivityProps) => {

) : ( 0} - name={attribute.name} + name={`${attribute.chartPrefix || ''}${attribute.name}`} unit={attribute.unit} attribute={attribute.attribute} data={chartData} - yLimit={usageMeta?.limit ?? 0} yLeftMargin={chartMeta[attribute.key].margin} yFormatter={(value) => ChartYFormatterCompactNumber(value, attribute.unit)} - quotaWarningType={isFreeTier || isProTier ? 'danger' : 'warning'} /> )} diff --git a/studio/components/interfaces/BillingV2/Usage/Bandwidth.tsx b/studio/components/interfaces/BillingV2/Usage/Bandwidth.tsx index 1cfa638840e..4e1c17070e7 100644 --- a/studio/components/interfaces/BillingV2/Usage/Bandwidth.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Bandwidth.tsx @@ -101,7 +101,7 @@ const Bandwidth = ({ projectRef }: BandwidthProps) => {
-

{attribute.name} quota usage

+

{attribute.name} usage

{!usageBasedBilling && usageRatio >= 1 ? (
{

-

Used

+

+ {attribute.chartPrefix || 'Used '}in period +

{formatBytes(usageMeta?.usage ?? 0)}

{usageMeta?.limit > 0 && ( @@ -168,7 +170,7 @@ const Bandwidth = ({ projectRef }: BandwidthProps) => {
-

{attribute.name} over time

+

{attribute.name} per day

{attribute.chartDescription.split('\n').map((paragraph, idx) => (

{paragraph} @@ -183,15 +185,12 @@ const Bandwidth = ({ projectRef }: BandwidthProps) => {

) : ( 0} - name={attribute.name} + name={`${attribute.chartPrefix || ''}${attribute.name}`} unit={attribute.unit} attribute={attribute.attribute} data={chartData} - yLimit={usageMeta?.limit ?? 0} yLeftMargin={chartMeta[attribute.key].margin} yFormatter={(value) => ChartYFormatterCompactNumber(value, attribute.unit)} - quotaWarningType={isFreeTier || isProTier ? 'danger' : 'warning'} /> )} diff --git a/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx b/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx index 1dd2d1cc2ed..70477e64f4b 100644 --- a/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx @@ -77,17 +77,16 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => { const { data: ioBudgetData, isLoading: isLoadingIoBudgetData } = useInfraMonitoringQuery({ projectRef, - attribute: 'disk_io_budget', + attribute: 'disk_io_consumption', interval, startDate, endDate, dateFormat, }) - const currentDayIoBudget = Number( - ioBudgetData?.data.find((x) => x.periodStartFormatted === dayjs().format('DD MMM'))?.[ - 'disk_io_budget' - ] ?? 100 + const highestIoBudgetConsumption = Math.max( + ...(ioBudgetData?.data || []).map((x) => Number(x.disk_io_consumption) ?? 0), + 0 ) const chartMeta: { [key: string]: { data: DataPoint[]; isLoading: boolean } } = { @@ -99,7 +98,7 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => { isLoading: isLoadingMemoryUsageData, data: memoryUsageData?.data ?? [], }, - disk_io_budget: { + disk_io_consumption: { isLoading: isLoadingIoBudgetData, data: ioBudgetData?.data ?? [], }, @@ -116,14 +115,14 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => { return (
- {attribute.key === 'disk_io_budget' && ( + {attribute.key === 'disk_io_consumption' && ( <> - {currentDayIoBudget <= 0 ? ( + {highestIoBudgetConsumption >= 100 ? (

- Your workload has used up all the burst IO throughput minutes during the day - and is running at the baseline performance. If you need consistent disk - performance, consider upgrading to a larger compute add-on. + Your workload has used up all the burst IO throughput minutes and ran at the + baseline performance. If you need consistent disk performance, consider + upgrading to a larger compute add-on.

@@ -133,7 +132,7 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => {
- ) : currentDayIoBudget <= 20 ? ( + ) : highestIoBudgetConsumption >= 80 ? (

Your workload is about to use up all the burst IO throughput minutes during @@ -185,24 +184,17 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => { )}

- {attribute.key === 'disk_io_budget' ? ( -

IO Budget remaining

+ {attribute.key === 'disk_io_consumption' ? ( +

IO consumed per day

) : (

Max{' '} {attribute.name} {' '} - usage + per {interval === '1d' ? 'day' : 'hour'}

)} - - - - -
{attribute.chartDescription.split('\n').map((paragraph, idx) => ( @@ -219,7 +211,7 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => {
) : chartData.length ? ( {
-

{attribute.name} quota usage

+

{attribute.name} usage

{!usageBasedBilling && usageRatio >= 1 ? (
{

-

Used

+

+ {attribute.chartPrefix || 'Used '}in period +

{attribute.unit === 'bytes' ? formatBytes(usageMeta?.usage ?? 0) @@ -217,7 +219,10 @@ const SizeAndCounts = ({ projectRef }: SizeAndCountsProps) => { )}

-

{attribute.name} over time

+

+ {attribute.chartPrefix || ''} + {attribute.name} per day +

{attribute.chartDescription.split('\n').map((paragraph, idx) => (

{paragraph} @@ -232,15 +237,12 @@ const SizeAndCounts = ({ projectRef }: SizeAndCountsProps) => {

) : ( 0} - name={attribute.name} + name={`${attribute.chartPrefix || ''}${attribute.name}`} unit={attribute.unit} attribute={attribute.attribute} data={chartData} - yLimit={usageMeta?.limit ?? 0} yLeftMargin={chartMeta[attribute.key].margin} yFormatter={(value) => ChartYFormatterCompactNumber(value, attribute.unit)} - quotaWarningType={isFreeTier || isProTier ? 'danger' : 'warning'} /> )} diff --git a/studio/components/interfaces/BillingV2/Usage/Usage.constants.ts b/studio/components/interfaces/BillingV2/Usage/Usage.constants.ts index 4fad750de7a..03fdf26c6cd 100644 --- a/studio/components/interfaces/BillingV2/Usage/Usage.constants.ts +++ b/studio/components/interfaces/BillingV2/Usage/Usage.constants.ts @@ -11,6 +11,7 @@ export interface CategoryAttribute { key: string // Property from project usage attribute: string // For querying against stats-daily / infra-monitoring name: string + chartPrefix?: string unit: 'bytes' | 'absolute' | 'percentage' links?: { name: string @@ -29,7 +30,7 @@ export const USAGE_CATEGORIES: { { key: 'infra', name: 'Infrastructure', - description: 'Usage statistics related to your Postgres server', + description: 'Usage statistics related to your server instance', attributes: [ { anchor: 'cpu', @@ -63,19 +64,19 @@ export const USAGE_CATEGORIES: { ], }, { - anchor: 'disk_io_budget', - key: 'disk_io_budget', - attribute: 'disk_io_budget', + anchor: 'disk_io', + key: 'disk_io_consumption', + attribute: 'disk_io_consumption', name: 'Disk IO bandwidth', unit: 'percentage', links: [ { name: 'Documentation', - url: 'https://supabase.com/docs/guides/platform/compute-add-ons#disk-io-bandwidth', + url: 'https://supabase.com/docs/guides/platform/compute-add-ons#disk-throughput-and-iops', }, ], description: - 'SSD Disks are attached to your servers and the disk performance of your workload is determined by the Disk IO bandwidth of this connection.', + 'SSD Disks are attached to your servers. The disk performance of your workload is determined by the Disk IO bandwidth of this connection.', chartDescription: '', }, ], @@ -92,8 +93,8 @@ export const USAGE_CATEGORIES: { name: 'Database egress', unit: 'bytes', description: - 'Contains any outgoing traffic (egress) from your database\nBilling is based on the total sum of egress in GB throughout your billing period.', - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + 'Contains any outgoing traffic (egress) from your database.\nBilling is based on the total sum of egress in GB throughout your billing period.', + chartDescription: 'The data shown here refreshes every 24 hours.', }, { anchor: 'storageEgress', @@ -103,7 +104,7 @@ export const USAGE_CATEGORIES: { unit: 'bytes', description: 'All requests to download/view your storage items go through our CDN. We sum up all outgoing traffic (egress) for storage related requests through our CDN. We do not differentiate between cache and no cache hits.\nBilling is based on the total amount of egress in GB throughout your billing period.', - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + chartDescription: 'The data shown here refreshes every 24 hours.', }, ], }, @@ -117,6 +118,7 @@ export const USAGE_CATEGORIES: { key: 'db_size', attribute: 'total_db_size_bytes', name: 'Database size', + chartPrefix: 'Average ', unit: 'bytes', description: 'Billing is based on the average daily database size in GB throughout the billing period.', @@ -126,27 +128,29 @@ export const USAGE_CATEGORIES: { url: 'https://supabase.com/docs/guides/platform/database-size', }, ], - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + chartDescription: 'The data shown here refreshes every 24 hours.', }, { anchor: 'storageSize', key: 'storage_size', attribute: 'total_storage_size_bytes', name: 'Storage size', + chartPrefix: 'Max ', unit: 'bytes', description: - 'Sum of all objects in your storage buckets\nBilling is based on the average size in GB throughout your billing period', - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + 'Sum of all objects in your storage buckets.\nBilling is based on the average size in GB throughout your billing period', + chartDescription: 'The data shown here refreshes every 24 hours.', }, { anchor: 'funcCount', key: 'func_count', attribute: 'total_func_count', name: 'Edge function count', + chartPrefix: 'Max ', unit: 'absolute', description: - 'Number of serverless functions in your project\nBilling is based on the maximum amount of functions at any point in time throughout your billing period', - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + 'Number of serverless functions in your project.\nBilling is based on the maximum amount of functions at any point in time throughout your billing period', + chartDescription: 'The data shown here refreshes every 24 hours.', }, ], }, @@ -159,23 +163,23 @@ export const USAGE_CATEGORIES: { anchor: 'mau', key: 'monthly_active_users', attribute: 'total_auth_billing_period_mau', - name: 'Monthly active users', + name: 'Monthly Active Users', unit: 'absolute', description: - 'Users who log in or refresh their token\nBilling is based on the sum of distinct users requesting your API throughout the billing period. Resets every billing cycle.', + 'Users who log in or refresh their token count towards MAU.\nBilling is based on the sum of distinct users requesting your API throughout the billing period. Resets every billing cycle.', chartDescription: - 'The data shown here is refreshed over a period of 24 hours and resets at the beginning of every billing period.', + 'The data shown here is refreshed over a period of 24 hours and resets at the beginning of every billing period.\nThe data points are relative to the beginning of your billing period.', }, { anchor: 'mauSso', key: 'monthly_active_sso_users', attribute: 'total_auth_billing_period_sso_mau', - name: 'Monthly active single sign-on users', + name: 'Monthly Active SSO Users', unit: 'absolute', description: - 'SSO users who log in or refresh their token\nBilling is based on the sum of distinct Single Sign-On users requesting your API throughout the billing period. Resets every billing cycle.', + 'SSO users who log in or refresh their token count towards SSO MAU.\nBilling is based on the sum of distinct Single Sign-On users requesting your API throughout the billing period. Resets every billing cycle.', chartDescription: - 'The data shown here is refreshed over a period of 24 hours and resets at the beginning of every billing period.', + 'The data shown here is refreshed over a period of 24 hours and resets at the beginning of every billing period.\nThe data points are relative to the beginning of your billing period.', }, { anchor: 'storageImageTransformations', @@ -185,7 +189,8 @@ export const USAGE_CATEGORIES: { unit: 'absolute', description: 'We distinctly count all images that were transformed in the billing period, ignoring any transformations. If you transform one image with different transformations, it only counts as one.\nBilling is based on the unique count of (origin) images that used transformations throughout the billing period. Resets every billing cycle.', - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + chartDescription: + 'The data shown here refreshes every 24 hours.\nThe data points are relative to the beginning of your billing period.', }, { anchor: 'functionInvocations', @@ -195,7 +200,7 @@ export const USAGE_CATEGORIES: { unit: 'absolute', description: 'Every single serverless function invocation independent of response status is counted.\nBilling is based on the sum of all invocations throughout your billing period.', - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + chartDescription: 'The data shown here refreshes every 24 hours.', }, { anchor: 'realtimeMessageCount', @@ -205,17 +210,18 @@ export const USAGE_CATEGORIES: { unit: 'absolute', description: "Count of messages going through Realtime. If you do a database change and 5 clients listen to that change via Realtime, that's 5 messages. If you broadcast a message and 4 clients listen to that, that's 5 messages (1 message sent, 4 received).\nBilling is based on the total amount of messages throughout your billing period.", - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + chartDescription: 'The data shown here refreshes every 24 hours.', }, { anchor: 'realtimePeakConnection', key: 'realtime_peak_connection', attribute: 'total_realtime_peak_connection', name: 'Realtime peak connections', + chartPrefix: 'Max ', unit: 'absolute', description: - 'Total number of successful connections (not connection attempts)\nBilling is based on the maximum amount of concurrent peak connections throughout your billing period.', - chartDescription: 'The data shown here is refreshed over a period of 24 hours.', + 'Total number of successful connections (not connection attempts).\nBilling is based on the maximum amount of concurrent peak connections throughout your billing period.', + chartDescription: 'The data shown here refreshes every 24 hours.', }, ], }, diff --git a/studio/components/interfaces/BillingV2/Usage/Usage.tsx b/studio/components/interfaces/BillingV2/Usage/Usage.tsx index 12196d3279c..d96a78e9292 100644 --- a/studio/components/interfaces/BillingV2/Usage/Usage.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Usage.tsx @@ -57,7 +57,8 @@ const Usage = () => { const subscriptionTierId = subscription?.tier?.supabase_prod_id const usageBillingEnabled = - subscriptionTierId === PRICING_TIER_PRODUCT_IDS.FREE || PRICING_TIER_PRODUCT_IDS.PRO + subscriptionTierId !== PRICING_TIER_PRODUCT_IDS.FREE && + subscriptionTierId !== PRICING_TIER_PRODUCT_IDS.PRO const scrollTo = (id: 'infra' | 'bandwidth' | 'sizeCount' | 'activity') => { switch (id) { diff --git a/studio/components/interfaces/BillingV2/Usage/UsageBarChart.tsx b/studio/components/interfaces/BillingV2/Usage/UsageBarChart.tsx index 50551b14dfc..681a8f2b322 100644 --- a/studio/components/interfaces/BillingV2/Usage/UsageBarChart.tsx +++ b/studio/components/interfaces/BillingV2/Usage/UsageBarChart.tsx @@ -4,7 +4,6 @@ import { CartesianGrid, Cell, ComposedChart, - ReferenceLine, ResponsiveContainer, Tooltip, XAxis, @@ -21,11 +20,9 @@ export interface UsageBarChartProps { name: string // Used within the tooltip attribute: string unit: 'bytes' | 'absolute' | 'percentage' - hasQuota?: boolean yLimit?: number yLeftMargin?: number yFormatter?: (value: number | string) => string - quotaWarningType?: 'warning' | 'danger' } const UsageBarChart = ({ @@ -33,23 +30,14 @@ const UsageBarChart = ({ name, attribute, unit, - hasQuota = false, yLimit, yLeftMargin = 10, yFormatter, - quotaWarningType = 'warning', }: UsageBarChartProps) => { const yMin = 0 // We can consider passing this as a prop if there's a use case in the future - const yMax = (yLimit ?? 0) * Y_DOMAIN_CEILING_MULTIPLIER - const quotaWarningClass = quotaWarningType === 'warning' ? 'fill-amber-900' : 'fill-red-900' const yDomain = [yMin, yLimit ?? 0] - const ticks = - yLimit !== undefined && hasQuota - ? [yMin, (yMax - yMin) * 0.25, (yMax - yMin) * 0.5, (yMax - yMin) * 0.75, yMax].map((x) => - Math.ceil(x) - ) - : undefined + return (
@@ -62,7 +50,6 @@ const UsageBarChart = ({ axisLine={false} tickLine={{ stroke: 'none' }} domain={yDomain} - ticks={ticks} tickFormatter={yFormatter} /> {data.map((entry) => { - return ( - = yLimit && hasQuota - ? quotaWarningClass - : 'fill-scale-1200' - } - /> - ) + return })} - {hasQuota && yLimit && ( - ( - -
- INCLUDED USAGE -
-
- )} - /> - )}
diff --git a/studio/data/analytics/infra-monitoring-query.ts b/studio/data/analytics/infra-monitoring-query.ts index 9e6429756f6..fbc79aa22ff 100644 --- a/studio/data/analytics/infra-monitoring-query.ts +++ b/studio/data/analytics/infra-monitoring-query.ts @@ -8,7 +8,7 @@ import { analyticsKeys } from './keys' export type InfraMonitoringVariables = { projectRef?: string - attribute: 'cpu_usage' | 'disk_io_budget' | 'ram_usage' + attribute: 'cpu_usage' | 'disk_io_budget' | 'ram_usage' | 'disk_io_consumption' startDate?: string endDate?: string interval?: '1m' | '5m' | '10m' | '30m' | '1h' | '1d' @@ -78,6 +78,7 @@ export const useInfraMonitoringQuery = ( }), } as TData }, + staleTime: 1000 * 60, // default good for a minute ...options, } )