use std::time::{Duration, Instant}; pub struct CacheEntry { pub value: Arc, pub expires_at: Instant, } use dashmap::DashMap; use serde_json::Value; use std::sync::Arc; use tokio::sync::OnceCell; pub struct ProxyRequestCache { pub map: DashMap>>, } impl ProxyRequestCache { pub fn global() -> &'static Self { static INSTANCE: once_cell::sync::OnceCell = once_cell::sync::OnceCell::new(); INSTANCE.get_or_init(|| ProxyRequestCache { map: DashMap::new(), }) } pub fn make_key(prefix: &str, id: &str) -> String { format!("{}:{}", prefix, id) } pub async fn get_or_fetch(&self, key: String, ttl: Duration, fetch_fn: F) -> Arc where F: Fn() -> Fut, Fut: std::future::Future, { let now = Instant::now(); let key_cloned = key.clone(); let cell = self .map .entry(key) .or_insert_with(|| Arc::new(OnceCell::new())) .clone(); if let Some(entry) = cell.get() { if entry.expires_at > now { return Arc::clone(&entry.value); } } if let Some(entry) = cell.get() { if entry.expires_at <= now { self.map .remove_if(&key_cloned, |_, v| Arc::ptr_eq(v, &cell)); let new_cell = Arc::new(OnceCell::new()); self.map.insert(key_cloned.clone(), new_cell.clone()); return Box::pin(self.get_or_fetch(key_cloned, ttl, fetch_fn)).await; } } let value = fetch_fn().await; let entry = CacheEntry { value: Arc::new(value), expires_at: Instant::now() + ttl, }; let _ = cell.set(entry); Arc::clone(&cell.get().unwrap().value) } }