fix(metrics): Read database memory gauges without a cache

This commit is contained in:
Erwan Leboucher
2026-10-12 10:03:15 +01:00
committed by Jade Ellis
parent ea3ec965ed
commit 698edda6bd
4 changed files with 18 additions and 48 deletions
-1
View File
@@ -6,7 +6,6 @@
mod files;
mod logger;
mod memory_usage;
pub use memory_usage::MemoryUsage;
mod open;
mod repair;
+11 -11
View File
@@ -10,13 +10,13 @@ pub struct MemoryUsage {
pub mem_table_unflushed: u64,
pub mem_table_readers_total: u64,
pub row_cache: u64,
pub col_caches: Vec<(String, u64)>,
}
impl super::Engine {
pub fn memory_usage(&self) -> Result<String> {
let mut res = String::new();
let stats = self.memory_usage_stats()?;
let col_caches = self.col_cache_usage()?;
let mibs = |input| f64::from(u32::try_from(input / 1024).unwrap_or(0)) / 1024.0;
writeln!(
res,
@@ -28,7 +28,7 @@ pub fn memory_usage(&self) -> Result<String> {
mibs(stats.row_cache),
)?;
for (name, usage) in stats.col_caches {
for (name, usage) in col_caches {
writeln!(res, "{name} cache: {:.2} MiB", mibs(usage))?;
}
@@ -40,20 +40,20 @@ pub fn memory_usage_stats(&self) -> Result<MemoryUsage> {
get_memory_usage_stats(Some(&[&self.db]), Some(&[&*self.ctx.row_cache.lock()]))
.or_else(or_else)?;
let col_caches = self
.ctx
.col_cache
.lock()
.iter()
.map(|(name, cache)| Ok((name.clone(), u64::try_from(cache.get_usage())?)))
.collect::<Result<_>>()?;
Ok(MemoryUsage {
mem_table_total: stats.mem_table_total,
mem_table_unflushed: stats.mem_table_unflushed,
mem_table_readers_total: stats.mem_table_readers_total,
row_cache: u64::try_from(self.ctx.row_cache.lock().get_usage())?,
col_caches,
})
}
pub fn col_cache_usage(&self) -> Result<Vec<(String, u64)>> {
self.ctx
.col_cache
.lock()
.iter()
.map(|(name, cache)| Ok((name.clone(), u64::try_from(cache.get_usage())?)))
.collect()
}
}
-1
View File
@@ -33,7 +33,6 @@
pub use self::{
de::{Ignore, IgnoreAll},
deserialized::Deserialized,
engine::MemoryUsage,
handle::Handle,
keyval::{KeyVal, Slice, serialize_key, serialize_val},
map::{Get, Map, Qry, compact},
+7 -35
View File
@@ -1,20 +1,12 @@
use std::{
sync::{Arc, Mutex},
time::{Duration, Instant},
};
use std::sync::Arc;
use conduwuit::{
metrics::{AsyncInstrument, KeyValue},
utils::millis_since_unix_epoch,
};
use database::MemoryUsage;
use crate::Services;
/// Both database memory gauges are collected in the same scrape, so the
/// statistics are shared between them for this long.
const MEMORY_USAGE_TTL: Duration = Duration::from_secs(1);
impl Services {
pub(crate) fn register_metrics(self: &Arc<Self>) {
self.gauge(
@@ -80,14 +72,12 @@ pub(crate) fn register_metrics(self: &Arc<Self>) {
},
);
let memory = Arc::new(MemoryUsageCache::default());
let block_cache_memory = memory.clone();
self.gauge(
"continuwuity.database.memory",
"RocksDB memory usage by component",
"By",
move |services, observer| {
let Some(usage) = memory.get(services) else {
|services, observer| {
let Ok(usage) = services.db.db.memory_usage_stats() else {
return;
};
@@ -106,13 +96,13 @@ pub(crate) fn register_metrics(self: &Arc<Self>) {
"continuwuity.database.block_cache",
"RocksDB block cache usage by column family",
"By",
move |services, observer| {
let Some(usage) = block_cache_memory.get(services) else {
|services, observer| {
let Ok(col_caches) = services.db.db.col_cache_usage() else {
return;
};
for (column, bytes) in &usage.col_caches {
observer.observe(*bytes, &[KeyValue::new("column", column.clone())]);
for (column, bytes) in col_caches {
observer.observe(bytes, &[KeyValue::new("column", column)]);
}
},
);
@@ -173,24 +163,6 @@ fn gauge<F>(
}
}
#[derive(Default)]
struct MemoryUsageCache(Mutex<Option<(Instant, Arc<MemoryUsage>)>>);
impl MemoryUsageCache {
fn get(&self, services: &Services) -> Option<Arc<MemoryUsage>> {
let mut cached = self.0.lock().expect("locked");
if let Some((at, usage)) = &*cached {
if at.elapsed() < MEMORY_USAGE_TTL {
return Some(usage.clone());
}
}
let usage = Arc::new(services.db.db.memory_usage_stats().ok()?);
*cached = Some((Instant::now(), usage.clone()));
Some(usage)
}
}
/// Cheap estimate from RocksDB properties, unlike iterating the column.
fn estimate_keys(services: &Services, map: &'static str) -> Option<u64> {
services