forest/utils/cache/
size_tracking.rs1use std::{borrow::Cow, fmt::Debug, hash::Hash, num::NonZeroUsize, sync::atomic::Ordering};
5
6use get_size2::GetSize;
7use prometheus_client::{
8 collector::Collector,
9 encoding::{DescriptorEncoder, EncodeMetric},
10 metrics::{counter::Counter, gauge::Gauge},
11 registry::Unit,
12};
13use quick_cache::sync::Cache;
14
15use crate::prelude::*;
16
17pub trait CacheKeyConstraints:
18 GetSize + Debug + Send + Sync + Hash + PartialEq + Eq + Clone + 'static
19{
20}
21
22impl<T> CacheKeyConstraints for T where
23 T: GetSize + Debug + Send + Sync + Hash + PartialEq + Eq + Clone + 'static
24{
25}
26
27pub trait CacheValueConstraints: GetSize + Debug + Send + Sync + Clone + 'static {}
28
29impl<T> CacheValueConstraints for T where T: GetSize + Debug + Send + Sync + Clone + 'static {}
30
31#[derive(Debug, derive_more::Deref)]
37pub struct SizeTrackingCache<K, V>
38where
39 K: CacheKeyConstraints,
40 V: CacheValueConstraints,
41{
42 cache_name: Cow<'static, str>,
43 #[deref]
44 cache: Arc<Cache<K, V>>,
45}
46
47impl<K, V> ShallowClone for SizeTrackingCache<K, V>
48where
49 K: CacheKeyConstraints,
50 V: CacheValueConstraints,
51{
52 fn shallow_clone(&self) -> Self {
53 Self {
54 cache_name: self.cache_name.clone(),
55 cache: self.cache.shallow_clone(),
56 }
57 }
58}
59
60impl<K, V> SizeTrackingCache<K, V>
61where
62 K: CacheKeyConstraints,
63 V: CacheValueConstraints,
64{
65 fn register_metrics(&self) {
66 crate::metrics::register_collector(Box::new(self.shallow_clone()));
67 }
68
69 fn new_inner(cache_name: impl Into<Cow<'static, str>>, capacity: NonZeroUsize) -> Self {
70 Self {
71 cache_name: cache_name.into(),
72 cache: Arc::new(Cache::new(capacity.get())),
73 }
74 }
75
76 pub fn new_without_metrics_registry(
77 cache_name: impl Into<Cow<'static, str>>,
78 capacity: NonZeroUsize,
79 ) -> Self {
80 Self::new_inner(cache_name, capacity)
81 }
82
83 pub fn new_with_metrics(
84 cache_name: impl Into<Cow<'static, str>>,
85 capacity: NonZeroUsize,
86 ) -> Self {
87 let c = Self::new_without_metrics_registry(cache_name, capacity);
88 c.register_metrics();
89 c
90 }
91
92 #[inline]
99 pub fn push_and_get_prev(&self, k: K, v: V) -> Option<V> {
100 let prev = self.cache.peek(&k);
101 self.cache.insert(k, v);
102 prev
103 }
104
105 pub(crate) fn size_in_bytes(&self) -> usize {
106 let mut size = 0_usize;
107 for (k, v) in self.cache.iter() {
108 size = size
109 .saturating_add(k.get_size())
110 .saturating_add(v.get_size());
111 }
112 size
113 }
114}
115
116impl<K, V> Collector for SizeTrackingCache<K, V>
117where
118 K: CacheKeyConstraints,
119 V: CacheValueConstraints,
120{
121 fn encode(&self, mut encoder: DescriptorEncoder) -> Result<(), std::fmt::Error> {
122 {
123 let size_in_bytes = {
124 let g: Gauge = Default::default();
125 g.set(self.size_in_bytes() as _);
126 g
127 };
128 let size_metric_name = format!("cache_{}_size", self.cache_name);
129 let size_metric_help = format!("Size of cache {} in bytes", self.cache_name);
130 let size_metric_encoder = encoder.encode_descriptor(
131 &size_metric_name,
132 &size_metric_help,
133 Some(&Unit::Bytes),
134 size_in_bytes.metric_type(),
135 )?;
136 size_in_bytes.encode(size_metric_encoder)?;
137 }
138 {
139 let len_metric_name = format!("cache_{}_len", self.cache_name);
140 let len_metric_help = format!("Length of cache {}", self.cache_name);
141 let len: Gauge = Default::default();
142 len.set(self.len() as _);
143 let len_metric_encoder = encoder.encode_descriptor(
144 &len_metric_name,
145 &len_metric_help,
146 None,
147 len.metric_type(),
148 )?;
149 len.encode(len_metric_encoder)?;
150 }
151 {
152 let cap_metric_name = format!("cache_{}_cap", self.cache_name);
153 let cap_metric_help = format!("Capacity of cache {}", self.cache_name);
154 let cap: Gauge = Default::default();
155 cap.set(self.capacity() as _);
156 let cap_metric_encoder = encoder.encode_descriptor(
157 &cap_metric_name,
158 &cap_metric_help,
159 None,
160 cap.metric_type(),
161 )?;
162 cap.encode(cap_metric_encoder)?;
163 }
164 {
165 let hits_metric_name = format!("cache_{}_hits", self.cache_name);
166 let hits_metric_help = format!("Cache hits of {}", self.cache_name);
167 let hits: Counter = Default::default();
168 hits.inner().store(self.cache.hits(), Ordering::Relaxed);
169 let hits_metric_encoder = encoder.encode_descriptor(
170 &hits_metric_name,
171 &hits_metric_help,
172 None,
173 hits.metric_type(),
174 )?;
175 hits.encode(hits_metric_encoder)?;
176 }
177 {
178 let misses_metric_name = format!("cache_{}_misses", self.cache_name);
179 let misses_metric_help = format!("Cache misses of {}", self.cache_name);
180 let misses: Counter = Default::default();
181 misses.inner().store(self.cache.misses(), Ordering::Relaxed);
182 let misses_metric_encoder = encoder.encode_descriptor(
183 &misses_metric_name,
184 &misses_metric_help,
185 None,
186 misses.metric_type(),
187 )?;
188 misses.encode(misses_metric_encoder)?;
189 }
190
191 Ok(())
192 }
193}