Files
AstralRinth/src/database/redis.rs
Wyatt Verchere 259c5ef3d0 Tests (#719)
* computer switch

* some fixes; github action

* added pr to master

* sqlx database setup

* switched intial GHA test db

* removed sqlx database setup

* unfinished patch route

* bug fixes + tests

* more tests, more fixes, cargo fmt

* merge fixes

* more tests, full reorganization

* fmt, clippy

* sqlx-data

* revs

* removed comments

* delete revs
2023-10-06 09:57:33 -07:00

129 lines
3.6 KiB
Rust

use super::models::DatabaseError;
use deadpool_redis::{Config, Runtime};
use redis::{cmd, FromRedisValue, ToRedisArgs};
use std::fmt::Display;
const DEFAULT_EXPIRY: i64 = 1800; // 30 minutes
#[derive(Clone)]
pub struct RedisPool {
pool: deadpool_redis::Pool,
meta_namespace: String,
}
impl RedisPool {
// initiate a new redis pool
// testing pool uses a hashmap to mimic redis behaviour for very small data sizes (ie: tests)
// PANICS: production pool will panic if redis url is not set
pub fn new(meta_namespace: Option<String>) -> Self {
let redis_pool = Config::from_url(dotenvy::var("REDIS_URL").expect("Redis URL not set"))
.builder()
.expect("Error building Redis pool")
.max_size(
dotenvy::var("DATABASE_MAX_CONNECTIONS")
.ok()
.and_then(|x| x.parse().ok())
.unwrap_or(10000),
)
.runtime(Runtime::Tokio1)
.build()
.expect("Redis connection failed");
RedisPool {
pool: redis_pool,
meta_namespace: meta_namespace.unwrap_or("".to_string()),
}
}
pub async fn set<T1, T2>(
&self,
namespace: &str,
id: T1,
data: T2,
expiry: Option<i64>,
) -> Result<(), DatabaseError>
where
T1: Display,
T2: ToRedisArgs,
{
let mut redis_connection = self.pool.get().await?;
cmd("SET")
.arg(format!("{}_{}:{}", self.meta_namespace, namespace, id))
.arg(data)
.arg("EX")
.arg(expiry.unwrap_or(DEFAULT_EXPIRY))
.query_async::<_, ()>(&mut redis_connection)
.await?;
Ok(())
}
pub async fn get<R, T1>(&self, namespace: &str, id: T1) -> Result<Option<R>, DatabaseError>
where
T1: Display,
R: FromRedisValue,
{
let mut redis_connection = self.pool.get().await?;
let res = cmd("GET")
.arg(format!("{}_{}:{}", self.meta_namespace, namespace, id))
.query_async::<_, Option<R>>(&mut redis_connection)
.await?;
Ok(res)
}
pub async fn multi_get<R, T1>(
&self,
namespace: &str,
ids: impl IntoIterator<Item = T1>,
) -> Result<Vec<Option<R>>, DatabaseError>
where
T1: Display,
R: FromRedisValue,
{
let mut redis_connection = self.pool.get().await?;
let res = cmd("MGET")
.arg(
ids.into_iter()
.map(|x| format!("{}_{}:{}", self.meta_namespace, namespace, x))
.collect::<Vec<_>>(),
)
.query_async::<_, Vec<Option<R>>>(&mut redis_connection)
.await?;
Ok(res)
}
pub async fn delete<T1>(&self, namespace: &str, id: T1) -> Result<(), DatabaseError>
where
T1: Display,
{
let mut redis_connection = self.pool.get().await?;
cmd("DEL")
.arg(format!("{}_{}:{}", self.meta_namespace, namespace, id))
.query_async::<_, ()>(&mut redis_connection)
.await?;
Ok(())
}
pub async fn delete_many(
&self,
iter: impl IntoIterator<Item = (&str, Option<String>)>,
) -> Result<(), DatabaseError>
where {
let mut redis_connection = self.pool.get().await?;
let mut cmd = cmd("DEL");
for (namespace, id) in iter {
if let Some(id) = id {
cmd.arg(format!("{}_{}:{}", self.meta_namespace, namespace, id));
}
}
cmd.query_async::<_, ()>(&mut redis_connection).await?;
Ok(())
}
}