use crate::config::AppConfig; use crate::error::AppError; use crate::error::AppResult; use r2d2::Pool; use redis::cluster::ClusterClient; use redis::{Client, ConnectionLike, RedisError}; use std::time::Duration; #[derive(Clone)] enum RedisBackend { Single(Pool), Cluster(Pool), } #[derive(Clone)] pub struct AppRedis { backend: RedisBackend, } impl AppRedis { pub fn from_config(config: &AppConfig) -> AppResult { let backend = if config.redis_cluster_enabled()? { let nodes = config.redis_cluster_nodes()?; let cluster_client = ClusterClient::new(nodes.iter().map(|s| s.as_str()).collect::>())?; let pool = Self::build_pool(config, cluster_client)?; RedisBackend::Cluster(pool) } else { let url = config .redis_url()? .ok_or_else(|| AppError::Config("APP_REDIS_URL is not set".into()))?; let client = Client::open(url.as_str())?; let pool = Self::build_pool(config, client)?; RedisBackend::Single(pool) }; Ok(Self { backend }) } fn build_pool(config: &AppConfig, manager: M) -> AppResult> { let max_conn = config.redis_max_connections()?; let min_conn = config.redis_min_connections()?; let idle_timeout = config.redis_idle_timeout()?; let conn_timeout = config.redis_connection_timeout()?; Ok(r2d2::Builder::new() .max_size(max_conn) .min_idle(Some(min_conn)) .idle_timeout(Some(Duration::from_secs(idle_timeout))) .connection_timeout(Duration::from_secs(conn_timeout)) .build(manager)?) } pub fn get_connection(&self) -> Result { match &self.backend { RedisBackend::Single(pool) => pool.get().map(PooledRedisConnection::Single), RedisBackend::Cluster(pool) => pool.get().map(PooledRedisConnection::Cluster), } } } #[allow(clippy::large_enum_variant)] pub enum PooledRedisConnection { Single(r2d2::PooledConnection), Cluster(r2d2::PooledConnection), } impl PooledRedisConnection { pub fn inner_mut(&mut self) -> &mut dyn ConnectionLike { match self { PooledRedisConnection::Single(conn) => conn, PooledRedisConnection::Cluster(conn) => conn, } } } impl ConnectionLike for PooledRedisConnection { fn req_packed_command(&mut self, cmd: &[u8]) -> Result { match self { PooledRedisConnection::Single(conn) => conn.req_packed_command(cmd), PooledRedisConnection::Cluster(conn) => conn.req_packed_command(cmd), } } fn req_packed_commands( &mut self, cmd: &[u8], offset: usize, count: usize, ) -> Result, RedisError> { match self { PooledRedisConnection::Single(conn) => conn.req_packed_commands(cmd, offset, count), PooledRedisConnection::Cluster(conn) => conn.req_packed_commands(cmd, offset, count), } } fn get_db(&self) -> i64 { match self { PooledRedisConnection::Single(conn) => conn.get_db(), PooledRedisConnection::Cluster(conn) => conn.get_db(), } } fn check_connection(&mut self) -> bool { match self { PooledRedisConnection::Single(conn) => conn.check_connection(), PooledRedisConnection::Cluster(conn) => conn.check_connection(), } } fn is_open(&self) -> bool { match self { PooledRedisConnection::Single(conn) => conn.is_open(), PooledRedisConnection::Cluster(conn) => conn.is_open(), } } }