use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use tokio::sync::Mutex;
struct InnerCacheLayer<K, V> {
pub map: HashMap<K, Arc<Mutex<Option<V>>>>,
pub expiration_map: HashMap<u64, Vec<K>>,
}
pub struct AsyncCacheStore<K, V> {
inner: Mutex<InnerCacheLayer<K, V>>,
}
impl<K: 'static + Eq + Hash + Debug + Sync + Send + Clone, V: 'static + Sync + Send>
AsyncCacheStore<K, V>
{
pub fn new() -> Arc<Self> {
let a = Arc::new(AsyncCacheStore {
inner: Mutex::new(InnerCacheLayer {
map: HashMap::new(),
expiration_map: HashMap::new(),
}),
});
let cloned = a.clone();
let first_refresh = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
- 1;
let mut timer = tokio::time::interval(tokio::time::Duration::from_secs(1));
tokio::spawn(async move {
let mut n = first_refresh;
loop {
timer.tick().await;
let mut lock = cloned.inner.lock().await;
match lock.expiration_map.remove(&n) {
Some(expired) => {
for item in expired {
lock.map.remove(&item);
}
}
None => {}
}
n += 1;
}
});
a
}
pub async fn get(&self, key: K, ttl: u64) -> Arc<Mutex<Option<V>>> {
let mut lock = self.inner.lock().await;
match lock.map.get(&key) {
Some(v) => v.clone(),
None => {
let v = Arc::new(Mutex::new(None));
lock.map.insert(key.clone(), v.clone());
lock.expiration_map
.entry(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ ttl,
)
.or_default()
.push(key);
v
}
}
}
pub async fn exists(&self, key: K) -> bool {
let lock = self.inner.lock().await;
lock.map.get(&key).is_some()
}
pub async fn ready(&self, key: K) -> bool {
let lock = self.inner.lock().await;
match lock.map.get(&key) {
Some(v) => v.lock().await.is_some(),
None => false,
}
}
pub async fn expire(&self, key: &K) {
let mut lock = self.inner.lock().await;
lock.map.remove(key);
}
pub async fn clone_inner_map(&self) -> HashMap<K, Option<V>> where V: Clone {
let lock = self.inner.lock().await;
let mut hm = HashMap::new();
for (k, v) in lock.map.iter() {
hm.insert(k.clone(), v.lock().await.clone());
}
hm
}
}