use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use crate::cache::LogicalCache;
use crate::resources::ByteBudget;
use crate::StorageError;
pub type Graph = Arc<dyn Any + Send + Sync>;
pub struct GraphCache {
cache: Mutex<Option<LogicalCache<(Vec<u8>, u64), Graph>>>,
rent: Mutex<HashMap<Vec<u8>, (u64, u64)>>,
}
impl GraphCache {
pub(crate) fn new(budget: Option<ByteBudget>) -> Result<Self, StorageError> {
let cache = budget
.map(LogicalCache::new)
.transpose()
.map_err(|e| StorageError::InvalidConfiguration(e.to_string()))?;
Ok(Self {
cache: Mutex::new(cache),
rent: Mutex::new(HashMap::new()),
})
}
#[must_use]
pub fn enabled(&self) -> bool {
self.cache.lock().is_ok_and(|cache| cache.is_some())
}
#[must_use]
pub fn get(&self, key: &[u8], ci: u64) -> Option<Graph> {
let mut guard = self.cache.lock().ok()?;
guard.as_mut()?.get(&(key.to_vec(), ci)).cloned()
}
pub fn insert(&self, key: Vec<u8>, ci: u64, graph: Graph, bytes: usize) -> bool {
let Ok(mut guard) = self.cache.lock() else {
return false;
};
let Some(cache) = guard.as_mut() else {
return false;
};
cache.retain(|(_, at), _| *at >= ci);
if let Ok(mut rent) = self.rent.lock() {
rent.remove(&key);
}
cache.insert((key, ci), graph, bytes)
}
pub fn rent(&self, key: &[u8], ci: u64, rows: u64, build_rows: u64) -> bool {
if !self.enabled() {
return false;
}
let Ok(mut rent) = self.rent.lock() else {
return false;
};
rent.retain(|_, (at, _)| *at >= ci);
let spent = rent.entry(key.to_vec()).or_insert((ci, 0));
if spent.0 != ci {
*spent = (ci, 0);
}
spent.1 = spent.1.saturating_add(rows);
spent.1 >= build_rows
}
}