fc_rpc/cache/
lru_cache.rs1use scale_codec::Encode;
20use schnellru::{LruMap, Unlimited};
21
22pub struct LRUCacheByteLimited<K, V> {
23 cache: LruMap<K, V, Unlimited>,
24 max_size: u64,
25 metrics: Option<LRUCacheByteLimitedMetrics>,
26 size: u64,
27}
28
29impl<K: Eq + core::hash::Hash, V: Encode> LRUCacheByteLimited<K, V> {
30 pub fn new(
31 cache_name: &'static str,
32 max_size: u64,
33 prometheus_registry: Option<prometheus_endpoint::Registry>,
34 ) -> Self {
35 let metrics = match prometheus_registry {
36 Some(registry) => match LRUCacheByteLimitedMetrics::register(cache_name, ®istry) {
37 Ok(metrics) => Some(metrics),
38 Err(e) => {
39 log::error!(target: "eth-cache", "Failed to register metrics: {e:?}");
40 None
41 }
42 },
43 None => None,
44 };
45
46 Self {
47 cache: LruMap::new(Unlimited),
48 max_size,
49 metrics,
50 size: 0,
51 }
52 }
53 pub fn get(&mut self, k: &K) -> Option<&V> {
54 if let Some(v) = self.cache.get(k) {
55 if let Some(metrics) = &self.metrics {
57 metrics.hits.inc();
58 }
59 Some(v)
60 } else {
61 if let Some(metrics) = &self.metrics {
63 metrics.miss.inc();
64 }
65 None
66 }
67 }
68 pub fn put(&mut self, k: K, v: V) {
69 self.size += v.encoded_size() as u64;
71
72 while self.size > self.max_size {
73 if let Some((_, v)) = self.cache.pop_oldest() {
74 let v_size = v.encoded_size() as u64;
75 self.size -= v_size;
76 } else {
77 break;
78 }
79 }
80
81 self.cache.insert(k, v);
83 if let Some(metrics) = &self.metrics {
85 metrics.size.set(self.size);
86 }
87 }
88}
89
90struct LRUCacheByteLimitedMetrics {
91 hits: prometheus::IntCounter,
92 miss: prometheus::IntCounter,
93 size: prometheus_endpoint::Gauge<prometheus_endpoint::U64>,
94}
95
96impl LRUCacheByteLimitedMetrics {
97 pub(crate) fn register(
98 cache_name: &'static str,
99 registry: &prometheus_endpoint::Registry,
100 ) -> Result<Self, prometheus_endpoint::PrometheusError> {
101 Ok(Self {
102 hits: prometheus_endpoint::register(
103 prometheus::IntCounter::new(
104 format!("frontier_eth_{cache_name}_hits"),
105 format!("Hits of eth {cache_name} cache."),
106 )?,
107 registry,
108 )?,
109 miss: prometheus_endpoint::register(
110 prometheus::IntCounter::new(
111 format!("frontier_eth_{cache_name}_miss"),
112 format!("Misses of eth {cache_name} cache."),
113 )?,
114 registry,
115 )?,
116 size: prometheus_endpoint::register(
117 prometheus_endpoint::Gauge::new(
118 format!("frontier_eth_{cache_name}_size"),
119 format!("Size of eth {cache_name} data cache."),
120 )?,
121 registry,
122 )?,
123 })
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn test_size_limit() {
133 let mut cache = LRUCacheByteLimited::new("name", 10, None);
134 cache.put(0, "abcd");
135 assert!(cache.get(&0).is_some());
136 cache.put(1, "efghij");
137 assert!(cache.get(&1).is_some());
138 cache.put(2, "k");
139 assert!(cache.get(&2).is_some());
140 assert!(cache.get(&0).is_none());
142 cache.put(3, "lmn");
144 assert!(cache.get(&3).is_some());
145 }
146}