Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions crates/l30_storage/postgres/pool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -125,7 +127,7 @@ pub async fn connect_postgres_as(
password: &str,
) -> Result<PgPool, ConnectPostgresAsError> {
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)
Expand Down Expand Up @@ -180,7 +182,7 @@ pub fn connect_postgres_lazy_as_capped(
) -> Result<PgPool, DatabaseConnectOptionsError> {
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 {
Expand Down Expand Up @@ -227,11 +229,11 @@ pub fn connect_postgres_lazy_as(
password: &str,
) -> Result<PgPool, DatabaseConnectOptionsError> {
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();

Expand Down Expand Up @@ -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 {
Expand Down
31 changes: 30 additions & 1 deletion crates/l30_storage/postgres/pool/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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]
Expand Down
Loading