diff options
Diffstat (limited to 'cache/src/lib.rs')
| -rw-r--r-- | cache/src/lib.rs | 47 |
1 files changed, 29 insertions, 18 deletions
diff --git a/cache/src/lib.rs b/cache/src/lib.rs index fbda2cf..20e1424 100644 --- a/cache/src/lib.rs +++ b/cache/src/lib.rs @@ -3,17 +3,17 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2025 metamuffin <metamuffin.org> */ -pub mod backends; -pub mod key; +mod backends; +mod helper; use crate::backends::{CacheStorage, filesystem::Filesystem}; use anyhow::{Context, Result, anyhow}; -pub use key::*; -use log::info; +use log::{info, warn}; use serde::{Deserialize, Serialize}; use std::{ any::Any, collections::{BTreeMap, HashMap}, + hash::{DefaultHasher, Hash, Hasher}, path::PathBuf, sync::{ Arc, LazyLock, Mutex, OnceLock, RwLock, @@ -22,11 +22,12 @@ use std::{ time::Instant, }; +pub use helper::{EscapeKey, HashKey}; + #[derive(Debug, Deserialize)] pub struct Config { path: PathBuf, max_in_memory_cache_size: usize, - secret: String, } const CACHE_GENERATION_BUCKET_COUNT: usize = 1024; @@ -54,23 +55,29 @@ pub fn init_cache() -> Result<()> { Ok(()) } -pub fn cache(key: CacheKey, generate: impl FnOnce() -> Result<Vec<u8>>) -> Result<Vec<u8>> { +fn bucket(key: &str) -> usize { + let mut h = DefaultHasher::new(); + key.hash(&mut h); + h.finish() as usize % CACHE_GENERATION_BUCKET_COUNT +} + +pub fn cache(key: &str, generate: impl FnOnce() -> Result<Vec<u8>>) -> Result<Vec<u8>> { // we need a lock even if it exists since somebody might be still in the process of writing. let already_within = WITHIN_CACHE_FILE.with(|a| a.swap(true, Ordering::Relaxed)); let _guard = if already_within { // TODO stupid hack to avoid deadlock for nested calls; not locking is fine but might cause double-generating - CACHE_GENERATION_LOCKS[key.bucket()].try_lock().ok() + CACHE_GENERATION_LOCKS[bucket(key)].try_lock().ok() } else { - CACHE_GENERATION_LOCKS[key.bucket()].lock().ok() + CACHE_GENERATION_LOCKS[bucket(key)].lock().ok() }; let store = CACHE_STORE.get().unwrap(); - let out = match store.read(key)? { + let out = match store.read(&key)? { Some(x) => x, None => { let value = generate()?; - store.store(key, &value)?; + store.store(key.to_owned(), &value)?; value } }; @@ -82,11 +89,11 @@ pub fn cache(key: CacheKey, generate: impl FnOnce() -> Result<Vec<u8>>) -> Resul Ok(out) } -pub fn cache_read(key: CacheKey) -> Result<Option<Vec<u8>>> { +pub fn cache_read(key: &str) -> Result<Option<Vec<u8>>> { CACHE_STORE.get().unwrap().read(key) } -pub fn cache_store(key: CacheKey, generate: impl FnOnce() -> Result<Vec<u8>>) -> Result<CacheKey> { - cache(key, generate)?; +pub fn cache_store(key: String, generate: impl FnOnce() -> Result<Vec<u8>>) -> Result<String> { + cache(&key, generate)?; Ok(key) } @@ -95,18 +102,22 @@ pub struct InMemoryCacheEntry { last_access: Instant, object: Arc<dyn Any + Send + Sync + 'static>, } -pub static CACHE_IN_MEMORY_OBJECTS: LazyLock<RwLock<HashMap<CacheKey, InMemoryCacheEntry>>> = +pub static CACHE_IN_MEMORY_OBJECTS: LazyLock<RwLock<HashMap<String, InMemoryCacheEntry>>> = LazyLock::new(|| RwLock::new(HashMap::new())); pub static CACHE_IN_MEMORY_SIZE: AtomicUsize = AtomicUsize::new(0); -pub fn cache_memory<Fun, T>(key: CacheKey, mut generate: Fun) -> Result<Arc<T>, anyhow::Error> +pub fn cache_memory<Fun, T>(key: &str, mut generate: Fun) -> Result<Arc<T>, anyhow::Error> where Fun: FnMut() -> Result<T, anyhow::Error>, T: Serialize + for<'de> Deserialize<'de> + Send + Sync + 'static, { + if !key.ends_with(".json") { + warn!("cache_memory key not ending in .json: {key:?}") + } + { let mut g = CACHE_IN_MEMORY_OBJECTS.write().unwrap(); - if let Some(entry) = g.get_mut(&key) { + if let Some(entry) = g.get_mut(key) { entry.last_access = Instant::now(); let object = entry .object @@ -117,7 +128,7 @@ where } } - let data = cache(key, move || { + let data = cache(&key, move || { let object = generate()?; Ok(serde_json::to_vec(&object)?) })?; @@ -128,7 +139,7 @@ where { let mut g = CACHE_IN_MEMORY_OBJECTS.write().unwrap(); g.insert( - key, + key.to_owned(), InMemoryCacheEntry { size, last_access: Instant::now(), |