fix: Use spawn_blocking for Redis cache connection to prevent freezing
All checks were successful
BotServer CI / build (push) Successful in 8m38s

The init_redis() function was using synchronous blocking calls
(redis::Client::get_connection()) inside an async function, which
blocked the entire tokio runtime and caused botserver to freeze.

Changes:
- Wrap Redis connection calls in tokio::task::spawn_blocking()
- Runs blocking operations in separate thread pool
- Prevents tokio runtime from freezing during cache connection

This fixes the issue where botserver would hang indefinitely
when connecting to Valkey/Redis cache.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Rodrigo Rodriguez 2026-02-15 12:21:33 +00:00
parent fc34461b2f
commit cf7bd7ffa2

View file

@ -289,54 +289,63 @@ pub async fn init_redis() -> Option<Arc<redis::Client>> {
loop { loop {
attempt += 1; attempt += 1;
match redis::Client::open(cache_url.as_str()) { // Use spawn_blocking to avoid freezing the tokio runtime
Ok(client) => { let cache_url_clone = cache_url.clone();
// Verify the connection actually works let result = tokio::task::spawn_blocking(move || {
match client.get_connection() { match redis::Client::open(cache_url_clone.as_str()) {
Ok(mut conn) => { Ok(client) => {
// Test with PING // Verify the connection actually works
match redis::cmd("PING").query::<String>(&mut conn) { match client.get_connection() {
Ok(response) if response == "PONG" => { Ok(mut conn) => {
info!("Cache connection verified: PONG"); // Test with PING
return Some(Arc::new(client)); match redis::cmd("PING").query::<String>(&mut conn) {
} Ok(response) if response == "PONG" => {
Ok(response) => { log::info!("Cache connection verified: PONG");
warn!("Cache PING returned unexpected response: {}", response); Ok(Some(Arc::new(client)))
return Some(Arc::new(client)); }
} Ok(response) => {
Err(e) => { log::warn!("Cache PING returned unexpected response: {}", response);
if attempt < max_attempts { Ok(Some(Arc::new(client)))
info!("Cache PING failed (attempt {}/{}): {}. Retrying in 5s...", attempt, max_attempts, e); }
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; Err(e) => {
continue; Err(format!("Cache PING failed: {}", e))
} else {
warn!("Cache PING failed after {} attempts: {}. Cache functions will be disabled.", max_attempts, e);
warn!("Suggestions and other cache-dependent features will not work.");
return None;
} }
} }
} }
} Err(e) => {
Err(e) => { Err(format!("Failed to establish cache connection: {}", e))
if attempt < max_attempts {
info!("Failed to establish cache connection (attempt {}/{}): {}. Retrying in 5s...", attempt, max_attempts, e);
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
continue;
} else {
warn!("Failed to establish cache connection after {} attempts: {}. Cache functions will be disabled.", max_attempts, e);
warn!("Suggestions and other cache-dependent features will not work.");
return None;
} }
} }
} }
Err(e) => {
Err(format!("Failed to create cache client: {}", e))
}
} }
Err(e) => { })
.await;
match result {
Ok(Ok(Some(client))) => return Some(client),
Ok(Ok(None)) => return None,
Ok(Err(e)) => {
if attempt < max_attempts { if attempt < max_attempts {
info!("Failed to create cache client (attempt {}/{}): {}. Retrying in 5s...", attempt, max_attempts, e); info!("Cache connection attempt {}/{} failed: {}. Retrying in 5s...", attempt, max_attempts, e);
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
continue; continue;
} else { } else {
log::warn!("Failed to create cache client after {} attempts: {}. Cache functions will be disabled.", max_attempts, e); warn!("Cache connection failed after {} attempts: {}. Cache functions will be disabled.", max_attempts, e);
warn!("Suggestions and other cache-dependent features will not work.");
return None;
}
}
Err(e) => {
// spawn_blocking itself failed
if attempt < max_attempts {
info!("Cache connection attempt {}/{} failed with task error: {}. Retrying in 5s...", attempt, max_attempts, e);
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
continue;
} else {
warn!("Cache connection failed after {} attempts with task error: {}. Cache functions will be disabled.", max_attempts, e);
return None; return None;
} }
} }