Skip to main content

omega_cache/
lib.rs

1pub mod admission;
2pub mod clock;
3pub mod core;
4pub mod metrics;
5pub mod s3fifo;
6
7pub use crate::admission::{AdmissionPolicy, AlwaysAdmission, FrequentPolicy};
8use crate::core::engine::CacheEngine;
9use crate::core::entry_ref::Ref;
10use crate::core::key::Key;
11use crate::metrics::MetricsSnapshot;
12pub use omega_cache_macros::cache;
13use std::borrow::Borrow;
14use std::hash::Hash;
15use std::marker::PhantomData;
16
17pub struct Cache<E, K, V, P>
18where
19    E: CacheEngine<K, V>,
20    K: Eq + Hash,
21    P: AdmissionPolicy<K>,
22{
23    engine: E,
24    admission_policy: P,
25    _phantom: PhantomData<(K, V)>,
26}
27
28impl<E, K, V, A> Cache<E, K, V, A>
29where
30    E: CacheEngine<K, V>,
31    K: Eq + Hash,
32    A: AdmissionPolicy<K>,
33{
34    pub fn new(engine: E, admission_policy: A) -> Self {
35        Self {
36            engine,
37            admission_policy,
38            _phantom: Default::default(),
39        }
40    }
41
42    /// Retrieves a value from the cache.
43    ///
44    /// If the key exists, the admission policy is notified of the access.
45    ///
46    /// Returns a [`EntryRef`] that provides controlled access to the entry.
47    pub fn get<Q>(&self, key: &Q) -> Option<Ref<K, V>>
48    where
49        Key<K>: Borrow<Q>,
50        Q: Eq + Hash,
51    {
52        self.admission_policy.record(key);
53        self.engine.get(key)
54    }
55
56    /// Inserts a key-value pair into the cache.
57    ///
58    /// When the cache is full, an eviction candidate is selected by the engine.
59    /// The admission policy then decides whether the incoming entry should
60    /// replace the candidate.
61    ///
62    /// Returns a [`EntryRef`] if an existing entry was replaced.
63    pub fn insert(&self, key: K, value: V) {
64        self.engine
65            .insert_with(key, value, None, |incoming, victim| {
66                self.admission_policy.admit(incoming, victim)
67            })
68    }
69
70    /// Removes an entry from the cache.
71    ///
72    /// Returns a [`EntryRef`] if the entry was present.
73    pub fn remove<Q>(&self, key: &Q) -> bool
74    where
75        Key<K>: Borrow<Q>,
76        Q: Eq + Hash,
77    {
78        self.engine.remove(key)
79    }
80
81    pub fn metrics(&self) -> MetricsSnapshot {
82        self.engine.metrics()
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use crate::cache;
89    use crate::core::backoff::BackoffPolicy;
90    use crate::core::utils::random_string_with_len;
91    use std::thread::scope;
92
93    #[test]
94    fn test_cache_should_create_s3fifo_and_run_workload() {
95        let num_threads = 16;
96        let op_per_thread = 10000;
97
98        let cache = cache!(
99            engine: S3FIFO {
100                capacity: 10000,
101                backoff: { policy: BackoffPolicy::Exponential, limit: 10 },
102                metrics: { shards: 8, latency_samples: 1024 },
103            },
104            admission: Frequent {
105                count_min_sketch: { width: 1024, depth: 4 },
106                decay_threshold: 1000
107            }
108        );
109
110        scope(|scope| {
111            for _ in 0..num_threads {
112                scope.spawn(|| {
113                    for _ in 0..op_per_thread {
114                        let key = random_string_with_len(10);
115                        let value = random_string_with_len(255);
116                        cache.insert(key, value);
117                    }
118                });
119            }
120        });
121    }
122
123    #[test]
124    fn test_cache_should_create_clock_cache_and_run_workload() {
125        let num_threads = 16;
126        let op_per_thread = 10000;
127
128        let cache = cache!(
129            engine: S3FIFO {
130                capacity: 10000,
131                backoff: { policy: BackoffPolicy::Exponential, limit: 10 },
132                metrics: { shards: 8, latency_samples: 1024 },
133            },
134            admission: Frequent {
135                count_min_sketch: { width: 1024, depth: 4 },
136                decay_threshold: 1000
137            }
138        );
139
140        scope(|scope| {
141            for _ in 0..num_threads {
142                scope.spawn(|| {
143                    for _ in 0..op_per_thread {
144                        let key = random_string_with_len(10);
145                        let value = random_string_with_len(255);
146                        cache.insert(key, value);
147                    }
148                });
149            }
150        });
151    }
152}