- )
- })}
- >
+
)
}
diff --git a/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx b/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx
index 70477e64f4b..9be2b2ffb20 100644
--- a/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx
+++ b/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx
@@ -1,17 +1,17 @@
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { DataPoint } from 'data/analytics/constants'
import { useInfraMonitoringQuery } from 'data/analytics/infra-monitoring-query'
-import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query'
import dayjs from 'dayjs'
-import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants'
import Link from 'next/link'
import { Alert, Button } from 'ui'
import SectionContent from './SectionContent'
import SectionHeader from './SectionHeader'
import { COMPUTE_INSTANCE_SPECS, USAGE_CATEGORIES } from './Usage.constants'
-import { getUpgradeUrl } from './Usage.utils'
+import { getUpgradeUrlFromV2Subscription } from './Usage.utils'
import UsageBarChart from './UsageBarChart'
import Panel from 'components/ui/Panel'
+import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscription-v2-query'
+import { useMemo } from 'react'
export interface InfrastructureProps {
projectRef: string
@@ -20,25 +20,27 @@ export interface InfrastructureProps {
// [Joshen] Need to update the IO budget chart to show burst mbps and duration next time
const Infrastructure = ({ projectRef }: InfrastructureProps) => {
- const { data: subscription } = useProjectSubscriptionQuery({ projectRef })
- const { current_period_start, current_period_end } = subscription?.billing ?? {}
- const startDate =
- current_period_start !== undefined
- ? new Date(current_period_start * 1000).toISOString()
- : undefined
- let endDate =
- current_period_end !== undefined ? new Date(current_period_end * 1000).toISOString() : undefined
+ const { data: subscription } = useProjectSubscriptionV2Query({ projectRef })
+ const { current_period_start, current_period_end } = subscription ?? {}
+ const startDate = useMemo(() => {
+ return current_period_start ? new Date(current_period_start * 1000).toISOString() : undefined
+ }, [current_period_start])
- // If end date is in future, set end date to now
- if (endDate && dayjs(endDate).isAfter(dayjs())) {
- // LF seems to have an issue with the milliseconds, causes infinite loading sometimes
- endDate = new Date().toISOString().slice(0, -5) + 'Z'
- }
+ const endDate = useMemo(() => {
+ const periodEndDate = current_period_end ? new Date(current_period_end * 1000) : undefined
+ // If end date is in future, set end date to now
+ if (periodEndDate && dayjs(periodEndDate).isAfter(dayjs())) {
+ // LF seems to have an issue with the milliseconds, causes infinite loading sometimes
+ return new Date().toISOString().slice(0, -5) + 'Z'
+ } else if (periodEndDate) {
+ return periodEndDate.toISOString()
+ }
+ }, [current_period_end])
const categoryMeta = USAGE_CATEGORIES.find((category) => category.key === 'infra')
- const upgradeUrl = getUpgradeUrl(projectRef, subscription)
- const isFreeTier = subscription?.tier.supabase_prod_id === PRICING_TIER_PRODUCT_IDS.FREE
+ const upgradeUrl = getUpgradeUrlFromV2Subscription(projectRef, subscription)
+ const isFreeTier = subscription?.plan?.id === 'free'
const currentComputeInstance = subscription?.addons.find((addon) =>
addon.supabase_prod_id.includes('_instance_')
)
@@ -150,12 +152,26 @@ const Infrastructure = ({ projectRef }: InfrastructureProps) => {
) : null}
-
What is Disk IO Bandwidth?
+
Disk IO Bandwidth
+
- Smaller compute instances can burst up to the maximum disk IO bandwidth for 30
- minutes in a day. Beyond that, the performance reverts to the baseline disk IO
- bandwidth.
+ The disk performance of your workload is determined by the Disk IO bandwidth.
+ Your current compute can has a baseline and maximum disk throughput of
+ {currentComputeInstanceSpecs.maxBandwidth.toLocaleString()} Mbps.
+
+ ) : (
+
+ Your current compute can burst up to{' '}
+ {currentComputeInstanceSpecs.maxBandwidth.toLocaleString()} Mbps for 30
+ minutes a day and reverts to the baseline performance of{' '}
+ {currentComputeInstanceSpecs.baseBandwidth.toLocaleString()} Mbps.
+
- )
- })}
- >
+
)
}
diff --git a/studio/components/interfaces/BillingV2/Usage/Usage.constants.ts b/studio/components/interfaces/BillingV2/Usage/Usage.constants.tsx
similarity index 61%
rename from studio/components/interfaces/BillingV2/Usage/Usage.constants.ts
rename to studio/components/interfaces/BillingV2/Usage/Usage.constants.tsx
index c8b99e2b1d4..c5433c5abbe 100644
--- a/studio/components/interfaces/BillingV2/Usage/Usage.constants.ts
+++ b/studio/components/interfaces/BillingV2/Usage/Usage.constants.tsx
@@ -1,3 +1,8 @@
+import { ProjectSubscriptionResponse } from 'data/subscriptions/project-subscription-v2-query'
+import { ProjectUsageData } from 'data/usage/project-usage-query'
+import Link from 'next/link'
+import { Badge, Button, IconExternalLink } from 'ui'
+
export const Y_DOMAIN_CEILING_MULTIPLIER = 4 / 3
export const USAGE_STATUS = {
@@ -19,14 +24,22 @@ export interface CategoryAttribute {
}[]
description: string
chartDescription: string
+ additionalInfo?: (
+ subscription?: ProjectSubscriptionResponse,
+ usage?: ProjectUsageData
+ ) => JSX.Element | null
}
-export const USAGE_CATEGORIES: {
+export type CategoryMetaKey = 'infra' | 'bandwidth' | 'sizeCount' | 'activity'
+
+export interface CategoryMeta {
key: 'infra' | 'bandwidth' | 'sizeCount' | 'activity'
name: string
description: string
attributes: CategoryAttribute[]
-}[] = [
+}
+
+export const USAGE_CATEGORIES: CategoryMeta[] = [
{
key: 'infra',
name: 'Infrastructure',
@@ -76,7 +89,7 @@ export const USAGE_CATEGORIES: {
},
],
description:
- 'SSD Disks are attached to your servers. The disk performance of your workload is determined by the Disk IO bandwidth of this connection.',
+ 'Smaller compute instances (below 4XL) can burst up to their largest throughput and IOPS for 30 minutes in a day. Beyond that, the performance reverts to the baseline. Your disk budget gets replenished throughout the day.',
chartDescription: '',
},
],
@@ -84,7 +97,7 @@ export const USAGE_CATEGORIES: {
{
key: 'bandwidth',
name: 'Bandwidth',
- description: 'Amount of data transmitted over network connections',
+ description: 'Amount of data transmitted over all network connections',
attributes: [
{
anchor: 'dbEgress',
@@ -94,7 +107,7 @@ export const USAGE_CATEGORIES: {
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 refreshes every 24 hours.',
+ chartDescription: 'The data refreshes every 24 hours.',
},
{
anchor: 'storageEgress',
@@ -103,8 +116,8 @@ export const USAGE_CATEGORIES: {
name: 'Storage egress',
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 refreshes every 24 hours.',
+ 'All requests to view and download 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 refreshes every 24 hours.',
},
],
},
@@ -128,7 +141,26 @@ export const USAGE_CATEGORIES: {
url: 'https://supabase.com/docs/guides/platform/database-size',
},
],
- chartDescription: 'The data shown here refreshes every 24 hours.',
+ chartDescription: 'The data refreshes every 24 hours.',
+ additionalInfo: (subscription?: ProjectSubscriptionResponse, usage?: ProjectUsageData) =>
+ subscription?.plan?.id !== 'free' ? (
+
+ ) : null,
},
{
anchor: 'storageSize',
@@ -138,8 +170,8 @@ export const USAGE_CATEGORIES: {
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 refreshes every 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 refreshes every 24 hours.',
},
{
anchor: 'funcCount',
@@ -149,8 +181,8 @@ export const USAGE_CATEGORIES: {
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 refreshes every 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 refreshes every 24 hours.',
},
],
},
@@ -168,7 +200,7 @@ export const USAGE_CATEGORIES: {
description:
'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.\nThe data points are relative to the beginning of your billing period.',
+ 'The data 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',
@@ -179,7 +211,7 @@ export const USAGE_CATEGORIES: {
description:
'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.\nThe data points are relative to the beginning of your billing period.',
+ 'The data refreshes 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',
@@ -188,9 +220,9 @@ export const USAGE_CATEGORIES: {
name: 'Storage image transformations',
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.',
+ 'We count all images that were transformed in the billing period, ignoring any transformations.\nUsage example: You transform one image with four different size transformations and another image with just a single transformations. It counts as two, as only two images were transformed.\nBilling is based on the count of (origin) images that used transformations throughout the billing period. Resets every billing cycle.',
chartDescription:
- 'The data shown here refreshes every 24 hours.\nThe data points are relative to the beginning of your billing period.',
+ 'The data refreshes every 24 hours.\nThe data points are relative to the beginning of your billing period.',
},
{
anchor: 'functionInvocations',
@@ -199,8 +231,8 @@ export const USAGE_CATEGORIES: {
name: 'Edge function invocations',
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 refreshes every 24 hours.',
+ 'Every 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 refreshes every 24 hours.',
},
{
anchor: 'realtimeMessageCount',
@@ -209,8 +241,8 @@ export const USAGE_CATEGORIES: {
name: 'Realtime message count',
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 refreshes every 24 hours.',
+ "Count of messages going through Realtime.\nUsage example: 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 refreshes every 24 hours.',
},
{
anchor: 'realtimePeakConnection',
@@ -221,7 +253,7 @@ export const USAGE_CATEGORIES: {
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 refreshes every 24 hours.',
+ chartDescription: 'The data refreshes every 24 hours.',
},
],
},
@@ -229,45 +261,65 @@ export const USAGE_CATEGORIES: {
// [Joshen] Ideally live in common package for www as well
export const COMPUTE_INSTANCE_SPECS: {
- [key: string]: { maxBandwidth: number; baseBandwidth: number }
+ [key: string]: { maxBandwidth: number; baseBandwidth: number; memoryGb: number; cpuCores: number }
} = {
addon_instance_micro: {
+ memoryGb: 1,
+ cpuCores: 2,
maxBandwidth: 2085,
baseBandwidth: 87,
},
addon_instance_small: {
+ memoryGb: 2,
+ cpuCores: 2,
maxBandwidth: 2085,
baseBandwidth: 174,
},
addon_instance_medium: {
+ memoryGb: 4,
+ cpuCores: 2,
maxBandwidth: 2085,
baseBandwidth: 347,
},
addon_instance_large: {
+ memoryGb: 8,
+ cpuCores: 2,
maxBandwidth: 4750,
baseBandwidth: 630,
},
addon_instance_xlarge: {
+ memoryGb: 16,
+ cpuCores: 4,
maxBandwidth: 4750,
baseBandwidth: 1188,
},
addon_instance_xxlarge: {
+ memoryGb: 32,
+ cpuCores: 8,
maxBandwidth: 4750,
baseBandwidth: 2375,
},
addon_instance_4xlarge: {
+ memoryGb: 64,
+ cpuCores: 16,
maxBandwidth: 4750,
baseBandwidth: 4750,
},
addon_instance_8xlarge: {
+ memoryGb: 128,
+ cpuCores: 32,
maxBandwidth: 9500,
baseBandwidth: 9500,
},
addon_instance_12xlarge: {
+ memoryGb: 192,
+ cpuCores: 48,
maxBandwidth: 14250,
baseBandwidth: 14250,
},
addon_instance_16xlarge: {
+ memoryGb: 256,
+ cpuCores: 64,
maxBandwidth: 19000,
baseBandwidth: 19000,
},
diff --git a/studio/components/interfaces/BillingV2/Usage/Usage.tsx b/studio/components/interfaces/BillingV2/Usage/Usage.tsx
index d96a78e9292..01d1fc8f79a 100644
--- a/studio/components/interfaces/BillingV2/Usage/Usage.tsx
+++ b/studio/components/interfaces/BillingV2/Usage/Usage.tsx
@@ -1,11 +1,10 @@
import clsx from 'clsx'
import { useParams } from 'common'
-import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query'
import { useProjectUsageQuery } from 'data/usage/project-usage-query'
import dayjs from 'dayjs'
import Link from 'next/link'
-import { useRef, useState } from 'react'
-import { Button, IconAlertCircle, IconCheckCircle, IconLoader, Listbox } from 'ui'
+import { useMemo, useRef, useState } from 'react'
+import { Button, IconAlertCircle, IconLoader, Listbox } from 'ui'
import Activity from './Activity'
import Bandwidth from './Bandwidth'
import Infrastructure from './Infrastructure'
@@ -13,7 +12,7 @@ import SizeAndCounts from './SizeAndCounts'
import { USAGE_CATEGORIES, USAGE_STATUS } from './Usage.constants'
import { getUsageStatus } from './Usage.utils'
import { useInfraMonitoringQuery } from 'data/analytics/infra-monitoring-query'
-import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants'
+import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscription-v2-query'
const Usage = () => {
const { ref } = useParams()
@@ -28,17 +27,38 @@ const Usage = () => {
const activityRef = useRef(null)
const { data: usage } = useProjectUsageQuery({ projectRef: ref })
- const { data: subscription, isLoading: isLoadingSubscription } = useProjectSubscriptionQuery({
+ const { data: subscription, isLoading: isLoadingSubscription } = useProjectSubscriptionV2Query({
projectRef: selectedProjectRef,
})
- const { current_period_start, current_period_end } = subscription?.billing ?? {}
- const startDate =
- current_period_start !== undefined
- ? new Date(current_period_start * 1000).toISOString()
- : undefined
+ const { current_period_start, current_period_end } = subscription ?? {}
+
+ const startDate = useMemo(() => {
+ return current_period_start ? new Date(current_period_start * 1000).toISOString() : undefined
+ }, [current_period_start])
+
const endDate =
current_period_end !== undefined ? new Date(current_period_end * 1000).toISOString() : undefined
+
+ const dailyStatsEndDate = useMemo(() => {
+ // If end date is in future, set end date to now
+ if (endDate && dayjs(endDate).isAfter(dayjs())) {
+ const yesterday = dayjs(new Date()).subtract(1, 'day')
+
+ /**
+ * Currently, daily-stats data is only available a day later, so we'll use yesterday as end date, as otherwise the current day would just show up with "0" values
+ *
+ * We are actively working on removing this restriction on the data-eng/LF side and can remove this workaround once that's done
+ */
+ const newEndDate = yesterday.isAfter(dayjs(startDate)) ? yesterday : new Date()
+
+ // LF seems to have an issue with the milliseconds, causes infinite loading sometimes
+ return newEndDate.toISOString().slice(0, -5) + 'Z'
+ } else if (endDate) {
+ return endDate
+ }
+ }, [endDate, startDate])
+
const { data: ioBudgetData } = useInfraMonitoringQuery({
projectRef: selectedProjectRef,
attribute: 'disk_io_budget',
@@ -52,13 +72,10 @@ const Usage = () => {
] ?? 100
)
- const billingCycleStart = dayjs.unix(subscription?.billing?.current_period_start ?? 0).utc()
- const billingCycleEnd = dayjs.unix(subscription?.billing?.current_period_end ?? 0).utc()
+ const billingCycleStart = dayjs.unix(subscription?.current_period_start ?? 0).utc()
+ const billingCycleEnd = dayjs.unix(subscription?.current_period_end ?? 0).utc()
- const subscriptionTierId = subscription?.tier?.supabase_prod_id
- const usageBillingEnabled =
- subscriptionTierId !== PRICING_TIER_PRODUCT_IDS.FREE &&
- subscriptionTierId !== PRICING_TIER_PRODUCT_IDS.PRO
+ const usageBillingEnabled = subscription?.usage_billing_enabled
const scrollTo = (id: 'infra' | 'bandwidth' | 'sizeCount' | 'activity') => {
switch (id) {
@@ -107,7 +124,7 @@ const Usage = () => {
) : subscription !== undefined ? (
- Project is on {subscription.tier.name}
+ Project is on {subscription.plan.name} plan