From 077a8ca690aadb0ed01bce7a5d5a1cf203bcdd12 Mon Sep 17 00:00:00 2001 From: Shehan Date: Mon, 9 Nov 2026 00:12:22 +0000 Subject: [PATCH] Add POSTGRES_CREDS_POOL_MAX_CONNECTIONS for the space_creds pool Processes that open both pools sized each at POSTGRES_POOL_MAX_CONNECTIONS, so their connection ceiling was twice the general cap. Unset keeps today's behavior. --- crates/l30_storage/postgres/pool/src/lib.rs | 30 +++++++++++++----- crates/l30_storage/postgres/pool/src/tests.rs | 31 ++++++++++++++++++- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/crates/l30_storage/postgres/pool/src/lib.rs b/crates/l30_storage/postgres/pool/src/lib.rs index e9841c7781..b6e2abb377 100644 --- a/crates/l30_storage/postgres/pool/src/lib.rs +++ b/crates/l30_storage/postgres/pool/src/lib.rs @@ -110,6 +110,8 @@ fn service_connect_options( /// One generic pool knob per setting, set per-Deployment in Helm (env is /// per-container, so per-service env names add nothing): /// `POSTGRES_POOL_MAX_CONNECTIONS` (default 25), +/// `POSTGRES_CREDS_POOL_MAX_CONNECTIONS` (the `space_creds` pool's cap; +/// unset uses `POSTGRES_POOL_MAX_CONNECTIONS`), /// `POSTGRES_POOL_MIN_CONNECTIONS` (default 1), /// `POSTGRES_POOL_ACQUIRE_TIMEOUT_SECS` (default 5), /// `POSTGRES_POOL_IDLE_TIMEOUT_SECS` (default 300), @@ -125,7 +127,7 @@ pub async fn connect_postgres_as( password: &str, ) -> Result { let options = service_connect_options(database_url, application_name, identity, password)?; - postgres_pool_options() + postgres_pool_options(identity) .connect_with(options) .await .map_err(ConnectPostgresAsError::Connect) @@ -180,7 +182,7 @@ pub fn connect_postgres_lazy_as_capped( ) -> Result { let max_connections = max_connections.get(); let options = service_connect_options(database_url, application_name, identity, password)?; - let pool_options = postgres_pool_options() + let pool_options = postgres_pool_options(identity) .max_connections(max_connections) .min_connections(min_connections_from_env().min(max_connections)); let pool_options = match statement_timeout { @@ -227,11 +229,11 @@ pub fn connect_postgres_lazy_as( password: &str, ) -> Result { let options = service_connect_options(database_url, application_name, identity, password)?; - Ok(postgres_pool_options().connect_lazy_with(options)) + Ok(postgres_pool_options(identity).connect_lazy_with(options)) } -fn postgres_pool_options() -> PgPoolOptions { - let max_connections = max_connections_from_env(); +fn postgres_pool_options(identity: DatabaseIdentity) -> PgPoolOptions { + let max_connections = max_connections_from_env(identity); let max_lifetime = max_lifetime_from_env(); let max_lifetime_jitter_fraction = max_lifetime_jitter_fraction_from_env(); @@ -285,12 +287,24 @@ async fn warm_type_cache(conn: &mut sqlx::PgConnection) -> Result<(), sqlx::Erro Ok(()) } -fn max_connections_from_env() -> u32 { - u32::try_from(parse_positive_env_u64( +/// `POSTGRES_POOL_MAX_CONNECTIONS`, except that a `space_creds` pool reads +/// `POSTGRES_CREDS_POOL_MAX_CONNECTIONS` first. A process that opens both +/// pools (the API, the cluster controller) otherwise holds up to twice the +/// general cap, even where its `space_creds` traffic is a few admin calls. +fn max_connections_from_env(identity: DatabaseIdentity) -> u32 { + let general = u32::try_from(parse_positive_env_u64( "POSTGRES_POOL_MAX_CONNECTIONS", u64::from(DEFAULT_MAX_CONNECTIONS), )) - .unwrap_or(DEFAULT_MAX_CONNECTIONS) + .unwrap_or(DEFAULT_MAX_CONNECTIONS); + match identity { + DatabaseIdentity::SpaceUser => general, + DatabaseIdentity::SpaceCreds => u32::try_from(parse_positive_env_u64( + "POSTGRES_CREDS_POOL_MAX_CONNECTIONS", + u64::from(general), + )) + .unwrap_or(general), + } } fn min_connections_from_env() -> u32 { diff --git a/crates/l30_storage/postgres/pool/src/tests.rs b/crates/l30_storage/postgres/pool/src/tests.rs index 1715b70705..842292a4f8 100644 --- a/crates/l30_storage/postgres/pool/src/tests.rs +++ b/crates/l30_storage/postgres/pool/src/tests.rs @@ -105,7 +105,7 @@ fn lifetime_jitter_spreads_retirement_around_target() { #[test] fn pool_retires_connections_after_release_and_keeps_a_floor() { - let options = postgres_pool_options(); + let options = postgres_pool_options(DatabaseIdentity::SpaceUser); assert_eq!(options.get_max_lifetime(), None); assert!(options.get_min_connections() > 0); @@ -177,6 +177,35 @@ async fn a_capped_pool_ignores_the_service_wide_connection_env() { ); } +/// `POSTGRES_CREDS_POOL_MAX_CONNECTIONS` caps only the `space_creds` pool; +/// the `space_user` pool beside it keeps `POSTGRES_POOL_MAX_CONNECTIONS`. +#[tokio::test] +async fn the_creds_pool_cap_applies_only_to_space_creds() { + if !pplx_space_test_env::enter_test_process(&[ + ( + "POSTGRES_POOL_MAX_CONNECTIONS", + Some(std::ffi::OsStr::new("50")), + ), + ( + "POSTGRES_CREDS_POOL_MAX_CONNECTIONS", + Some(std::ffi::OsStr::new("4")), + ), + ("POSTGRES_POOL_MIN_CONNECTIONS", None), + ]) { + return; + } + let url = "postgres://space_admin:admin@127.0.0.1:1/space?sslmode=disable"; + let password = std::process::id().to_string(); + let max = |identity| { + connect_postgres_lazy_as(url, "creds-cap-test", identity, &password) + .unwrap() + .options() + .get_max_connections() + }; + assert_eq!(max(DatabaseIdentity::SpaceUser), 50); + assert_eq!(max(DatabaseIdentity::SpaceCreds), 4); +} + /// Pins the always-on query spans: SQLx keeps the statement level only in /// `PgConnectOptions`' private log settings, which `Debug` exposes. #[tokio::test]