feat: add IPv4 add on direct conn notice (#26903)

* feat: add IPv4 add on direct conn notice

* feat: add session mode warning

* fix: remove statement from Supavisor-related types

* Revert change in api types file

* Revert change in api types file

* Update API types file via codegen

* Smol feex

* Temp lint fix

* Fix

* fix: include serverless mention

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
Wen Bo XieandJoshen Lim authored and GitHub committed 2024-05-31 17:07:25 +00:00
1 parent d2bd47a32a
commit cd930f5a2f
9 files changed
+397 -735

No files matched your search

@@ -57,7 +57,7 @@ const StringToPositiveNumber = z.union([
const FormSchema = z.object({
default_pool_size: StringToPositiveNumber,
pool_mode: z.union([z.literal('transaction'), z.literal('session'), z.literal('statement')]),
pool_mode: z.union([z.literal('transaction'), z.literal('session')]),
max_client_conn: StringToPositiveNumber,
})
@@ -107,7 +107,7 @@ export const ConnectionPooling = () => {
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
pool_mode: poolingConfiguration?.pool_mode as 'transaction' | 'session' | 'statement',
pool_mode: poolingConfiguration?.pool_mode as 'transaction' | 'session',
default_pool_size: poolingConfiguration?.default_pool_size as number | undefined,
max_client_conn: poolingConfiguration?.max_client_conn as number | undefined,
},
@@ -141,7 +141,7 @@ export const ConnectionPooling = () => {
useEffect(() => {
if (isSuccess) {
form.reset({
pool_mode: poolingConfiguration?.pool_mode as 'transaction' | 'session' | 'statement',
pool_mode: poolingConfiguration?.pool_mode as 'transaction' | 'session',
default_pool_size: poolingConfiguration?.default_pool_size as number | undefined,
max_client_conn: poolingConfiguration?.max_client_conn as number | undefined,
})
@@ -0,0 +1,82 @@
import { ExternalLink } from 'lucide-react'
import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Button } from 'ui'
import { WarningIcon } from 'ui-patterns/Icons/StatusIcons'
export const IPv4DeprecationNotice = () => {
return (
<Alert_Shadcn_ variant="warning">
<WarningIcon />
<AlertTitle_Shadcn_>
Direct database access is only available via IPv6-compatible networks.
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_ className="space-y-3">
<p>
We strongly recommend using connection pooling to connect to your database because it's
compatible with both IPv4 and IPv6 networks. You'll only need to change the connection
credentials that you're using in your application to the pooler's connection credentials.
</p>
<Button asChild type="default" icon={<ExternalLink />}>
<a
href="https://github.com/orgs/supabase/discussions/17817"
target="_blank"
rel="noreferrer"
>
Learn more
</a>
</Button>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)
}
export const IPv4AddonDirectConnectionNotice = () => {
return (
<Alert_Shadcn_ variant="default">
<AlertTitle_Shadcn_>
Direct database connections are recommended if you're connecting with session mode.
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_ className="space-y-3">
<p>
If you are not connecting to the database from serverless environments, we strongly
recommend you connect to the database directly instead of using connection pooling for
lower latency.
</p>
<p>
Since your project has the IPv4 add on, you are not required to use the connection pooler
even if your network only supports IPv4.
</p>
<Button asChild type="default" icon={<ExternalLink />}>
<a
href="https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pooler"
target="_blank"
rel="noreferrer"
>
Learn more
</a>
</Button>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)
}
export const DefaultSessionModeNotice = () => {
return (
<Alert_Shadcn_ variant="warning">
<WarningIcon />
<AlertTitle_Shadcn_>You are not yet in Transaction mode.</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_ className="space-y-3">
<p>
You need to set your <span className="text-foreground">Pool Mode</span> to{' '}
<span className="text-foreground">Transaction</span> in the{' '}
<span className="text-foreground">Connection pooling configuration</span> section below in
order to use transaction mode on port 6543.
</p>
<p>
Otherwise, port 6543 will continue to connect in{' '}
<span className="text-foreground">Session</span> mode.
</p>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)
}
@@ -2,6 +2,7 @@ import { useParams, useTelemetryProps } from 'common'
import { useRouter } from 'next/router'
import { useEffect, useRef, useState } from 'react'
import { getAddons } from 'components/interfaces/Billing/Subscription/Subscription.utils'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import AlertError from 'components/ui/AlertError'
import DatabaseSelector from 'components/ui/DatabaseSelector'
@@ -9,6 +10,7 @@ import Panel from 'components/ui/Panel'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { usePoolingConfigurationQuery } from 'data/database/pooling-configuration-query'
import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
import { useSelectedProject } from 'hooks'
import { pluckObjectFields } from 'lib/helpers'
import Telemetry from 'lib/telemetry'
@@ -28,7 +30,11 @@ import {
Tooltip_Shadcn_,
cn,
} from 'ui'
import { IPv4DeprecationNotice } from '../IPv4DeprecationNotice'
import {
DefaultSessionModeNotice,
IPv4AddonDirectConnectionNotice,
IPv4DeprecationNotice,
} from '../DatabaseConnectionNotices'
import { UsePoolerCheckbox } from '../UsePoolerCheckbox'
import {
constructConnStringSyntax,
@@ -61,7 +67,7 @@ export const DatabaseConnectionString = ({ appearance }: DatabaseConnectionStrin
const { project: projectDetails, isLoading: isProjectLoading } = useProjectContext()
const connectionStringsRef = useRef<HTMLDivElement>(null)
const [poolingMode, setPoolingMode] = useState<'transaction' | 'session' | 'statement'>('session')
const [poolingMode, setPoolingMode] = useState<'transaction' | 'session'>('transaction')
const [selectedTab, setSelectedTab] = useState<
'uri' | 'psql' | 'golang' | 'jdbc' | 'dotnet' | 'nodejs' | 'php' | 'python'
>('uri')
@@ -70,6 +76,7 @@ export const DatabaseConnectionString = ({ appearance }: DatabaseConnectionStrin
projectRef,
})
const poolingConfiguration = poolingInfo?.find((x) => x.identifier === state.selectedDatabaseId)
const defaultPoolingMode = poolingConfiguration?.pool_mode
const {
data: databases,
@@ -83,6 +90,9 @@ export const DatabaseConnectionString = ({ appearance }: DatabaseConnectionStrin
(db) => db.identifier === state.selectedDatabaseId
)
const { data: addons, isSuccess: isSuccessAddons } = useProjectAddonsQuery({ projectRef })
const { ipv4: ipv4Addon } = getAddons(addons?.selected_addons ?? [])
const DB_FIELDS = ['db_host', 'db_name', 'db_port', 'db_user', 'inserted_at']
const emptyState = { db_user: '', db_host: '', db_port: '', db_name: '' }
const connectionInfo = pluckObjectFields(selectedDatabase || emptyState, DB_FIELDS)
@@ -218,7 +228,14 @@ export const DatabaseConnectionString = ({ appearance }: DatabaseConnectionStrin
onCheckedChange={snap.setUsePoolerConnection}
onSelectPoolingMode={setPoolingMode}
/>
{!snap.usePoolerConnection && <IPv4DeprecationNotice />}
{defaultPoolingMode === 'session' && poolingMode === 'transaction' && (
<DefaultSessionModeNotice />
)}
{isSuccessAddons &&
poolingMode === 'session' &&
ipv4Addon !== undefined &&
snap.usePoolerConnection && <IPv4AddonDirectConnectionNotice />}
{ipv4Addon === undefined && !snap.usePoolerConnection && <IPv4DeprecationNotice />}
<Input
copy
readOnly
@@ -2,19 +2,25 @@ import { useRouter } from 'next/router'
import { useEffect, useRef, useState } from 'react'
import { useParams, useTelemetryProps } from 'common'
import { getAddons } from 'components/interfaces/Billing/Subscription/Subscription.utils'
import AlertError from 'components/ui/AlertError'
import DatabaseSelector from 'components/ui/DatabaseSelector'
import Panel from 'components/ui/Panel'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { usePoolingConfigurationQuery } from 'data/database/pooling-configuration-query'
import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
import { pluckObjectFields } from 'lib/helpers'
import Telemetry from 'lib/telemetry'
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
import { useDatabaseSettingsStateSnapshot } from 'state/database-settings'
import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Input } from 'ui'
import { WarningIcon } from 'ui-patterns/Icons/StatusIcons'
import { IPv4DeprecationNotice } from '../IPv4DeprecationNotice'
import {
DefaultSessionModeNotice,
IPv4AddonDirectConnectionNotice,
IPv4DeprecationNotice,
} from '../DatabaseConnectionNotices'
import { UsePoolerCheckbox } from '../UsePoolerCheckbox'
import ResetDbPassword from './ResetDbPassword'
@@ -26,7 +32,7 @@ const DatabaseSettings = () => {
const state = useDatabaseSelectorStateSnapshot()
const connectionStringsRef = useRef<HTMLDivElement>(null)
const [poolingMode, setPoolingMode] = useState<'transaction' | 'session' | 'statement'>('session')
const [poolingMode, setPoolingMode] = useState<'transaction' | 'session'>('transaction')
const {
data: poolingInfo,
@@ -44,15 +50,24 @@ const DatabaseSettings = () => {
isError: isErrorReadReplicas,
isSuccess: isSuccessReadReplicas,
} = useReadReplicasQuery({ projectRef })
const error = readReplicasError || poolingInfoError
const isLoading = isLoadingReadReplicas || isLoadingPoolingInfo
const isError = isErrorReadReplicas || isErrorPoolingInfo
const isSuccess = isSuccessReadReplicas && isSuccessPoolingInfo
const {
data: addons,
error: addOnsError,
isLoading: isLoadingAddons,
isError: isErrorAddons,
isSuccess: isSuccessAddons,
} = useProjectAddonsQuery({ projectRef })
const error = readReplicasError || poolingInfoError || addOnsError
const isLoading = isLoadingReadReplicas || isLoadingPoolingInfo || isLoadingAddons
const isError = isErrorReadReplicas || isErrorPoolingInfo || isErrorAddons
const isSuccess = isSuccessReadReplicas && isSuccessPoolingInfo && isSuccessAddons
const selectedDatabase = (databases ?? []).find(
(db) => db.identifier === state.selectedDatabaseId
)
const primaryConfig = poolingInfo?.find((x) => x.identifier === state.selectedDatabaseId)
const defaultPoolingMode = primaryConfig?.pool_mode
const { ipv4: ipv4Addon } = getAddons(addons?.selected_addons ?? [])
const isMd5 = primaryConfig?.connectionString.includes('?options=reference')
const DB_FIELDS = ['db_host', 'db_name', 'db_port', 'db_user']
@@ -128,7 +143,15 @@ const DatabaseSettings = () => {
onCheckedChange={snap.setUsePoolerConnection}
onSelectPoolingMode={setPoolingMode}
/>
{!snap.usePoolerConnection && <IPv4DeprecationNotice />}
{defaultPoolingMode === 'session' && poolingMode === 'transaction' && (
<DefaultSessionModeNotice />
)}
{ipv4Addon !== undefined &&
poolingMode === 'session' &&
snap.usePoolerConnection && <IPv4AddonDirectConnectionNotice />}
{ipv4Addon === undefined && !snap.usePoolerConnection && (
<IPv4DeprecationNotice />
)}
{isMd5 && (
<Alert_Shadcn_>
<WarningIcon />
@@ -1,39 +0,0 @@
import {
Alert_Shadcn_,
IconAlertTriangle,
AlertTitle_Shadcn_,
AlertDescription_Shadcn_,
Button,
IconExternalLink,
} from 'ui'
export const IPv4DeprecationNotice = () => {
return (
<Alert_Shadcn_ variant="warning">
<IconAlertTriangle strokeWidth={2} />
<AlertTitle_Shadcn_>
Direct database access is only available via IPv6-compatible networks or if your project has
the IPv4 add on.
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_ className="space-y-3">
<p>
We strongly recommend using <span className="text-foreground">connection pooling</span> to
connect to your database because it's compatible with both IPv4 and IPv6 networks.
</p>
<p>
You'll only need to change the connection credentials that you're using in your
application to the pooler's connection credentials.
</p>
<Button asChild type="default" icon={<IconExternalLink strokeWidth={1.5} />}>
<a
href="https://github.com/orgs/supabase/discussions/17817"
target="_blank"
rel="noreferrer"
>
Learn more
</a>
</Button>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)
}
@@ -30,7 +30,7 @@ import {
interface UsePoolerCheckboxInterface {
id: string
checked: boolean
poolingMode: 'transaction' | 'session' | 'statement'
poolingMode: 'transaction' | 'session'
onCheckedChange: (value: boolean) => void
onSelectPoolingMode: (mode: 'transaction' | 'session') => void
}
@@ -21,6 +21,7 @@ export async function getDatabaseFunctions(
if (connectionString) headers.set('x-connection-encrypted', connectionString)
const { data, error } = await get('/platform/pg-meta/{ref}/functions', {
// @ts-ignore [Joshen] Temp, seems like API codegen is wrong
params: { path: { ref: projectRef } },
headers,
signal,
@@ -28,12 +28,10 @@ const handleGet = async (req: NextApiRequest, res: NextApiResponse<ResponseData>
usage_billing_enabled: false,
plan: {
id: 'enterprise',
name: '',
price: 0,
name: 'Enterprise',
},
addons: [],
project_addons: [],
usage_fees: [],
payment_method_type: '',
billing_via_partner: false,
billing_partner: 'fly',
+259 -679
View File
File diff suppressed because it is too large. Load diff