new redis ttl cache crate (untested)

This commit is contained in:
Brandon Thomas committed 2023-03-11 22:53:01 -05:00
1 parent c06c47f02e
commit 817b2c01df
13 files changed
+161 -8

No files matched your search

Generated
+11
View File
@@ -5354,6 +5354,17 @@ dependencies = [
"url 2.3.1",
]
[[package]]
name = "redis_caching"
version = "0.0.1"
dependencies = [
"errors",
"r2d2_redis",
"serde",
"serde_derive",
"serde_json",
]
[[package]]
name = "redis_common"
version = "0.0.1"
+2 -1
View File
@@ -15,6 +15,8 @@ members = [
# Shared libraries
"crates/lib/actix_helpers",
"crates/lib/caching/memory_caching",
"crates/lib/caching/redis_caching",
"crates/lib/clients/cloud_storage",
"crates/lib/clients/fakeyou_client",
"crates/lib/clients/google_drive_common",
@@ -32,7 +34,6 @@ members = [
"crates/lib/idempotency",
"crates/lib/jobs_common",
"crates/lib/media",
"crates/lib/memory_caching",
"crates/lib/redis_common",
"crates/lib/sqlite_queries",
"crates/lib/subprocess_common",
@@ -15,9 +15,9 @@ path = "src/lib.rs"
[dependencies]
# Internal
config = { path = "../config" }
container_common = { path = "../container_common" }
errors = { path = "../errors" }
config = { path = "../../config" }
container_common = { path = "../../container_common" }
errors = { path = "../../errors" }
# External
anyhow = "1.0.40"
@@ -0,0 +1,28 @@
[package]
name = "redis_caching"
edition = "2018"
version = "0.0.1"
authors = [
"Brandon Thomas <bt@brand.io>",
"Brandon Thomas <echelon@gmail.com>",
]
publish = false
[lib]
name = "redis_caching"
path = "src/lib.rs"
[dependencies]
# Internal
errors = { path = "../../errors" }
# Redis Database
r2d2_redis = "0.14.0"
# Serialization
serde = "1.0.125"
serde_derive = "1.0.125"
serde_json = "1.0.64"
[dev-dependencies]
# None yet
@@ -0,0 +1,20 @@
//! redis_caching
//!
//! Easy-to use Redis caching utility.
//!
// Never allow these
#![forbid(private_in_public)]
#![forbid(unused_must_use)] // NB: It's unsafe to not close/check some things
// Okay to toggle
#![forbid(unreachable_patterns)]
#![forbid(unused_imports)]
#![forbid(unused_mut)]
#![forbid(unused_variables)]
// Always allow
#![allow(dead_code)]
#![allow(non_snake_case)]
pub mod redis_ttl_cache;
@@ -0,0 +1,93 @@
use r2d2_redis::RedisConnectionManager;
use r2d2_redis::r2d2::{Pool, PooledConnection};
use r2d2_redis::redis::Commands;
use serde::{Deserialize, Serialize};
use errors::AnyhowResult;
const DEFAULT_TTL_SECONDS : usize = 60;
const REDIS_KEY_PREFIX : &'static str = "_c";
// TODO: Async Redis
#[derive(Clone)]
pub struct RedisTtlCache {
redis_pool: Pool<RedisConnectionManager>,
default_key_ttl: usize,
}
impl RedisTtlCache {
pub fn new(redis_pool: Pool<RedisConnectionManager>) -> Self {
Self {
redis_pool,
default_key_ttl: DEFAULT_TTL_SECONDS,
}
}
pub fn get_connection(&self) -> AnyhowResult<RedisTtlCacheConnection> {
let redis = self.redis_pool.get()?;
Ok(RedisTtlCacheConnection {
redis_connection: redis,
default_key_ttl: self.default_key_ttl,
})
}
}
pub struct RedisTtlCacheConnection {
redis_connection: PooledConnection<RedisConnectionManager>,
default_key_ttl: usize,
}
impl RedisTtlCacheConnection {
pub fn delete_from_cache(&mut self, item_key: &str) -> AnyhowResult<()> {
let redis_key = Self::cache_key(item_key);
let _r : Option<String> = self.redis_connection.del(redis_key)?;
Ok(())
}
pub fn get_from_cache<T>(&mut self, item_key: &str) -> AnyhowResult<Option<T>> where T: for <'a> Deserialize<'a> {
let redis_key = Self::cache_key(item_key);
let maybe_value : Option<String> = self.redis_connection.get(redis_key)?;
if let Some(value) = maybe_value {
let hydrated = serde_json::from_str::<T>(&value)?;
return Ok(Some(hydrated));
}
Ok(None)
}
pub fn persist_to_cache<T>(&mut self, item_key: &str, item: T) -> AnyhowResult<()> where T: Serialize {
let redis_key = Self::cache_key(item_key);
let value = serde_json::to_string(&item)?;
let _r : Option<String> = self.redis_connection.set_ex(redis_key, value, self.default_key_ttl)?;
Ok(())
}
pub fn lazy_load_if_not_cached<T, F>(&mut self, item_key: &str, loader_func: &mut F)
-> AnyhowResult<Option<T>>
where for<'a> T: Serialize + Deserialize<'a> + Clone,
F: FnMut() -> AnyhowResult<Option<T>>,
{
let maybe_value = self.get_from_cache(item_key)?;
if let Some(value) = maybe_value {
return Ok(value);
}
let maybe_value = loader_func()?;
if let Some(value) = maybe_value {
self.persist_to_cache(item_key, value.clone())?;
return Ok(Some(value));
}
Ok(None)
}
fn cache_key(key: &str) -> String {
format!("{}:{}", REDIS_KEY_PREFIX, key)
}
}
+1 -1
View File
@@ -22,7 +22,7 @@ database_queries = { path = "../../../lib/database_queries" }
enums = { path = "../../../_types/enums" }
errors = { path = "../../../lib/errors" }
jobs_common = { path = "../../../lib/jobs_common" }
memory_caching = { path = "../../../lib/memory_caching" }
memory_caching = { path = "../../../lib/caching/memory_caching" }
tokens = { path = "../../../_types/tokens" }
tts_common = { path = "../../../lib/tts_common" }
@@ -21,7 +21,7 @@ container_common = { path = "../../../lib/container_common" }
database_queries = { path = "../../../lib/database_queries" }
hashing = { path = "../../../lib/hashing" }
jobs_common = { path = "../../../lib/jobs_common" }
memory_caching = { path = "../../../lib/memory_caching" }
memory_caching = { path = "../../../lib/caching/memory_caching" }
tts_common = { path = "../../../lib/tts_common" }
# Vendored code
@@ -24,7 +24,7 @@ errors = { path = "../../../lib/errors" }
google_drive_common = { path = "../../../lib/clients/google_drive_common" }
http_server_common = { path = "../../../lib/http_server_common" }
jobs_common = { path = "../../../lib/jobs_common" }
memory_caching = { path = "../../../lib/memory_caching" }
memory_caching = { path = "../../../lib/caching/memory_caching" }
redis_common = { path = "../../../lib/redis_common" }
reusable_types = { path = "../../../_types/reusable_types" }
tts_common = { path = "../../../lib/tts_common" }
@@ -30,7 +30,7 @@ google_drive_common = { path = "../../../lib/clients/google_drive_common" }
http_server_common = { path = "../../../lib/http_server_common" }
jobs_common = { path = "../../../lib/jobs_common" }
media = { path = "../../../lib/media" }
memory_caching = { path = "../../../lib/memory_caching" }
memory_caching = { path = "../../../lib/caching/memory_caching" }
redis_common = { path = "../../../lib/redis_common" }
reusable_types = { path = "../../../_types/reusable_types" }
tokens = { path = "../../../_types/tokens" }