use std::{
ops::{Deref, DerefMut},
time::{Duration, Instant},
};
use async_trait::async_trait;
use deadpool::managed::RecycleError;
use rand::Rng;
use redis::AsyncCommands;
pub use deadpool;
pub type RedisPool = deadpool::managed::Pool<RedisConnection, redis::RedisError>;
pub enum Ttl {
Simple(Duration),
Fuzzy { min: Duration, fuzz: Duration },
Once,
}
pub struct RedisConnectionManager {
client: redis::Client,
check_on_recycle: bool,
connection_ttl: Option<Ttl>,
}
impl RedisConnectionManager {
pub fn new(client: redis::Client, check_on_recycle: bool, connection_ttl: Option<Ttl>) -> Self {
Self {
client,
check_on_recycle,
connection_ttl,
}
}
}
#[async_trait]
impl deadpool::managed::Manager<RedisConnection, redis::RedisError> for RedisConnectionManager {
async fn create(&self) -> Result<RedisConnection, redis::RedisError> {
Ok(RedisConnection {
actual: self.client.get_async_connection().await?,
expires_at: self
.connection_ttl
.as_ref()
.map(|max_duration| match max_duration {
Ttl::Simple(ttl) => Instant::now() + *ttl,
Ttl::Fuzzy { min, fuzz } => {
Instant::now()
+ *min
+ Duration::from_secs_f64(
rand::thread_rng().gen_range(0.0, fuzz.as_secs_f64()),
)
}
Ttl::Once => Instant::now(),
}),
})
}
async fn recycle(
&self,
conn: &mut RedisConnection,
) -> deadpool::managed::RecycleResult<redis::RedisError> {
if self.check_on_recycle {
let _r: bool = conn.exists(b"key").await?;
}
match &conn.expires_at {
Some(expires_at) => {
if &Instant::now() >= expires_at {
Err(RecycleError::Message("Connection expired".to_string()))
} else {
Ok(())
}
}
None => Ok(()),
}
}
}
pub struct RedisConnection {
actual: redis::aio::Connection,
expires_at: Option<Instant>,
}
impl Deref for RedisConnection {
type Target = redis::aio::Connection;
fn deref(&self) -> &Self::Target {
&self.actual
}
}
impl DerefMut for RedisConnection {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.actual
}
}
impl AsMut<redis::aio::Connection> for RedisConnection {
fn as_mut(&mut self) -> &mut redis::aio::Connection {
&mut self.actual
}
}
impl AsRef<redis::aio::Connection> for RedisConnection {
fn as_ref(&self) -> &redis::aio::Connection {
&self.actual
}
}