sentinel_core/core/hotspot/
cache.rs1use 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
58impl<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 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 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 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 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 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 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 fn len(&self) -> usize {
134 self.cache.read().unwrap().len()
135 }
136
137 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}