darkwing/cache/connection.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
//! Redis cache connection management module.
//!
//! This module provides functionality for establishing and managing connections
//! to a Redis cache using connection pooling. It handles connection
//! initialization and pooling configuration.
use std::time::Duration;
use anyhow::{Context, Ok};
use r2d2::Pool;
use tracing::info;
/// Type alias for the Redis connection pool.
pub type ConnectionPool = Pool<redis::Client>;
/// Represents a Redis cache connection manager.
///
/// The `Cache` struct maintains a connection pool to Redis and provides methods
/// for establishing connections with configurable parameters.
#[derive(Debug, Clone)]
pub struct Cache {
/// The connection pool for Redis connections.
pub pool: ConnectionPool,
}
impl Cache {
/// Establishes a new connection to Redis with the specified configuration.
///
/// # Arguments
///
/// * `connection_string` - The Redis connection URL (e.g.,
/// "redis://:password@127.0.0.1:6379")
/// * `connection_pool_size` - Maximum number of connections in the pool
/// * `connection_timeout` - Maximum time to wait for a connection
///
/// # Returns
///
/// Returns a `Result` containing the `Cache` instance if successful, or an
/// error if the connection could not be established.
///
/// # Examples
///
/// ```rust,no_run
/// use std::time::Duration;
/// use darkwing::cache::Cache;
///
/// async fn example() -> anyhow::Result<()> {
/// let cache = Cache::connect(
/// "redis://127.0.0.1:6379",
/// 5,
/// Duration::from_secs(10)
/// )?;
/// Ok(())
/// }
/// ```
pub fn connect(
connection_string: &str,
connection_pool_size: u32,
connection_timeout: Duration,
) -> anyhow::Result<Self> {
info!("Connecting to Redis cache...");
let client = redis::Client::open(connection_string)
.context("error creating Redis client")?;
let pool = Pool::builder()
.max_size(connection_pool_size)
.connection_timeout(connection_timeout)
.build(client)
.context("error while initializing the Redis connection pool")?;
info!("Connected to Redis cache");
Ok(Self { pool })
}
}
#[cfg(test)]
mod tests {
use super::*;
use redis::Commands;
use testcontainers::runners::AsyncRunner;
use testcontainers_modules::redis::Redis;
#[tokio::test]
async fn test_database_connection() -> anyhow::Result<()> {
let redis_container = Redis::default().start().await?;
let host = redis_container.get_host().await?.to_string();
let port = redis_container.get_host_port_ipv4(6379).await?;
let connection_string = format!("redis://{host}:{port}");
let db = Cache::connect(&connection_string, 5, Duration::from_secs(10))?;
// Verify we can execute a simple query
let mut redis = db
.pool
.get_timeout(Duration::from_secs(10))
.context("Failed to get redis pool instance")?;
redis
.set::<&str, &str, ()>("test", "test")
.context("Failed to set test value in Redis")?;
Ok(())
}
#[tokio::test]
async fn test_invalid_connection() {
let result = Cache::connect("redis://invalid", 5, Duration::from_secs(5));
assert!(result.is_err());
}
}