diff --git a/relay-config/src/config.rs b/relay-config/src/config.rs index e9a6864952..25216653f9 100644 --- a/relay-config/src/config.rs +++ b/relay-config/src/config.rs @@ -2285,12 +2285,20 @@ impl Config { let redis = self.values.processing.redis.as_ref()?; + let max_connections = redis + .options + .max_connections + .unwrap_or(cpu_concurrency as u32 * 2) + .max(crate::redis::DEFAULT_MIN_MAX_CONNECTIONS); + + let min_idle = redis + .options + .min_idle + .unwrap_or_else(|| max_connections.div_ceil(crate::redis::DEFAULT_MIN_IDLE_RATIO)); + let options = RedisConfigOptions { - max_connections: redis - .options - .max_connections - .unwrap_or(cpu_concurrency as u32 * 2) - .min(crate::redis::DEFAULT_MIN_MAX_CONNECTIONS), + max_connections, + min_idle: Some(min_idle), connection_timeout: redis.options.connection_timeout, max_lifetime: redis.options.max_lifetime, idle_timeout: redis.options.idle_timeout, diff --git a/relay-config/src/redis.rs b/relay-config/src/redis.rs index 437d1e5b15..fb25943fcf 100644 --- a/relay-config/src/redis.rs +++ b/relay-config/src/redis.rs @@ -4,6 +4,13 @@ use serde::{Deserialize, Serialize}; /// In this case, we fall back to the old default. pub(crate) const DEFAULT_MIN_MAX_CONNECTIONS: u32 = 24; +/// By default the `min_idle` count of the Redis pool is set to the calculated +/// amount of max connections divided by this value and rounded up. +/// +/// To express this value as a percentage of max connections, +/// use this formula: `100 / DEFAULT_MIN_IDLE_RATIO`. +pub(crate) const DEFAULT_MIN_IDLE_RATIO: u32 = 5; + /// Additional configuration options for a redis client. #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] #[serde(default)] @@ -13,6 +20,11 @@ pub struct PartialRedisConfigOptions { /// Defaults to 2x `limits.max_thread_count` or a minimum of 24. #[serde(skip_serializing_if = "Option::is_none")] pub max_connections: Option, + /// Minimum amount of idle connections kept alive in the pool. + /// + /// If not set it will default to 20% of [`Self::max_connections`]. + #[serde(skip_serializing_if = "Option::is_none")] + pub min_idle: Option, /// Sets the connection timeout used by the pool, in seconds. /// /// Calls to `Pool::get` will wait this long for a connection to become available before returning an error. @@ -31,6 +43,7 @@ impl Default for PartialRedisConfigOptions { fn default() -> Self { Self { max_connections: None, + min_idle: None, connection_timeout: 5, max_lifetime: 300, idle_timeout: 60, diff --git a/relay-redis/src/config.rs b/relay-redis/src/config.rs index 057319bc9e..b09184e8aa 100644 --- a/relay-redis/src/config.rs +++ b/relay-redis/src/config.rs @@ -5,6 +5,10 @@ use serde::{Deserialize, Serialize}; pub struct RedisConfigOptions { /// Maximum number of connections managed by the pool. pub max_connections: u32, + /// Minimum amount of idle connections kept alive in the pool. + /// + /// If not set it will default to [`Self::max_connections`]. + pub min_idle: Option, /// Sets the connection timeout used by the pool, in seconds. /// /// Calls to `Pool::get` will wait this long for a connection to become available before returning an error. @@ -23,6 +27,7 @@ impl Default for RedisConfigOptions { fn default() -> Self { Self { max_connections: 24, + min_idle: None, connection_timeout: 5, max_lifetime: 300, idle_timeout: 60, diff --git a/relay-redis/src/real.rs b/relay-redis/src/real.rs index 6b4f129940..922fed7d32 100644 --- a/relay-redis/src/real.rs +++ b/relay-redis/src/real.rs @@ -151,6 +151,7 @@ impl RedisPool { ) -> Result { let pool = Pool::builder() .max_size(opts.max_connections) + .min_idle(opts.min_idle) .test_on_check_out(false) .max_lifetime(Some(Duration::from_secs(opts.max_lifetime))) .idle_timeout(Some(Duration::from_secs(opts.idle_timeout))) @@ -166,6 +167,7 @@ impl RedisPool { pub fn single(server: &str, opts: RedisConfigOptions) -> Result { let pool = Pool::builder() .max_size(opts.max_connections) + .min_idle(opts.min_idle) .test_on_check_out(false) .max_lifetime(Some(Duration::from_secs(opts.max_lifetime))) .idle_timeout(Some(Duration::from_secs(opts.idle_timeout)))