@@ -6,11 +6,13 @@ import { getAddons } from 'components/interfaces/Billing/Subscription/Subscripti
66import AlertError from 'components/ui/AlertError'
77import DatabaseSelector from 'components/ui/DatabaseSelector'
88import ShimmeringLoader from 'components/ui/ShimmeringLoader'
9- import { usePoolingConfigurationQuery } from 'data/database/pooling-configuration-query'
9+ import { usePgbouncerConfigQuery } from 'data/database/pgbouncer-config-query'
10+ import { useSupavisorConfigurationQuery } from 'data/database/supavisor-configuration-query'
1011import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
1112import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
1213import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
1314import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
15+ import { useFlag } from 'hooks/ui/useFlag'
1416import { pluckObjectFields } from 'lib/helpers'
1517import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
1618import {
@@ -32,6 +34,8 @@ import {
3234 CONNECTION_PARAMETERS ,
3335 DATABASE_CONNECTION_TYPES ,
3436 DatabaseConnectionType ,
37+ IPV4_ADDON_TEXT ,
38+ PGBOUNCER_ENABLED_BUT_NO_IPV4_ADDON_TEXT ,
3539} from './Connect.constants'
3640import { CodeBlockFileHeader , ConnectionPanel } from './ConnectionPanel'
3741import { getConnectionStrings } from './DatabaseSettings.utils'
@@ -54,19 +58,28 @@ export const DatabaseConnectionString = () => {
5458 const { ref : projectRef } = useParams ( )
5559 const org = useSelectedOrganization ( )
5660 const state = useDatabaseSelectorStateSnapshot ( )
61+ const allowPgBouncerSelection = useFlag ( 'dualPoolerSupport' )
5762
5863 const [ selectedTab , setSelectedTab ] = useState < DatabaseConnectionType > ( 'uri' )
5964
6065 const {
61- data : poolingInfo ,
62- error : poolingInfoError ,
63- isLoading : isLoadingPoolingInfo ,
64- isError : isErrorPoolingInfo ,
65- isSuccess : isSuccessPoolingInfo ,
66- } = usePoolingConfigurationQuery ( {
67- projectRef,
68- } )
69- const poolingConfiguration = poolingInfo ?. find ( ( x ) => x . identifier === state . selectedDatabaseId )
66+ data : pgbouncerConfig ,
67+ error : pgbouncerError ,
68+ isLoading : isLoadingPgbouncerConfig ,
69+ isError : isErrorPgbouncerConfig ,
70+ isSuccess : isSuccessPgBouncerConfig ,
71+ } = usePgbouncerConfigQuery ( { projectRef } , { enabled : allowPgBouncerSelection } )
72+ const {
73+ data : supavisorConfig ,
74+ error : supavisorConfigError ,
75+ isLoading : isLoadingSupavisorConfig ,
76+ isError : isErrorSupavisorConfig ,
77+ isSuccess : isSuccessSupavisorConfig ,
78+ } = useSupavisorConfigurationQuery ( { projectRef } )
79+ const isPgBouncerEnabled = allowPgBouncerSelection && ! ! pgbouncerConfig ?. pgbouncer_enabled
80+ const poolingConfiguration = isPgBouncerEnabled
81+ ? pgbouncerConfig
82+ : supavisorConfig ?. find ( ( x ) => x . identifier === state . selectedDatabaseId )
7083
7184 const {
7285 data : databases ,
@@ -76,10 +89,19 @@ export const DatabaseConnectionString = () => {
7689 isSuccess : isSuccessReadReplicas ,
7790 } = useReadReplicasQuery ( { projectRef } )
7891
79- const error = poolingInfoError || readReplicasError
80- const isLoading = isLoadingPoolingInfo || isLoadingReadReplicas
81- const isError = isErrorPoolingInfo || isErrorReadReplicas
82- const isSuccess = isSuccessPoolingInfo && isSuccessReadReplicas
92+ const poolerError = isPgBouncerEnabled ? pgbouncerError : supavisorConfigError
93+ const isLoadingPoolerConfig = isPgBouncerEnabled
94+ ? isLoadingPgbouncerConfig
95+ : isLoadingSupavisorConfig
96+ const isErrorPoolerConfig = isPgBouncerEnabled ? isErrorPgbouncerConfig : isErrorSupavisorConfig
97+ const isSuccessPoolerConfig = isPgBouncerEnabled
98+ ? isSuccessPgBouncerConfig
99+ : isSuccessSupavisorConfig
100+
101+ const error = poolerError || readReplicasError
102+ const isLoading = isLoadingPoolerConfig || isLoadingReadReplicas
103+ const isError = isErrorPoolerConfig || isErrorReadReplicas
104+ const isSuccess = isSuccessPoolerConfig && isSuccessReadReplicas
83105
84106 const selectedDatabase = ( databases ?? [ ] ) . find (
85107 ( db ) => db . identifier === state . selectedDatabaseId
@@ -109,9 +131,17 @@ export const DatabaseConnectionString = () => {
109131 }
110132
111133 const connectionStrings =
112- isSuccessPoolingInfo && poolingConfiguration !== undefined
113- ? getConnectionStrings ( connectionInfo , poolingConfiguration , {
114- projectRef,
134+ isSuccessSupavisorConfig && poolingConfiguration !== undefined
135+ ? getConnectionStrings ( {
136+ connectionInfo,
137+ poolingInfo : {
138+ connectionString : poolingConfiguration . connectionString ,
139+ db_host : poolingConfiguration . db_host ,
140+ db_name : poolingConfiguration . db_name ,
141+ db_port : poolingConfiguration . db_port ,
142+ db_user : poolingConfiguration . db_user ,
143+ } ,
144+ metadata : { projectRef } ,
115145 } )
116146 : {
117147 direct : {
@@ -173,6 +203,22 @@ export const DatabaseConnectionString = () => {
173203 // [Refactor] See if we can do this in an immutable way, technically not a good practice to do this
174204 let stepNumber = 0
175205
206+ const ipv4AddOnUrl = {
207+ text : 'IPv4 add-on' ,
208+ url : `/project/${ projectRef } /settings/addons?panel=ipv4` ,
209+ }
210+ const ipv4SettingsUrl = {
211+ text : 'IPv4 settings' ,
212+ url : `/project/${ projectRef } /settings/addons?panel=ipv4` ,
213+ }
214+ const poolerSettingsUrl = {
215+ text : 'Pooler settings' ,
216+ url : `/project/${ projectRef } /settings/database#connection-pooling` ,
217+ }
218+ const buttonLinks = ! ipv4Addon
219+ ? [ ipv4AddOnUrl , ...( isPgBouncerEnabled ? [ poolerSettingsUrl ] : [ ] ) ]
220+ : [ ipv4SettingsUrl , ...( isPgBouncerEnabled ? [ poolerSettingsUrl ] : [ ] ) ]
221+
176222 return (
177223 < div className = "flex flex-col" >
178224 < div
@@ -279,16 +325,13 @@ export const DatabaseConnectionString = () => {
279325 ipv4Status = { {
280326 type : ! ipv4Addon ? 'error' : 'success' ,
281327 title : ! ipv4Addon ? 'Not IPv4 compatible' : 'IPv4 compatible' ,
282- description : ipv4Addon && 'Connections are IPv4 proxied with IPv4 addon.' ,
283- link : ! ipv4Addon
284- ? {
285- text : 'IPv4 addon' ,
286- url : `/project/${ projectRef } /settings/addons?panel=ipv4` ,
287- }
288- : {
289- text : 'IPv4 settings' ,
290- url : `/project/${ projectRef } /settings/addons?panel=ipv4` ,
291- } ,
328+ description :
329+ isPgBouncerEnabled && ! ipv4Addon
330+ ? PGBOUNCER_ENABLED_BUT_NO_IPV4_ADDON_TEXT
331+ : ! isPgBouncerEnabled
332+ ? 'Use Session Pooler if on a IPv4 network or purchase IPv4 add-on'
333+ : IPV4_ADDON_TEXT ,
334+ links : buttonLinks ,
292335 } }
293336 parameters = { [
294337 { ...CONNECTION_PARAMETERS . host , value : connectionInfo . db_host } ,
@@ -303,13 +346,20 @@ export const DatabaseConnectionString = () => {
303346 lang = { lang }
304347 type = "transaction"
305348 title = "Transaction pooler"
349+ badge = { isPgBouncerEnabled ? 'Dedicated Pooler' : 'Supavisor' }
306350 fileTitle = { fileTitle }
307351 description = "Ideal for stateless applications like serverless functions where each interaction with Postgres is brief and isolated."
308352 connectionString = { connectionStrings [ 'pooler' ] [ selectedTab ] }
309353 ipv4Status = { {
310- type : 'success' ,
311- title : 'IPv4 compatible' ,
312- description : 'Transaction pooler connections are IPv4 proxied for free.' ,
354+ type : isPgBouncerEnabled && ! ipv4Addon ? 'error' : 'success' ,
355+ title : isPgBouncerEnabled && ! ipv4Addon ? 'IPv4 incompatible' : 'IPv4 compatible' ,
356+ description :
357+ isPgBouncerEnabled && ! ipv4Addon
358+ ? PGBOUNCER_ENABLED_BUT_NO_IPV4_ADDON_TEXT
359+ : ! isPgBouncerEnabled
360+ ? 'Transaction pooler connections are IPv4 proxied for free.'
361+ : IPV4_ADDON_TEXT ,
362+ links : isPgBouncerEnabled ? buttonLinks : undefined ,
313363 } }
314364 notice = { [ 'Does not support PREPARE statements' ] }
315365 parameters = { [
@@ -341,13 +391,24 @@ export const DatabaseConnectionString = () => {
341391 lang = { lang }
342392 type = "session"
343393 title = "Session pooler"
394+ badge = { isPgBouncerEnabled ? 'Dedicated Pooler' : 'Supavisor' }
344395 fileTitle = { fileTitle }
345- description = "Only recommended as an alternative to Direct Connection, when connecting via an IPv4 network."
396+ description = {
397+ isPgBouncerEnabled
398+ ? 'Recommended if you need to use prepared statements, or other features that are only available in Session mode.'
399+ : 'Only recommended as an alternative to Direct Connection, when connecting via an IPv4 network.'
400+ }
346401 connectionString = { connectionStrings [ 'pooler' ] [ selectedTab ] . replace ( '6543' , '5432' ) }
347402 ipv4Status = { {
348- type : 'success' ,
349- title : 'IPv4 compatible' ,
350- description : 'Session pooler connections are IPv4 proxied for free.' ,
403+ type : isPgBouncerEnabled && ! ipv4Addon ? 'error' : 'success' ,
404+ title : isPgBouncerEnabled && ! ipv4Addon ? 'IPv4 incompatible' : 'IPv4 compatible' ,
405+ description :
406+ isPgBouncerEnabled && ! ipv4Addon
407+ ? PGBOUNCER_ENABLED_BUT_NO_IPV4_ADDON_TEXT
408+ : ! isPgBouncerEnabled
409+ ? 'Session pooler connections are IPv4 proxied for free'
410+ : IPV4_ADDON_TEXT ,
411+ links : isPgBouncerEnabled ? buttonLinks : undefined ,
351412 } }
352413 parameters = { [
353414 { ...CONNECTION_PARAMETERS . host , value : poolingConfiguration ?. db_host ?? '' } ,
0 commit comments