74 lines
2.7 KiB
Rust
74 lines
2.7 KiB
Rust
//! Redis connection utility with tracing for connection lifecycle and errors.
|
|||
|
|
//!
|
||
|
|
//! Uses the type-safe CONFIG for Redis connection parameters.
|
||
|
|
|
||
|
|
use crate::shared::config::CONFIG;
|
||
|
|
use crate::shared::errors::AppError;
|
||
|
|
use deadpool_redis::{Manager, Pool};
|
||
|
|
use once_cell::sync::Lazy;
|
||
|
|
use tracing::{debug, error, info};
|
||
|
|
|
||
|
|
static REDIS_POOL_INIT: Lazy<Result<Pool, String>> = Lazy::new(|| {
|
||
|
|
let redis_url = if !CONFIG.redis_url.is_empty() {
|
||
|
|
CONFIG.redis_url.clone()
|
||
|
|
} else {
|
||
|
|
let host = std::env::var("REDIS_HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
|
||
|
|
let port = std::env::var("REDIS_PORT").unwrap_or_else(|_| "6379".to_string());
|
||
|
|
let password = std::env::var("REDIS_PASSWORD").unwrap_or_default();
|
||
|
|
|
||
|
|
if password.is_empty() {
|
||
|
|
format!("redis://{}:{}", host, port)
|
||
|
|
} else {
|
||
|
|
format!("redis://:{}@{}:{}", password, host, port)
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
info!("Initializing Redis connection pool for URL: {}", redis_url);
|
||
|
|
|
||
|
|
Manager::new(redis_url)
|
||
|
|
.map_err(|e| format!("Failed to create Redis manager: {}", e))
|
||
|
|
.and_then(|manager| {
|
||
|
|
Pool::builder(manager)
|
||
|
|
.max_size(100)
|
||
|
|
.wait_timeout(Some(std::time::Duration::from_millis(200)))
|
||
|
|
.runtime(deadpool_redis::Runtime::Tokio1)
|
||
|
|
.build()
|
||
|
|
.map_err(|e| format!("Failed to create Redis connection pool: {}", e))
|
||
|
|
})
|
||
|
|
});
|
||
|
|
|
||
|
|
/// Get the Redis connection pool (for internal use).
|
||
|
|
pub fn get_redis_pool() -> Result<&'static Pool, String> {
|
||
|
|
REDIS_POOL_INIT.as_ref().map_err(|e| e.clone())
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Get a cloned reference to the Redis pool.
|
||
|
|
pub fn redis_pool() -> Result<Pool, String> {
|
||
|
|
get_redis_pool().map(|p| (*p).clone())
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Get an async connection from the pool with retry backoff.
|
||
|
|
pub async fn get_redis_conn() -> Result<deadpool_redis::Connection, AppError> {
|
||
|
|
let pool = get_redis_pool().map_err(|e| AppError::Other(e))?;
|
||
|
|
let mut retries = 5;
|
||
|
|
let mut wait = std::time::Duration::from_millis(100);
|
||
|
|
|
||
|
|
loop {
|
||
|
|
match pool.get().await {
|
||
|
|
Ok(conn) => {
|
||
|
|
debug!("Successfully retrieved Redis connection from pool.");
|
||
|
|
return Ok(conn);
|
||
|
|
}
|
||
|
|
Err(e) => {
|
||
|
|
if retries <= 0 {
|
||
|
|
error!("Failed to get Redis connection after retries: {:?}", e);
|
||
|
|
return Err(AppError::from(e));
|
||
|
|
}
|
||
|
|
debug!("Redis connection failed, retrying in {:?}: {:?}", wait, e);
|
||
|
|
tokio::time::sleep(wait).await;
|
||
|
|
wait = std::cmp::min(wait * 2, std::time::Duration::from_secs(5));
|
||
|
|
retries -= 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|