Skip to content

Commit

Permalink
Optionally keep Hit/Miss metric using prometheus
Browse files Browse the repository at this point in the history
  • Loading branch information
RCasatta committed Sep 24, 2024
1 parent 6dc5e1a commit df1b4ad
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 3 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ sha2 = { version = "0.10", optional = true }
bitcoin = { version = "0.32.0", optional = true }
redb = { version = "1.0", optional = true }
hashbrown = { version = "0.14", optional = true }
prometheus = { version = "0.13.4", optional = true }

[features]
default = []
Expand All @@ -25,6 +26,7 @@ sha2 = ["dep:sha2"]
redb = ["dep:redb"]
bitcoin = ["dep:bitcoin", "bitcoin_hashes"]
slice_cache = ["dep:hashbrown"]
prometheus = ["dep:prometheus", "slice_cache"]

[dev-dependencies]
hex_lit = { version = "0.1", features = ["rust_v_1_46"] }
Expand Down
62 changes: 59 additions & 3 deletions src/slice_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ pub struct SliceCache<K: Hash + PartialEq + Eq + core::fmt::Debug> {
/// The cache is full, at least once it removed an older element to insert a new one.
/// Obviously elements can still be inserted but they may remove older elements.
full: bool,

#[cfg(feature = "prometheus")]
metric: prometheus::IntCounterVec,
}

mod private {
Expand Down Expand Up @@ -96,8 +99,17 @@ impl<K: Hash + PartialEq + Eq + core::fmt::Debug> SliceCache<K> {
indexes: HashMap::new(),
insertions: VecDeque::new(),
full: false,

// TODO: metric name should be parametrized
#[cfg(feature = "prometheus")]
metric: prometheus::IntCounterVec::new(
prometheus::Opts::new("slice_cache", "Counters for cache Hit/Miss"),
&["event"],
)
.expect("statically defined"),
}
}

/// Insert a value V in the cache, with key K
/// returns the number of old entries removed
pub fn insert<V: AsRef<[u8]>>(&mut self, key: K, value: &V) -> Result<usize, Error> {
Expand Down Expand Up @@ -139,7 +151,20 @@ impl<K: Hash + PartialEq + Eq + core::fmt::Debug> SliceCache<K> {

/// Get the value as slice at key `K` if exist in the cache, `None` otherwise
pub fn get(&self, key: &K) -> Option<&[u8]> {
let index = self.indexes.get(key)?;
let index = match self.indexes.get(key) {
Some(val) => {
#[cfg(feature = "prometheus")]
self.metric.with_label_values(&["hit"]).inc();

val
}
None => {
#[cfg(feature = "prometheus")]
self.metric.with_label_values(&["miss"]).inc();

return None;
}
};

Some(&self.buffer[index.begin()..index.end()])
}
Expand All @@ -152,8 +177,8 @@ impl<K: Hash + PartialEq + Eq + core::fmt::Debug> SliceCache<K> {
#[cfg(feature = "redb")]
/// Get the value at key `K` if exist in the cache, `None` otherwise
pub fn get_value<'a, V: redb::RedbValue>(&'a self, key: &K) -> Option<V::SelfType<'a>> {
let index = self.indexes.get(key)?;
let value = V::from_bytes(&self.buffer[index.begin()..index.end()]);
let slice = self.get(key)?;
let value = V::from_bytes(slice);

Some(value)
}
Expand Down Expand Up @@ -187,6 +212,12 @@ impl<K: Hash + PartialEq + Eq + core::fmt::Debug> SliceCache<K> {
}
removed
}

#[cfg(feature = "prometheus")]
/// Register the inner metric for hit/cache in the prometheus registry
pub fn register_metric(&self, r: &prometheus::Registry) -> Result<(), prometheus::Error> {
r.register(Box::new(self.metric.clone()))
}
}

#[cfg(test)]
Expand Down Expand Up @@ -240,6 +271,31 @@ mod tests {
println!("{:?}", cache.insertions);
}

#[cfg(feature = "prometheus")]
#[test]
fn prometheus() {
use prometheus::Encoder;

let r = prometheus::default_registry();

let mut cache = SliceCache::new(10);
cache.register_metric(&r).unwrap();

let k1 = 0;
let v1 = [1, 2];
cache.insert(k1, &v1).unwrap();
assert_eq!(cache.get(&k1), Some(&v1[..]));
assert_eq!(cache.get(&1), None);

let mut buffer = Vec::<u8>::new();
let encoder = prometheus::TextEncoder::new();

let metric_families = r.gather();
encoder.encode(&metric_families, &mut buffer).unwrap();
let result = format!("{}", String::from_utf8(buffer.clone()).unwrap());
assert_eq!(result, "# HELP slice_cache Counters for cache Hit/Miss\n# TYPE slice_cache counter\nslice_cache{event=\"hit\"} 1\nslice_cache{event=\"miss\"} 1\n");
}

#[cfg(feature = "bitcoin")]
#[test]
fn with_transaction() {
Expand Down

0 comments on commit df1b4ad

Please sign in to comment.