sentinel_core/core/hotspot/
cache.rs

1use crate::base::ParamKey;
2use lru::{KeyRef, LruCache};
3use std::borrow::Borrow;
4use std::hash::Hash;
5use std::sync::{
6    atomic::{AtomicU64, Ordering},
7    Arc, RwLock,
8};
9
10pub trait CounterTrait<K = ParamKey>: Send + Sync + std::fmt::Debug + Default + 'static {
11    fn with_capacity(cap: usize) -> Self;
12    fn cap(&self) -> usize;
13    fn add(&self, key: K, value: u64);
14    fn add_if_absent(&self, key: K, value: u64) -> Option<Arc<AtomicU64>>;
15    #[cfg(not(test))]
16    fn get<Q>(&self, key: &Q) -> Option<Arc<AtomicU64>>
17    where
18        KeyRef<K>: Borrow<Q>,
19        Q: Hash + Eq + ?Sized;
20    #[cfg(test)]
21    fn get<Q>(&self, key: &Q) -> Option<Arc<AtomicU64>>
22    where
23        KeyRef<K>: Borrow<Q>,
24        Q: Hash + Eq + 'static + Sized;
25    #[cfg(not(test))]
26    fn remove<Q>(&self, key: &Q) -> bool
27    where
28        KeyRef<K>: Borrow<Q>,
29        Q: Hash + Eq + ?Sized;
30    #[cfg(test)]
31    fn remove<Q>(&self, key: &Q) -> bool
32    where
33        KeyRef<K>: Borrow<Q>,
34        Q: Hash + Eq + 'static + Sized;
35    #[cfg(not(test))]
36    fn contains<Q>(&self, key: &Q) -> bool
37    where
38        KeyRef<K>: Borrow<Q>,
39        Q: Hash + Eq + ?Sized;
40    #[cfg(test)]
41    fn contains<Q>(&self, key: &Q) -> bool
42    where
43        KeyRef<K>: Borrow<Q>,
44        Q: Hash + Eq + 'static + Sized;
45    fn keys(&self) -> Vec<K>;
46    fn len(&self) -> usize;
47    fn purge(&self);
48}
49
50#[derive(Debug)]
51pub struct Counter<K = ParamKey>
52where
53    K: Send + Sync + Hash + Eq + std::fmt::Debug + Clone + 'static,
54{
55    cache: RwLock<LruCache<K, Arc<AtomicU64>>>,
56}
57
58/// Counter caches the hotspot parameter
59impl<K> CounterTrait<K> for Counter<K>
60where
61    K: Send + Sync + Hash + Eq + std::fmt::Debug + Clone,
62{
63    fn with_capacity(cap: usize) -> Counter<K> {
64        Counter {
65            cache: RwLock::new(LruCache::new(cap)),
66        }
67    }
68
69    fn cap(&self) -> usize {
70        self.cache.read().unwrap().cap()
71    }
72
73    /// `add` add a value to the cache,
74    /// Updates the "recently used"-ness of the key.
75    fn add(&self, key: K, value: u64) {
76        let mut cache = self.cache.write().unwrap();
77        if cache.contains(&key) {
78            cache.get(&key).unwrap().store(value, Ordering::SeqCst);
79        } else {
80            cache.put(key, Arc::new(AtomicU64::new(value)));
81        }
82    }
83
84    // If the key is not existed in the cache, adds a value to the cache then return None. And updates the "recently used"-ness of the key
85    // If the key is already existed in the cache, do nothing and return the prior value
86    fn add_if_absent(&self, key: K, value: u64) -> Option<Arc<AtomicU64>> {
87        let mut cache = self.cache.write().unwrap();
88        if cache.contains(&key) {
89            cache.get(&key).map(|v| Arc::clone(v))
90        } else {
91            cache.put(key, Arc::new(AtomicU64::new(value)));
92            None
93        }
94    }
95
96    // `get` returns key's value from the cache and updates the "recently used"-ness of the key.
97    fn get<Q>(&self, key: &Q) -> Option<Arc<AtomicU64>>
98    where
99        KeyRef<K>: Borrow<Q>,
100        Q: Hash + Eq + ?Sized,
101    {
102        self.cache.write().unwrap().get(&key).map(|v| Arc::clone(v))
103    }
104
105    // `remove` removes a key from the cache.
106    // Return true if the key was contained.
107    fn remove<Q>(&self, key: &Q) -> bool
108    where
109        KeyRef<K>: Borrow<Q>,
110        Q: Hash + Eq + ?Sized,
111    {
112        self.cache.write().unwrap().pop(&key).is_some()
113    }
114
115    // `contains` checks if a key exists in cache
116    // Without updating the recent-ness.
117    fn contains<Q>(&self, key: &Q) -> bool
118    where
119        KeyRef<K>: Borrow<Q>,
120        Q: Hash + Eq + ?Sized,
121    {
122        self.cache.read().unwrap().contains(&key)
123    }
124
125    // `keys` returns the keys in the cache, from oldest to newest.
126    fn keys(&self) -> Vec<K> {
127        let cache = self.cache.read().unwrap();
128        let keys = cache.iter().rev().map(|(k, _v)| k.clone());
129        keys.collect()
130    }
131
132    // `len` returns the number of items in the cache.
133    fn len(&self) -> usize {
134        self.cache.read().unwrap().len()
135    }
136
137    // `purge` clears all cache entries.
138    fn purge(&self) {
139        self.cache.write().unwrap().clear()
140    }
141}
142
143impl<K> Default for Counter<K>
144where
145    K: Send + Sync + Hash + Eq + std::fmt::Debug + Clone,
146{
147    fn default() -> Counter<K> {
148        Counter::<K>::with_capacity(0)
149    }
150}
151
152#[cfg(test)]
153pub(crate) use test::MockCounter;
154
155#[cfg(test)]
156pub(crate) mod test {
157    use super::*;
158    use mockall::predicate::*;
159    use mockall::*;
160
161    mock! {
162        #[derive(Debug)]
163        pub(crate) Counter<K>
164        where
165        K: Send + Sync +Hash + Eq + std::fmt::Debug + Clone + 'static
166        {}
167        impl<K> CounterTrait<K> for Counter<K>
168        where
169        K: Send + Sync +Hash + Eq + std::fmt::Debug + Clone + 'static
170        {
171            fn with_capacity(cap: usize) -> Self;
172            fn cap(&self) -> usize;
173            fn add(&self, key: K, value: u64);
174            fn add_if_absent(&self, key: K, value: u64) -> Option<Arc<AtomicU64>>;
175            fn get<Q>(&self, key: &Q) -> Option<Arc<AtomicU64>>
176            where
177                KeyRef<K>: Borrow<Q>,
178                Q: Hash + Eq + Sized + 'static;
179            fn remove<Q>(&self, key: &Q) -> bool
180            where
181                KeyRef<K>: Borrow<Q>,
182                Q: Hash + Eq + Sized + 'static;
183            fn contains<Q>(&self, key: &Q) -> bool
184            where
185                KeyRef<K>: Borrow<Q>,
186                Q: Hash + Eq + Sized + 'static;
187            fn keys(&self) -> Vec<K>;
188            fn len(&self) -> usize;
189            fn purge(&self);
190        }
191    }
192
193    #[test]
194    fn add_get() {
195        let counter = Counter::with_capacity(100);
196        for i in 1..=100 {
197            counter.add(i.to_string(), i);
198        }
199        assert_eq!(100, counter.len());
200        assert_eq!(
201            1,
202            counter.get(&"1".to_owned()).unwrap().load(Ordering::SeqCst)
203        );
204    }
205
206    #[test]
207    fn add_if_absent() {
208        let counter = Counter::with_capacity(100);
209        for i in 1..=99 {
210            counter.add(i.to_string(), i);
211        }
212        let prior = counter.add_if_absent(100.to_string(), 100);
213        assert!(prior.is_none());
214        let prior = counter.add_if_absent(100.to_string(), 100);
215        assert_eq!(100, prior.unwrap().load(Ordering::SeqCst));
216        assert_eq!(
217            100,
218            counter
219                .get(&100.to_string())
220                .unwrap()
221                .load(Ordering::SeqCst)
222        );
223    }
224
225    #[test]
226    fn contains() {
227        let counter = Counter::with_capacity(100);
228        for i in 1..=100 {
229            counter.add(i.to_string(), i);
230        }
231        assert!(counter.contains(&100.to_string()));
232        assert!(counter.contains(&1.to_string()));
233        assert!(!counter.contains(&101.to_string()));
234        counter.add(101.to_string(), 101);
235        assert!(!counter.contains(&1.to_string()));
236    }
237
238    #[test]
239    fn keys() {
240        let counter = Counter::with_capacity(100);
241        for i in 1..=100 {
242            counter.add(i.to_string(), i);
243        }
244        assert_eq!(100, counter.keys().len());
245        assert_eq!("1", counter.keys()[0]);
246        assert_eq!("100", counter.keys()[99]);
247    }
248
249    #[test]
250    fn purge() {
251        let counter = Counter::with_capacity(100);
252        for i in 1..=100 {
253            counter.add(i.to_string(), i);
254        }
255        assert_eq!(100, counter.len());
256        counter.purge();
257        assert_eq!(0, counter.len());
258    }
259
260    #[test]
261    fn remove() {
262        let counter = Counter::with_capacity(100);
263        for i in 1..=100 {
264            counter.add(i.to_string(), i);
265        }
266        assert_eq!(100, counter.len());
267        counter.remove(&100.to_string());
268        assert_eq!(99, counter.len());
269        let prior = counter.get(&100.to_string());
270        assert!(prior.is_none());
271    }
272}