From e5593840043244fe04d4c6495d8607bc5300d841 Mon Sep 17 00:00:00 2001 From: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> Date: Thu, 23 Jul 2026 07:12:36 -0700 Subject: [PATCH] feat(relay): make Redis pool size configurable, default 16 deadpool-redis defaults its pool max to CPU_COUNT * 2, which on a 2-vCPU relay pod is only 4 connections shared by rate limiting, presence, pub/sub publishes, and cache invalidation. bb-public pods started showing pool waiters (buzz_redis_pool_waiting > 0, buzz_redis_pool_max = 4) at ~190 events/s. Raise the default to 16 and expose BUZZ_REDIS_POOL_SIZE for per-deploy tuning. Zero or unparsable values fall back to the default. Co-authored-by: Tyler Longwell Signed-off-by: Tyler Longwell --- .env.example | 2 ++ crates/buzz-relay/src/config.rs | 39 +++++++++++++++++++++++++++++++++ crates/buzz-relay/src/main.rs | 3 ++- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 1e69c6a0e3..db5a7ea25c 100644 --- a/.env.example +++ b/.env.example @@ -31,6 +31,8 @@ PGDATABASE=buzz # Redis 7 # ----------------------------------------------------------------------------- REDIS_URL=redis://localhost:6379 +# Max connections in the relay's shared Redis pool (default 16). +# BUZZ_REDIS_POOL_SIZE=16 # ----------------------------------------------------------------------------- # Typesense (search) diff --git a/crates/buzz-relay/src/config.rs b/crates/buzz-relay/src/config.rs index 75e70d6179..47030dcf3f 100644 --- a/crates/buzz-relay/src/config.rs +++ b/crates/buzz-relay/src/config.rs @@ -58,6 +58,12 @@ pub struct Config { pub read_database_url: Option, /// Redis connection URL used by the pub/sub manager. pub redis_url: String, + /// Maximum connections in the shared Redis pool. Defaults to 16. + /// + /// deadpool's own default is `CPU_COUNT * 2`, which on a 2-vCPU relay + /// pod is only 4 — small enough that rate-limit checks, presence, and + /// pub/sub publishes queue behind each other under load. + pub redis_pool_size: usize, /// Public WebSocket URL of this relay, advertised in NIP-11. pub relay_url: String, /// Public WebSocket URL of the dedicated device-pairing relay, when configured. @@ -412,6 +418,12 @@ impl Config { let redis_url = std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://localhost:6379".to_string()); + let redis_pool_size = std::env::var("BUZZ_REDIS_POOL_SIZE") + .ok() + .and_then(|v| v.parse::().ok()) + .filter(|&v| v > 0) + .unwrap_or(16); + let relay_url = std::env::var("RELAY_URL").unwrap_or_else(|_| "ws://localhost:3000".to_string()); @@ -862,6 +874,7 @@ impl Config { database_url, read_database_url, redis_url, + redis_pool_size, relay_url, pairing_relay_url, max_connections, @@ -928,6 +941,7 @@ mod tests { assert!(config.bind_addr.port() > 0); assert!(!config.database_url.is_empty()); assert!(!config.redis_url.is_empty()); + assert_eq!(config.redis_pool_size, 16); assert!(config.max_connections > 0); assert!(config.send_buffer_size > 0); assert_eq!(config.max_frame_bytes, DEFAULT_MAX_FRAME_BYTES); @@ -970,6 +984,31 @@ mod tests { ); } + #[test] + fn redis_pool_size_env_override_and_invalid_fallback() { + let _guard = ENV_MUTEX.lock().unwrap(); + let previous = std::env::var_os("BUZZ_REDIS_POOL_SIZE"); + + std::env::set_var("BUZZ_REDIS_POOL_SIZE", "32"); + let overridden = Config::from_env().expect("config").redis_pool_size; + + std::env::set_var("BUZZ_REDIS_POOL_SIZE", "0"); + let zero = Config::from_env().expect("config").redis_pool_size; + + std::env::set_var("BUZZ_REDIS_POOL_SIZE", "not-a-number"); + let junk = Config::from_env().expect("config").redis_pool_size; + + if let Some(value) = previous { + std::env::set_var("BUZZ_REDIS_POOL_SIZE", value); + } else { + std::env::remove_var("BUZZ_REDIS_POOL_SIZE"); + } + + assert_eq!(overridden, 32); + assert_eq!(zero, 16, "zero must fall back to the default"); + assert_eq!(junk, 16, "unparsable value must fall back to the default"); + } + #[test] fn read_database_url_unset_or_blank_is_none() { let _guard = ENV_MUTEX.lock().unwrap(); diff --git a/crates/buzz-relay/src/main.rs b/crates/buzz-relay/src/main.rs index 00ef7819cb..be9794922b 100644 --- a/crates/buzz-relay/src/main.rs +++ b/crates/buzz-relay/src/main.rs @@ -334,7 +334,8 @@ async fn main() -> anyhow::Result<()> { }; let redis_pool = { - let cfg = deadpool_redis::Config::from_url(&config.redis_url); + let mut cfg = deadpool_redis::Config::from_url(&config.redis_url); + cfg.pool = Some(deadpool_redis::PoolConfig::new(config.redis_pool_size)); cfg.create_pool(Some(deadpool_redis::Runtime::Tokio1)) .map_err(|e| anyhow::anyhow!("Redis pool creation failed: {e}"))? };