a06673b
use std::sync::Arc;
use std::time::Duration;
use cache::redis::RedisCache;
use uuid::Uuid;
use crate::error::{RepoError, RepoResult};
pub const IMMUTABLE_TTL: Duration = Duration::from_secs(300);
pub const MUTABLE_TTL: Duration = Duration::from_secs(60);
pub const NEG_TTL: Duration = Duration::from_secs(10);
const KEY_PREFIX: &str = "repo.cache";
#[derive(Clone)]
pub struct RepoCache {
inner: Arc<RedisCache>,
}
impl RepoCache {
pub fn new(inner: Arc<RedisCache>) -> Self {
Self { inner }
}
pub fn raw(&self) -> &Arc<RedisCache> {
&self.inner
}
fn scoped(&self, repo_id: Uuid, kind: &str, key: &str) -> String {
format!("{KEY_PREFIX}:{{{{{repo_id}}}}}:{kind}:{key}")
}
pub async fn get_string(
&self,
repo_id: Uuid,
kind: &str,
key: &str,
) -> RepoResult<Option<String>> {
let k = self.scoped(repo_id, kind, key);
let bytes = self
.inner
.get_bytes(&k)
.await
.map_err(|e| RepoError::Internal(format!("cache get: {e}")))?;
Ok(bytes.map(|b| String::from_utf8_lossy(&b).to_string()))
}
pub async fn set_string(
&self,
repo_id: Uuid,
kind: &str,
key: &str,
value: &str,
ttl: Duration,
) -> RepoResult<()> {
let k = self.scoped(repo_id, kind, key);
self.inner
.set_bytes(&k, value.as_bytes().to_vec(), ttl.as_secs())
.await
.map_err(|e| RepoError::Internal(format!("cache set: {e}")))
}
pub async fn delete(&self, repo_id: Uuid, kind: &str, key: &str) -> RepoResult<()> {
let k = self.scoped(repo_id, kind, key);
self.inner
.delete(&k)
.await
.map_err(|e| RepoError::Internal(format!("cache del: {e}")))
}
pub async fn invalidate_repo(&self, repo_id: Uuid) -> RepoResult<()> {
let _ = repo_id;
Ok(())
}
}
pub trait ObjectCache {
type Value;
async fn fetch<F, Fut>(
&self,
repo_id: Uuid,
kind: &str,
key: &str,
ttl: Duration,
loader: F,
) -> RepoResult<Self::Value>
where
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = RepoResult<Self::Value>>;
}
impl ObjectCache for RepoCache {
type Value = String;
async fn fetch<F, Fut>(
&self,
repo_id: Uuid,
kind: &str,
key: &str,
ttl: Duration,
loader: F,
) -> RepoResult<Self::Value>
where
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = RepoResult<Self::Value>>,
{
if let Some(v) = self.get_string(repo_id, kind, key).await? {
return Ok(v);
}
let fresh = loader().await?;
self.set_string(repo_id, kind, key, &fresh, ttl).await?;
Ok(fresh)
}
}