Skip to main content

bazuka/
lib.rs

1mod expiry;
2mod cache;
3
4pub use cache::*;
5
6mod test {
7    #[allow(unused_imports)]
8    use super::expiry::DataExpiry;
9    #[allow(unused_imports)]
10    use std::sync::Arc;
11    #[allow(unused_imports)]
12    use std::sync::atomic::{AtomicBool, Ordering};
13
14    #[tokio::test]
15    async fn test_cache_size_eviction() {
16        let cache = moka::future::Cache::builder()
17            .max_capacity(3)
18            .time_to_live(tokio::time::Duration::from_secs(5))
19            .eviction_listener(|k: std::sync::Arc<&'static str>, _v, _cause| {
20                // we get k2 here as k1,k3 and k4 has been accessed more frequently.
21                println!("Evicting key: {}", k);
22                assert_eq!(*k, "key2");
23            })
24            .build();
25        cache.insert("key1", "value1").await;
26        cache.insert("key2", "value2").await;
27        cache.insert("key3", "value3").await;
28        assert_eq!(cache.iter().count(), 3);
29        // modify freq access pattern
30        for _ in 0..100 {
31            cache.get("key3").await;
32        }
33        for _ in 0..50 {
34            cache.get("key1").await;
35        }
36        //insertion never triggers eviction
37        cache.insert("key4", "value4").await;
38
39        // lets increase k4 access frequency quickly before eviction cycle kicks in
40        for i in 0..10 {
41            cache.get("key4").await; // get will trigger eviction listener if ttl is reached
42            print!("{},", i + 1);
43        }
44        println!("");
45        //force eviction to happen
46        cache.run_pending_tasks().await;
47        assert_eq!(cache.iter().count(), 3);
48    }
49
50    #[tokio::test]
51    async fn test_immediate_eviction() {
52        let cache = moka::future::Cache::builder()
53            .max_capacity(3)
54            .time_to_live(tokio::time::Duration::from_secs(5))
55            .eviction_listener(|k: std::sync::Arc<&'static str>, _v, _cause| {
56                // we get k2 here as k1,k3 and k4 has been accessed more frequently.
57                println!("Evicting key: {}", k);
58            })
59            .build();
60        cache.insert("key1", "value1").await;
61        cache.invalidate("key1").await;
62        assert_eq!(cache.iter().count(), 0);
63    }
64
65    #[tokio::test]
66    async fn test_cache_expiry() {
67        let cache = moka::future::Cache::builder()
68            .max_capacity(3)
69            .expire_after(DataExpiry)
70            .eviction_listener(|k, v, c| match c {
71                moka::notification::RemovalCause::Expired => {
72                    println!("Evicting expired key: {}, value: {}", k.0, v)
73                }
74                _ => println!("Evicting key: {}, value: {}, cause: {:?}", k.0, v, c),
75            })
76            .build();
77
78        type Key<K, V> = Arc<(Arc<K>, Arc<V>)>;
79        let k1 = Key::from((Arc::new("key1").to_owned(), Arc::new("value1").to_owned()));
80        cache.insert(k1.clone(), 2).await;
81        cache.insert(Key::from((Arc::new("key2").to_owned(), Arc::new("value2").to_owned())), 2).await;
82        cache.insert(Key::from((Arc::new("key3").to_owned(), Arc::new("value3").to_owned())), 2).await;
83        assert_eq!(cache.iter().count(), 3);
84
85        // Update the expiry time for one item to be 5 seconds
86        cache.insert(k1.clone(), 5).await;
87
88        // Wait for the items to expire
89        tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
90
91        //force eviction to happen
92        println!("Running pending tasks to trigger expiry");
93        cache.run_pending_tasks().await;
94
95        assert_eq!(cache.iter().count(), 1);
96    }
97
98    #[tokio::test]
99    async fn test_eviction_laziness() {
100        let flag = Arc::new(AtomicBool::new(false));
101        let cache = moka::future::Cache::builder()
102            .max_capacity(3)
103            .time_to_live(tokio::time::Duration::from_secs(2))
104            .eviction_listener({
105                let flag = Arc::clone(&flag);
106                move |_k: std::sync::Arc<&'static str>, _v: u32, _cause| {
107                    println!("Evicting key: {}", _k);
108                    flag.store(true, Ordering::Relaxed);
109                }
110            })
111            .build();
112
113        cache.insert("key1", 2).await;
114        tokio::time::sleep(tokio::time::Duration::from_secs(4)).await;
115
116        // expecting eviction handler not to be called and flag to be false
117        assert!(!flag.load(Ordering::Relaxed));
118    }
119
120    #[tokio::test]
121    async fn test_eviction_laziness_2() {
122        let flag = Arc::new(AtomicBool::new(false));
123        let cache = moka::future::Cache::builder()
124            .max_capacity(1)
125            .time_to_live(tokio::time::Duration::from_secs(5))
126            .eviction_listener({
127                let flag = Arc::clone(&flag);
128                move |_k: std::sync::Arc<&'static str>, _v: u32, _cause| {
129                    println!("Evicting key: {}", _k);
130                    flag.store(true, Ordering::Relaxed);
131                }
132            })
133            .build();
134
135        cache.insert("key1", 2).await;
136        cache.insert("key2", 2).await;
137        cache.insert("key3", 2).await;
138
139        // expecting eviction handler not to be called and flag to be false even overflowing capcity 
140        // as it's waiting for eviction cycle
141        assert!(!flag.load(Ordering::Relaxed));
142    }
143}