#[cfg(feature = "stats")]
use crate::CacheStats;
use crate::EvictionPolicy;
use dashmap::DashMap;
use parking_lot::lock_api::MutexGuard;
use parking_lot::{Mutex, RawMutex};
use std::collections::VecDeque;
pub struct AsyncGlobalCache<'a, R: Clone> {
cache: &'a DashMap<String, (R, u64, u64)>,
order: &'a Mutex<VecDeque<String>>,
limit: Option<usize>,
max_memory: Option<usize>,
policy: EvictionPolicy,
ttl: Option<u64>,
frequency_weight: Option<f64>,
window_ratio: Option<f64>,
#[cfg(feature = "stats")]
stats: &'a CacheStats,
}
impl<'a, R: Clone> AsyncGlobalCache<'a, R> {
#[cfg(not(feature = "stats"))]
pub fn new(
cache: &'a DashMap<String, (R, u64, u64)>,
order: &'a Mutex<VecDeque<String>>,
limit: Option<usize>,
max_memory: Option<usize>,
policy: EvictionPolicy,
ttl: Option<u64>,
frequency_weight: Option<f64>,
window_ratio: Option<f64>,
) -> Self {
Self {
cache,
order,
limit,
max_memory,
policy,
ttl,
frequency_weight,
window_ratio,
}
}
#[cfg(feature = "stats")]
pub fn new(
cache: &'a DashMap<String, (R, u64, u64)>,
order: &'a Mutex<VecDeque<String>>,
limit: Option<usize>,
max_memory: Option<usize>,
policy: EvictionPolicy,
ttl: Option<u64>,
frequency_weight: Option<f64>,
window_ratio: Option<f64>,
stats: &'a CacheStats,
) -> Self {
Self {
cache,
order,
limit,
max_memory,
policy,
ttl,
frequency_weight,
window_ratio,
stats,
}
}
pub fn get(&self, key: &str) -> Option<R> {
if let Some(mut entry_ref) = self.cache.get_mut(key) {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let is_expired = if let Some(ttl) = self.ttl {
let age = now.saturating_sub(entry_ref.1);
age >= ttl
} else {
false
};
if !is_expired {
let cached_value = entry_ref.0.clone();
match self.policy {
EvictionPolicy::LFU => {
entry_ref.2 = entry_ref.2.saturating_add(1);
}
EvictionPolicy::ARC => {
entry_ref.2 = entry_ref.2.saturating_add(1);
}
EvictionPolicy::TLRU => {
entry_ref.2 = entry_ref.2.saturating_add(1);
}
EvictionPolicy::WTinyLFU => {
entry_ref.2 = entry_ref.2.saturating_add(1);
}
EvictionPolicy::LRU => {
}
EvictionPolicy::FIFO | EvictionPolicy::Random => {
}
}
drop(entry_ref);
#[cfg(feature = "stats")]
self.stats.record_hit();
if self.limit.is_some()
&& (self.policy == EvictionPolicy::LRU
|| self.policy == EvictionPolicy::ARC
|| self.policy == EvictionPolicy::TLRU)
{
if self.cache.contains_key(key) {
let mut order = self.order.lock();
if self.cache.contains_key(key) {
order.retain(|k| k != key);
order.push_back(key.to_string());
}
}
}
return Some(cached_value);
}
drop(entry_ref);
self.cache.remove(key);
let mut order = self.order.lock();
order.retain(|k| k != key);
}
#[cfg(feature = "stats")]
self.stats.record_miss();
None
}
pub fn insert(&self, key: &str, value: R) {
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let mut order = self.order.lock();
if self.is_already_key_inserted(key, &mut order) {
return;
}
self.handle_entry_limit_eviction(&mut order);
order.push_back(key.to_string());
self.cache.insert(key.to_string(), (value, timestamp, 0));
}
fn is_already_key_inserted(
&self,
key: &str,
order: &mut MutexGuard<RawMutex, VecDeque<String>>,
) -> bool {
if self.cache.contains_key(key) {
if self.policy == EvictionPolicy::LRU || self.policy == EvictionPolicy::ARC {
order.retain(|k| k != key);
order.push_back(key.to_string());
}
return true;
}
false
}
fn find_min_frequency_key(&self, order: &VecDeque<String>) -> Option<String> {
let mut min_freq_key: Option<String> = None;
let mut min_freq = u64::MAX;
for evict_key in order.iter() {
if let Some(entry) = self.cache.get(evict_key) {
if entry.2 < min_freq {
min_freq = entry.2;
min_freq_key = Some(evict_key.clone());
}
}
}
min_freq_key
}
fn find_arc_eviction_key(&self, order: &VecDeque<String>) -> Option<String> {
let mut best_evict_key: Option<String> = None;
let mut best_score = f64::MAX;
for (idx, evict_key) in order.iter().enumerate() {
if let Some(entry) = self.cache.get(evict_key) {
let frequency = entry.2 as f64;
let position_weight = (order.len() - idx) as f64;
let score = frequency * position_weight;
if score < best_score {
best_score = score;
best_evict_key = Some(evict_key.clone());
}
}
}
best_evict_key
}
fn find_tlru_eviction_key(&self, order: &VecDeque<String>) -> Option<String> {
let mut best_evict_key: Option<String> = None;
let mut best_score = f64::MAX;
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
for (idx, evict_key) in order.iter().enumerate() {
if let Some(entry) = self.cache.get(evict_key) {
let frequency = entry.2 as f64;
let position_weight = (order.len() - idx) as f64;
let age_factor = if let Some(ttl_secs) = self.ttl {
let entry_timestamp = entry.1;
let elapsed = now.saturating_sub(entry_timestamp) as f64;
let ttl_f64 = ttl_secs as f64;
(1.0 - (elapsed / ttl_f64).min(1.0)).max(0.0)
} else {
1.0 };
let frequency_component = if let Some(weight) = self.frequency_weight {
if frequency > 0.0 {
frequency.powf(weight)
} else {
0.0
}
} else {
frequency
};
let score = frequency_component * position_weight * age_factor;
if score < best_score {
best_score = score;
best_evict_key = Some(evict_key.clone());
}
}
}
best_evict_key
}
fn find_min_frequency_in_protected_segment<'b, I>(&self, order: I) -> Option<String>
where
I: Iterator<Item = &'b String>,
{
let mut min_freq = u64::MAX;
let mut min_freq_key: Option<String> = None;
for key in order {
if let Some(entry) = self.cache.get(key) {
if entry.2 < min_freq {
min_freq = entry.2;
min_freq_key = Some(key.clone());
}
}
}
min_freq_key
}
fn try_evict_from_window(&self, order: &mut VecDeque<String>, window_size: usize) -> bool {
for i in 0..window_size.min(order.len()) {
if let Some(evict_key) = order.get(i) {
if self.cache.contains_key(evict_key) {
let key_to_remove = evict_key.clone();
self.cache.remove(&key_to_remove);
order.remove(i);
return true;
}
}
}
false
}
fn handle_entry_limit_eviction(&self, order: &mut VecDeque<String>) {
if let Some(limit) = self.limit {
if self.cache.len() >= limit {
match self.policy {
EvictionPolicy::LFU => {
if let Some(evict_key) = self.find_min_frequency_key(order) {
self.cache.remove(&evict_key);
order.retain(|k| k != &evict_key);
}
}
EvictionPolicy::ARC => {
if let Some(evict_key) = self.find_arc_eviction_key(order) {
self.cache.remove(&evict_key);
order.retain(|k| k != &evict_key);
}
}
EvictionPolicy::TLRU => {
if let Some(evict_key) = self.find_tlru_eviction_key(order) {
self.cache.remove(&evict_key);
order.retain(|k| k != &evict_key);
}
}
EvictionPolicy::Random => {
if !order.is_empty() {
let pos = fastrand::usize(..order.len());
if let Some(evict_key) = order.remove(pos) {
self.cache.remove(&evict_key);
}
}
}
EvictionPolicy::FIFO | EvictionPolicy::LRU => {
while let Some(evict_key) = order.pop_front() {
if self.cache.contains_key(&evict_key) {
self.cache.remove(&evict_key);
break;
}
}
}
EvictionPolicy::WTinyLFU => {
let window_ratio = self.window_ratio.unwrap_or(0.20); let window_size = crate::utils::calculate_window_size(limit, window_ratio);
if order.len() <= window_size {
while let Some(evict_key) = order.pop_front() {
if self.cache.contains_key(&evict_key) {
self.cache.remove(&evict_key);
break;
}
}
} else {
let evicted = self.try_evict_from_window(order, window_size);
if !evicted {
if let Some(evict_key) = self
.find_min_frequency_in_protected_segment(
order.iter().skip(window_size),
)
{
self.cache.remove(&evict_key);
order.retain(|k| k != &evict_key);
}
}
}
}
}
}
}
}
#[cfg(feature = "stats")]
pub fn stats(&self) -> &CacheStats {
self.stats
}
}
impl<'a, R: Clone + crate::MemoryEstimator> AsyncGlobalCache<'a, R> {
pub fn insert_with_memory(&self, key: &str, value: R) {
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let mut order = self.order.lock();
if self.is_already_key_inserted(key, &mut order) {
return;
}
if let Some(max_mem) = self.max_memory {
let value_size = value.estimate_memory();
if value_size > max_mem {
return;
}
loop {
let current_mem: usize = self
.cache
.iter()
.map(|entry| entry.value().0.estimate_memory())
.sum();
if current_mem + value_size <= max_mem {
break;
}
let evicted = match self.policy {
EvictionPolicy::LFU => {
if let Some(evict_key) = self.find_min_frequency_key(&*order) {
self.cache.remove(&evict_key);
order.retain(|k| k != &evict_key);
true
} else {
false
}
}
EvictionPolicy::ARC => {
if let Some(evict_key) = self.find_arc_eviction_key(&*order) {
self.cache.remove(&evict_key);
order.retain(|k| k != &evict_key);
true
} else {
false
}
}
EvictionPolicy::TLRU => {
if let Some(evict_key) = self.find_tlru_eviction_key(&*order) {
self.cache.remove(&evict_key);
order.retain(|k| k != &evict_key);
true
} else {
false
}
}
EvictionPolicy::Random => {
if !order.is_empty() {
let pos = fastrand::usize(..order.len());
if let Some(evict_key) = order.remove(pos) {
self.cache.remove(&evict_key);
true
} else {
false
}
} else {
false
}
}
EvictionPolicy::FIFO | EvictionPolicy::LRU => {
if let Some(evict_key) = order.pop_front() {
self.cache.remove(&evict_key);
true
} else {
false
}
}
EvictionPolicy::WTinyLFU => {
let window_ratio = self.window_ratio.unwrap_or(0.20); let limit = self.limit.unwrap_or(usize::MAX);
let window_size = crate::utils::calculate_window_size(limit, window_ratio);
if order.len() <= window_size {
if let Some(evict_key) = order.pop_front() {
if self.cache.contains_key(&evict_key) {
self.cache.remove(&evict_key);
true
} else {
false
}
} else {
false
}
} else {
let evicted = self.try_evict_from_window(&mut order, window_size);
if !evicted {
if let Some(evict_key) = self
.find_min_frequency_in_protected_segment(
order.iter().skip(window_size),
)
{
self.cache.remove(&evict_key);
order.retain(|k| k != &evict_key);
true
} else {
false
}
} else {
true
}
}
}
};
if !evicted {
break; }
}
}
self.handle_entry_limit_eviction(&mut order);
order.push_back(key.to_string());
self.cache.insert(key.to_string(), (value, timestamp, 0));
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::{SystemTime, UNIX_EPOCH};
#[test]
fn test_async_cache_basic() {
let cache = DashMap::new();
let order = Mutex::new(VecDeque::new());
#[cfg(not(feature = "stats"))]
let async_cache =
AsyncGlobalCache::new(&cache, &order, None, None, EvictionPolicy::FIFO, None, None);
#[cfg(feature = "stats")]
let stats = CacheStats::new();
#[cfg(feature = "stats")]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
None,
None,
EvictionPolicy::FIFO,
None,
None,
None,
&stats,
);
async_cache.insert("key1", "value1");
assert_eq!(async_cache.get("key1"), Some("value1"));
assert_eq!(async_cache.get("key2"), None);
}
#[test]
fn test_async_cache_lfu_eviction() {
let cache = DashMap::new();
let order = Mutex::new(VecDeque::new());
#[cfg(not(feature = "stats"))]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(2),
None,
EvictionPolicy::LFU,
None,
None,
);
#[cfg(feature = "stats")]
let stats = CacheStats::new();
#[cfg(feature = "stats")]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(2),
None,
EvictionPolicy::LFU,
None,
None,
None,
&stats,
);
async_cache.insert("key1", "value1");
async_cache.insert("key2", "value2");
for _ in 0..5 {
async_cache.get("key1");
}
async_cache.insert("key3", "value3");
assert_eq!(async_cache.get("key1"), Some("value1"));
assert_eq!(async_cache.get("key2"), None);
assert_eq!(async_cache.get("key3"), Some("value3"));
}
#[test]
fn test_async_cache_ttl_boundary_expires() {
let cache = DashMap::new();
let order = Mutex::new(VecDeque::new());
#[cfg(not(feature = "stats"))]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
None,
None,
EvictionPolicy::FIFO,
Some(1),
None,
);
#[cfg(feature = "stats")]
let stats = CacheStats::new();
#[cfg(feature = "stats")]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
None,
None,
EvictionPolicy::FIFO,
Some(1),
None,
None,
&stats,
);
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
cache.insert("k".to_string(), ("v", now.saturating_sub(1), 0));
assert_eq!(async_cache.get("k"), None);
}
#[test]
fn test_async_cache_clock_moves_backwards_not_expired() {
let cache = DashMap::new();
let order = Mutex::new(VecDeque::new());
#[cfg(not(feature = "stats"))]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
None,
None,
EvictionPolicy::FIFO,
Some(10),
None,
);
#[cfg(feature = "stats")]
let stats = CacheStats::new();
#[cfg(feature = "stats")]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
None,
None,
EvictionPolicy::FIFO,
Some(10),
None,
None,
&stats,
);
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
async_cache.insert("k", "v");
let future_ts = now.saturating_add(100);
if let Some(mut entry) = cache.get_mut("k") {
entry.1 = future_ts;
}
assert_eq!(async_cache.get("k"), Some("v"));
assert!(order.lock().contains(&"k".to_string()));
}
#[test]
fn test_tlru_with_low_frequency_weight() {
let cache = DashMap::new();
let order = Mutex::new(VecDeque::new());
#[cfg(not(feature = "stats"))]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(3),
None,
EvictionPolicy::TLRU,
Some(10),
Some(0.3), );
#[cfg(feature = "stats")]
let stats = CacheStats::new();
#[cfg(feature = "stats")]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(3),
None,
EvictionPolicy::TLRU,
Some(10),
Some(0.3),
None,
&stats,
);
async_cache.insert("k1", 1);
async_cache.insert("k2", 2);
async_cache.insert("k3", 3);
for _ in 0..10 {
let _ = async_cache.get("k1");
}
std::thread::sleep(std::time::Duration::from_millis(100));
async_cache.insert("k4", 4);
assert_eq!(async_cache.get("k4"), Some(4));
}
#[test]
fn test_tlru_with_high_frequency_weight() {
let cache = DashMap::new();
let order = Mutex::new(VecDeque::new());
#[cfg(not(feature = "stats"))]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(3),
None,
EvictionPolicy::TLRU,
Some(10),
Some(1.5), );
#[cfg(feature = "stats")]
let stats = CacheStats::new();
#[cfg(feature = "stats")]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(3),
None,
EvictionPolicy::TLRU,
Some(10),
Some(1.5),
None,
&stats,
);
async_cache.insert("k1", 1);
async_cache.insert("k2", 2);
async_cache.insert("k3", 3);
for _ in 0..10 {
let _ = async_cache.get("k1");
}
std::thread::sleep(std::time::Duration::from_millis(100));
async_cache.insert("k4", 4);
assert_eq!(async_cache.get("k1"), Some(1));
assert_eq!(async_cache.get("k4"), Some(4));
}
#[test]
fn test_tlru_default_frequency_weight() {
let cache = DashMap::new();
let order = Mutex::new(VecDeque::new());
#[cfg(not(feature = "stats"))]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(2),
None,
EvictionPolicy::TLRU,
Some(5),
None, );
#[cfg(feature = "stats")]
let stats = CacheStats::new();
#[cfg(feature = "stats")]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(2),
None,
EvictionPolicy::TLRU,
Some(5),
None,
None,
&stats,
);
async_cache.insert("k1", 1);
async_cache.insert("k2", 2);
for _ in 0..3 {
let _ = async_cache.get("k1");
}
async_cache.insert("k3", 3);
assert_eq!(async_cache.get("k1"), Some(1));
assert_eq!(async_cache.get("k3"), Some(3));
}
#[test]
fn test_tlru_no_ttl_with_frequency_weight() {
let cache = DashMap::new();
let order = Mutex::new(VecDeque::new());
#[cfg(not(feature = "stats"))]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(3),
None,
EvictionPolicy::TLRU,
None, Some(1.5),
);
#[cfg(feature = "stats")]
let stats = CacheStats::new();
#[cfg(feature = "stats")]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(3),
None,
EvictionPolicy::TLRU,
None,
Some(1.5),
None,
&stats,
);
async_cache.insert("k1", 1);
async_cache.insert("k2", 2);
async_cache.insert("k3", 3);
for _ in 0..10 {
let _ = async_cache.get("k1");
}
async_cache.insert("k4", 4);
assert_eq!(async_cache.get("k1"), Some(1));
}
#[test]
fn test_tlru_frequency_weight_comparison() {
let cache_low = DashMap::new();
let order_low = Mutex::new(VecDeque::new());
let cache_high = DashMap::new();
let order_high = Mutex::new(VecDeque::new());
#[cfg(not(feature = "stats"))]
let async_cache_low = AsyncGlobalCache::new(
&cache_low,
&order_low,
Some(2),
None,
EvictionPolicy::TLRU,
Some(10),
Some(0.3), );
#[cfg(not(feature = "stats"))]
let async_cache_high = AsyncGlobalCache::new(
&cache_high,
&order_high,
Some(2),
None,
EvictionPolicy::TLRU,
Some(10),
Some(2.0), );
#[cfg(feature = "stats")]
let stats_low = CacheStats::new();
#[cfg(feature = "stats")]
let async_cache_low = AsyncGlobalCache::new(
&cache_low,
&order_low,
Some(2),
None,
EvictionPolicy::TLRU,
Some(10),
Some(0.3),
None,
&stats_low,
);
#[cfg(feature = "stats")]
let stats_high = CacheStats::new();
#[cfg(feature = "stats")]
let async_cache_high = AsyncGlobalCache::new(
&cache_high,
&order_high,
Some(2),
None,
EvictionPolicy::TLRU,
Some(10),
Some(2.0),
None,
&stats_high,
);
async_cache_low.insert("k1", 1);
async_cache_low.insert("k2", 2);
async_cache_high.insert("k1", 1);
async_cache_high.insert("k2", 2);
for _ in 0..5 {
let _ = async_cache_low.get("k1");
let _ = async_cache_high.get("k1");
}
std::thread::sleep(std::time::Duration::from_millis(50));
async_cache_low.insert("k3", 3);
async_cache_high.insert("k3", 3);
assert_eq!(async_cache_low.get("k3"), Some(3));
assert_eq!(async_cache_high.get("k3"), Some(3));
}
#[test]
fn test_tlru_concurrent_with_frequency_weight() {
use std::sync::Arc;
use std::thread;
let cache = Arc::new(DashMap::new());
let order = Arc::new(Mutex::new(VecDeque::new()));
#[cfg(feature = "stats")]
let stats = Arc::new(CacheStats::new());
{
#[cfg(not(feature = "stats"))]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(10),
None,
EvictionPolicy::TLRU,
Some(10),
Some(1.2), );
#[cfg(feature = "stats")]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(10),
None,
EvictionPolicy::TLRU,
Some(10),
Some(1.2),
None,
&stats,
);
async_cache.insert("k1", 1);
async_cache.insert("k2", 2);
}
let handles: Vec<_> = (0..5)
.map(|i| {
let cache_clone = Arc::clone(&cache);
let order_clone = Arc::clone(&order);
#[cfg(feature = "stats")]
let stats_clone = Arc::clone(&stats);
thread::spawn(move || {
#[cfg(not(feature = "stats"))]
let async_cache = AsyncGlobalCache::new(
&cache_clone,
&order_clone,
Some(10),
None,
EvictionPolicy::TLRU,
Some(10),
Some(1.2),
);
#[cfg(feature = "stats")]
let async_cache = AsyncGlobalCache::new(
&cache_clone,
&order_clone,
Some(10),
None,
EvictionPolicy::TLRU,
Some(10),
Some(1.2),
None,
&stats_clone,
);
for _ in 0..3 {
let _ = async_cache.get("k1");
}
async_cache.insert(&format!("k{}", i + 3), i + 3);
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
#[cfg(not(feature = "stats"))]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(10),
None,
EvictionPolicy::TLRU,
Some(10),
Some(1.2),
None,
);
#[cfg(feature = "stats")]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(10),
None,
EvictionPolicy::TLRU,
Some(10),
Some(1.2),
None,
&stats,
);
assert_eq!(async_cache.get("k1"), Some(1));
}
#[test]
fn test_tlru_frequency_weight_edge_cases() {
let cache = DashMap::new();
let order = Mutex::new(VecDeque::new());
#[cfg(not(feature = "stats"))]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(2),
None,
EvictionPolicy::TLRU,
Some(5),
Some(0.1), None,
);
#[cfg(feature = "stats")]
let stats = CacheStats::new();
#[cfg(feature = "stats")]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(2),
None,
EvictionPolicy::TLRU,
Some(5),
Some(0.1),
None,
&stats,
);
async_cache.insert("k1", 1);
async_cache.insert("k2", 2);
for _ in 0..100 {
let _ = async_cache.get("k1");
}
std::thread::sleep(std::time::Duration::from_millis(50));
async_cache.insert("k3", 3);
assert!(async_cache.get("k3").is_some());
}
#[test]
fn test_tlru_frequency_weight_with_lru_pattern() {
let cache = DashMap::new();
let order = Mutex::new(VecDeque::new());
#[cfg(not(feature = "stats"))]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(3),
None,
EvictionPolicy::TLRU,
Some(10),
Some(1.0), None,
);
#[cfg(feature = "stats")]
let stats = CacheStats::new();
#[cfg(feature = "stats")]
let async_cache = AsyncGlobalCache::new(
&cache,
&order,
Some(3),
None,
EvictionPolicy::TLRU,
Some(10),
Some(1.0),
None,
&stats,
);
async_cache.insert("k1", 1);
async_cache.insert("k2", 2);
async_cache.insert("k3", 3);
let _ = async_cache.get("k1");
let _ = async_cache.get("k2");
let _ = async_cache.get("k1");
let _ = async_cache.get("k2");
async_cache.insert("k4", 4);
assert_eq!(async_cache.get("k1"), Some(1));
assert_eq!(async_cache.get("k2"), Some(2));
assert_eq!(async_cache.get("k4"), Some(4));
assert_eq!(async_cache.get("k3"), None);
}
}