mirror of
https://forgejo.ellis.link/continuwuation/continuwuity/
synced 2026-10-15 09:19:58 +00:00
fix(metrics): Read database memory gauges without a cache
This commit is contained in:
@@ -6,7 +6,6 @@
|
||||
mod files;
|
||||
mod logger;
|
||||
mod memory_usage;
|
||||
pub use memory_usage::MemoryUsage;
|
||||
mod open;
|
||||
mod repair;
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user