mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 01:15:03 +03:00
fix(orioledb): use alpha and beta conditionally based on AMI version (#51162)
## Problem - Older orioledb projects show "Public Beta" instead of "Public Alpha" in UI. - List of backups in the "Scheduled backups" tab hangs. ## Solution - show in the UI "Public Alpha" for projects older than 17.11.0.001-orioledb - show in the UI "Public Beta" for new projects - enable scheduled-backup query for "Public Beta" <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Backup availability messages now reflect OrioleDB’s current release stage rather than always describing it as public beta. * AWS backup queries are no longer disabled for every OrioleDB project; they remain disabled during the alpha stage. * **New Features** * Added an informational notice and documentation link for scheduled backups on AWS OrioleDB projects in alpha. * Added release-stage details to the PITR availability notice. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Charis <26616127+charislam@users.noreply.github.com>
This commit is contained in:
1 parent
895f016a54
commit
4b2d163a1d
7 files changed
+243
-7
No files matched your search
+3
-1
@@ -30,6 +30,7 @@ import {
|
||||
useIsAwsK8sCloudProvider,
|
||||
useIsHighAvailability,
|
||||
useIsOrioleDb,
|
||||
useOrioleDbReleaseStage,
|
||||
useSelectedProjectQuery,
|
||||
} from '@/hooks/misc/useSelectedProject'
|
||||
import { DOCS_URL, PROJECT_STATUS } from '@/lib/constants'
|
||||
@@ -41,6 +42,7 @@ export const RestoreToNewProject = () => {
|
||||
const { hasAccess: hasAccessToRestoreToNewProject, isLoading: isLoadingEntitlement } =
|
||||
useCheckEntitlements('backup.restore_to_new_project')
|
||||
const isOrioleDb = useIsOrioleDb()
|
||||
const orioleDbReleaseStage = useOrioleDbReleaseStage()
|
||||
const isAwsK8s = useIsAwsK8sCloudProvider()
|
||||
const isHighAvailability = useIsHighAvailability()
|
||||
|
||||
@@ -130,7 +132,7 @@ export const RestoreToNewProject = () => {
|
||||
<Admonition
|
||||
type="default"
|
||||
title="Restoring to new projects are not available for OrioleDB"
|
||||
description="OrioleDB is currently in public beta and restoring to a new project is unavailable on OrioleDB projects"
|
||||
description={`OrioleDB is currently in public ${orioleDbReleaseStage} and restoring to a new project is unavailable on OrioleDB projects`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query'
|
||||
import { databaseKeys } from './keys'
|
||||
import type { components } from '@/data/api'
|
||||
import { get, handleError } from '@/data/fetchers'
|
||||
import { useIsOrioleDbInAws } from '@/hooks/misc/useSelectedProject'
|
||||
import { useIsOrioleDbInAws, useOrioleDbReleaseStage } from '@/hooks/misc/useSelectedProject'
|
||||
import { PROJECT_STATUS } from '@/lib/constants'
|
||||
import type { ResponseError, UseCustomQueryOptions } from '@/types'
|
||||
|
||||
@@ -35,13 +35,14 @@ export const useBackupsQuery = <TData = BackupsData>(
|
||||
) => {
|
||||
// [Joshen] Check for specifically false to account for project not loaded yet
|
||||
const isOrioleDbInAws = useIsOrioleDbInAws()
|
||||
const orioleDbReleaseStage = useOrioleDbReleaseStage()
|
||||
|
||||
return useQuery<BackupsData, BackupsError, TData>({
|
||||
queryKey: databaseKeys.backups(projectRef),
|
||||
queryFn: ({ signal }) => getBackups({ projectRef }, signal),
|
||||
enabled:
|
||||
enabled &&
|
||||
!isOrioleDbInAws &&
|
||||
!(isOrioleDbInAws && orioleDbReleaseStage === 'alpha') &&
|
||||
typeof projectRef !== 'undefined' &&
|
||||
projectStatus !== PROJECT_STATUS.COMING_UP &&
|
||||
projectStatus !== PROJECT_STATUS.UNKNOWN,
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { useParams } from 'common'
|
||||
|
||||
import {
|
||||
isOrioleDbVersionAtLeast,
|
||||
ORIOLEDB_PUBLIC_BETA_VERSION,
|
||||
type OrioleDbReleaseStage,
|
||||
} from './useSelectedProject.utils'
|
||||
import { useProjectDetailQuery } from '@/data/projects/project-detail-query'
|
||||
import { PROJECT_STATUS, PROVIDERS } from '@/lib/constants'
|
||||
|
||||
@@ -54,6 +59,16 @@ export const useIsOrioleDbInAws = () => {
|
||||
return isOrioleDbInAws
|
||||
}
|
||||
|
||||
// OrioleDB moved from Public Alpha to Public Beta at engine version 17.11.0.001.
|
||||
// PITR remains unsupported for every OrioleDB version regardless of stage.
|
||||
export const useOrioleDbReleaseStage = (): OrioleDbReleaseStage | undefined => {
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
if (!project?.dbVersion?.endsWith('orioledb')) return undefined
|
||||
return isOrioleDbVersionAtLeast(project.dbVersion, ORIOLEDB_PUBLIC_BETA_VERSION)
|
||||
? 'beta'
|
||||
: 'alpha'
|
||||
}
|
||||
|
||||
// [Joshen TODO] There's a duplicate method `resolveHighAvailability` in `useHighAvailability.constants`
|
||||
export const useIsHighAvailability = () => {
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
appVersionAtLeast,
|
||||
compareAppVersions,
|
||||
isOrioleDbVersionAtLeast,
|
||||
parseAppVersionKey,
|
||||
} from './useSelectedProject.utils'
|
||||
|
||||
describe('parseAppVersionKey', () => {
|
||||
it('parses a dbVersion string into a version key', () => {
|
||||
expect(parseAppVersionKey('supabase-postgres-17.9.9.999-orioledb')).toEqual([17, 9, 9, 999])
|
||||
})
|
||||
|
||||
it('parses dbVersion strings with an arch prefix', () => {
|
||||
expect(parseAppVersionKey('supabase-postgres-arm64-17.9.0.019')).toEqual([17, 9, 0, 19])
|
||||
expect(parseAppVersionKey('supabase-postgres-x86_64-17.9.0.019')).toEqual([17, 9, 0, 19])
|
||||
})
|
||||
|
||||
it('returns null for non-conforming strings', () => {
|
||||
expect(parseAppVersionKey('CUSTOM_AMI_SENTINEL')).toBeNull()
|
||||
expect(parseAppVersionKey('not-a-version')).toBeNull()
|
||||
})
|
||||
|
||||
it('returns null for null or undefined input', () => {
|
||||
expect(parseAppVersionKey(null)).toBeNull()
|
||||
expect(parseAppVersionKey(undefined)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('compareAppVersions', () => {
|
||||
it('returns a negative number when a is older than b', () => {
|
||||
expect(
|
||||
compareAppVersions('supabase-postgres-17.8.9.999', 'supabase-postgres-17.9.0.019')
|
||||
).toBeLessThan(0)
|
||||
})
|
||||
|
||||
it('returns zero when versions are equal', () => {
|
||||
expect(compareAppVersions('supabase-postgres-17.9.0.019', 'supabase-postgres-17.9.0.019')).toBe(
|
||||
0
|
||||
)
|
||||
})
|
||||
|
||||
it('returns a positive number when a is newer than b', () => {
|
||||
expect(
|
||||
compareAppVersions('supabase-postgres-17.11.0.001', 'supabase-postgres-17.9.0.019')
|
||||
).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
it('compares numerically rather than by segment digit-width', () => {
|
||||
// Regression case for the digit-concatenation bug in lib/helpers.ts'
|
||||
// getSemanticVersion, where '15.1.1.2' (-> '15112') compares as less than
|
||||
// '14.1.0.44' (-> '141044') despite being the newer version.
|
||||
expect(
|
||||
compareAppVersions('supabase-postgres-15.1.1.2', 'supabase-postgres-14.1.0.44')
|
||||
).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
it('throws on unparseable input', () => {
|
||||
expect(() => compareAppVersions('not-a-version', 'supabase-postgres-17.9.0.019')).toThrow()
|
||||
expect(() => compareAppVersions('supabase-postgres-17.9.0.019', 'not-a-version')).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe('appVersionAtLeast', () => {
|
||||
it('returns true when the version is above or equal to the threshold', () => {
|
||||
expect(appVersionAtLeast('supabase-postgres-17.9.9.999', 'supabase-postgres-17.9.0.019')).toBe(
|
||||
true
|
||||
)
|
||||
expect(appVersionAtLeast('supabase-postgres-17.9.0.019', 'supabase-postgres-17.9.0.019')).toBe(
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('returns false when the version is below the threshold', () => {
|
||||
expect(appVersionAtLeast('supabase-postgres-17.8.9.999', 'supabase-postgres-17.9.0.019')).toBe(
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
it('returns false for an unparseable version instead of throwing', () => {
|
||||
expect(appVersionAtLeast('not-a-version', 'supabase-postgres-17.9.0.019')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isOrioleDbVersionAtLeast', () => {
|
||||
it('returns true when the engine version is above the threshold', () => {
|
||||
expect(
|
||||
isOrioleDbVersionAtLeast(
|
||||
'supabase-postgres-17.11.0.002-orioledb',
|
||||
'supabase-postgres-17.11.0.001-orioledb'
|
||||
)
|
||||
).toBe(true)
|
||||
expect(
|
||||
isOrioleDbVersionAtLeast(
|
||||
'supabase-postgres-17.12.0.001-orioledb',
|
||||
'supabase-postgres-17.11.0.001-orioledb'
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('returns true when the engine version equals the threshold', () => {
|
||||
expect(
|
||||
isOrioleDbVersionAtLeast(
|
||||
'supabase-postgres-17.11.0.001-orioledb',
|
||||
'supabase-postgres-17.11.0.001-orioledb'
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('returns false when the engine version is below the threshold', () => {
|
||||
expect(
|
||||
isOrioleDbVersionAtLeast(
|
||||
'supabase-postgres-17.10.9.999-orioledb',
|
||||
'supabase-postgres-17.11.0.001-orioledb'
|
||||
)
|
||||
).toBe(false)
|
||||
// wal-g (scheduled backup) support landed at 17.9.0.019, which is still
|
||||
// below the Public Beta cutoff — these projects remain alpha.
|
||||
expect(
|
||||
isOrioleDbVersionAtLeast(
|
||||
'supabase-postgres-17.9.0.019-orioledb',
|
||||
'supabase-postgres-17.11.0.001-orioledb'
|
||||
)
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for a non-OrioleDB dbVersion', () => {
|
||||
expect(
|
||||
isOrioleDbVersionAtLeast(
|
||||
'supabase-postgres-17.11.0.001',
|
||||
'supabase-postgres-17.11.0.001-orioledb'
|
||||
)
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for undefined dbVersion', () => {
|
||||
expect(isOrioleDbVersionAtLeast(undefined, 'supabase-postgres-17.11.0.001-orioledb')).toBe(
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
it('returns false for a non-OrioleDB threshold', () => {
|
||||
expect(
|
||||
isOrioleDbVersionAtLeast(
|
||||
'supabase-postgres-17.11.0.001-orioledb',
|
||||
'supabase-postgres-17.11.0.001'
|
||||
)
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,42 @@
|
||||
export type OrioleDbReleaseStage = 'alpha' | 'beta'
|
||||
|
||||
// OrioleDB engine versions ship as 4-part strings (e.g. `17.11.0.001`), which
|
||||
// `lib/semver` doesn't parse (it's strict 3-part semver), so compare them separately here.
|
||||
export const ORIOLEDB_PUBLIC_BETA_VERSION = 'supabase-postgres-17.11.0.001-orioledb'
|
||||
|
||||
// Sortable [major, minor, patch, build] key parsed from a dbVersion string like
|
||||
// `supabase-postgres-17.11.0.001-orioledb` or `supabase-postgres-arm64-17.11.0.001`.
|
||||
const APP_VERSION_KEY_RE = /^supabase-postgres-(?:arm64-|x86_64-)?(\d+)\.(\d+)\.(\d+)\.(\d+)/
|
||||
|
||||
export type AppVersionKey = [number, number, number, number]
|
||||
|
||||
// Returns null for non-conforming strings — callers should treat null as "not orderable".
|
||||
export function parseAppVersionKey(version: string | null | undefined): AppVersionKey | null {
|
||||
if (!version) return null
|
||||
|
||||
const match = APP_VERSION_KEY_RE.exec(version)
|
||||
if (!match) return null
|
||||
|
||||
return [Number(match[1]), Number(match[2]), Number(match[3]), Number(match[4])]
|
||||
}
|
||||
|
||||
// Negative when `a` is older, zero when equal, positive when newer.
|
||||
// Throws on unparseable input — null-tolerant callers parse first.
|
||||
export function compareAppVersions(a: string, b: string): number {
|
||||
const keyA = parseAppVersionKey(a)
|
||||
const keyB = parseAppVersionKey(b)
|
||||
if (!keyA) throw new Error(`unparseable app_version: ${a}`)
|
||||
if (!keyB) throw new Error(`unparseable app_version: ${b}`)
|
||||
|
||||
return keyA[0] - keyB[0] || keyA[1] - keyB[1] || keyA[2] - keyB[2] || keyA[3] - keyB[3]
|
||||
}
|
||||
|
||||
export function appVersionAtLeast(version: string, threshold: string): boolean {
|
||||
if (!parseAppVersionKey(version)) return false
|
||||
return compareAppVersions(version, threshold) >= 0
|
||||
}
|
||||
|
||||
export const isOrioleDbVersionAtLeast = (dbVersion: string | undefined, threshold: string) => {
|
||||
if (!dbVersion?.endsWith('orioledb') || !threshold.endsWith('orioledb')) return false
|
||||
return appVersionAtLeast(dbVersion, threshold)
|
||||
}
|
||||
@@ -28,7 +28,11 @@ import { useBackupsQuery } from '@/data/database/backups-query'
|
||||
import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
|
||||
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
|
||||
import { useHighAvailability } from '@/hooks/misc/useHighAvailability'
|
||||
import { useIsOrioleDbInAws, useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
||||
import {
|
||||
useIsOrioleDbInAws,
|
||||
useOrioleDbReleaseStage,
|
||||
useSelectedProjectQuery,
|
||||
} from '@/hooks/misc/useSelectedProject'
|
||||
import { DOCS_URL, PROJECT_STATUS } from '@/lib/constants'
|
||||
import type { NextPageWithLayout } from '@/types'
|
||||
|
||||
@@ -71,6 +75,7 @@ const PITR = () => {
|
||||
const { hasAccess: hasAccessToPitr, isLoading: isLoadingEntitlements } =
|
||||
useCheckEntitlements('pitr.available_variants')
|
||||
const isOrioleDbInAws = useIsOrioleDbInAws()
|
||||
const orioleDbReleaseStage = useOrioleDbReleaseStage()
|
||||
const {
|
||||
data: backups,
|
||||
error,
|
||||
@@ -96,8 +101,8 @@ const PITR = () => {
|
||||
return (
|
||||
<Admonition
|
||||
type="default"
|
||||
title="Database backups are not available for OrioleDB"
|
||||
description="OrioleDB is currently in public beta and Point-in-Time Recovery is unavailable on OrioleDB projects"
|
||||
title="Point-in-Time Recovery is not available for OrioleDB"
|
||||
description={`OrioleDB is currently in public ${orioleDbReleaseStage} and Point-in-Time Recovery is unavailable on OrioleDB projects`}
|
||||
>
|
||||
<DocsButton abbrev={false} className="mt-2" href={DOCS_URL} />
|
||||
</Admonition>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { useParams } from 'common'
|
||||
import { DatabaseBackup, Info } from 'lucide-react'
|
||||
import { Admonition } from 'ui-patterns/Admonition'
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import {
|
||||
PageHeader,
|
||||
@@ -17,12 +18,17 @@ import DatabaseBackupsNav from '@/components/interfaces/Database/Backups/Databas
|
||||
import { DatabaseLayout } from '@/components/layouts/DatabaseLayout/DatabaseLayout'
|
||||
import { DefaultLayout } from '@/components/layouts/DefaultLayout'
|
||||
import { AlertError } from '@/components/ui/AlertError'
|
||||
import { DocsButton } from '@/components/ui/DocsButton'
|
||||
import { HighAvailabilityDisabledEmptyState } from '@/components/ui/HighAvailability/HighAvailabilityDisabledEmptyState'
|
||||
import InformationBox from '@/components/ui/InformationBox'
|
||||
import { NoPermission } from '@/components/ui/NoPermission'
|
||||
import { useBackupsQuery } from '@/data/database/backups-query'
|
||||
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
|
||||
import { useIsHighAvailability } from '@/hooks/misc/useSelectedProject'
|
||||
import {
|
||||
useIsHighAvailability,
|
||||
useIsOrioleDbInAws,
|
||||
useOrioleDbReleaseStage,
|
||||
} from '@/hooks/misc/useSelectedProject'
|
||||
import { DOCS_URL } from '@/lib/constants'
|
||||
import type { NextPageWithLayout } from '@/types'
|
||||
|
||||
@@ -62,6 +68,8 @@ const ScheduledBackups = () => {
|
||||
} = useBackupsQuery({ projectRef })
|
||||
|
||||
const isHighAvailability = useIsHighAvailability()
|
||||
const isOrioleDbInAws = useIsOrioleDbInAws()
|
||||
const orioleDbReleaseStage = useOrioleDbReleaseStage()
|
||||
const isPitrEnabled = backups?.pitr_enabled
|
||||
|
||||
const { can: canReadScheduledBackups, isSuccess: isPermissionsLoaded } = useAsyncCheckPermissions(
|
||||
@@ -69,6 +77,18 @@ const ScheduledBackups = () => {
|
||||
'back_ups'
|
||||
)
|
||||
|
||||
if (isOrioleDbInAws && orioleDbReleaseStage === 'alpha') {
|
||||
return (
|
||||
<Admonition
|
||||
type="default"
|
||||
title="Database backups are not available for OrioleDB"
|
||||
description={`OrioleDB is currently in public ${orioleDbReleaseStage} and projects created are strictly ephemeral with no database backups`}
|
||||
>
|
||||
<DocsButton abbrev={false} className="mt-2" href={DOCS_URL} />
|
||||
</Admonition>
|
||||
)
|
||||
}
|
||||
|
||||
if (isHighAvailability) {
|
||||
return (
|
||||
<HighAvailabilityDisabledEmptyState
|
||||
|
||||
Reference in new issue
Block a user