caveman/cave/src/posts_cache.rs

57 lines
1.5 KiB
Rust

use std::{
collections::{BTreeMap, HashSet},
sync::{Arc, Mutex},
time::{Instant, Duration},
};
/// In-process cache avoids a round-trip to redis for each post
#[derive(Clone)]
pub struct PostsCache {
inner: Arc<Mutex<PostsCacheInner>>,
}
struct PostsCacheInner {
cache: HashSet<Arc<String>>,
ages: BTreeMap<Instant, Arc<String>>,
size: usize,
}
impl PostsCache {
#[must_use] pub fn new(size: usize) -> Self {
PostsCache {
inner: Arc::new(Mutex::new(PostsCacheInner {
cache: HashSet::new(),
ages: BTreeMap::new(),
size,
})),
}
}
// returns true if already exists
#[must_use] pub fn insert(&self, k: String) -> bool {
let k = Arc::new(k);
let mut inner = self.inner.lock().expect("lock");
if inner.cache.contains(&k) {
metrics::increment_counter!("posts_cache_hits", "type" => "hit");
return true;
}
let mut now = Instant::now();
while inner.ages.get(&now).is_some() {
now += Duration::from_millis(1);
}
inner.ages.insert(now, k.clone());
inner.cache.insert(k);
while inner.cache.len() > inner.size {
let oldest = inner.ages.keys().copied().next().expect("ages first");
let oldest_k = inner.ages.remove(&oldest).expect("remove oldest");
inner.cache.remove(&oldest_k);
}
metrics::increment_counter!("posts_cache_hits", "type" => "miss");
false
}
}