Skip to main content

tuwunel_database/engine/
memory_usage.rs

1use std::{ffi::CStr, fmt::Write};
2
3use rocksdb::perf::get_memory_usage_stats;
4use tuwunel_core::{Result, implement};
5
6use super::{
7	Engine,
8	context::{ColCache, SHARED_POOL},
9};
10use crate::or_else;
11
12/// Per-CF byte count of the block cache the CF is using. Returns the same
13/// value for every participant of a shared pool.
14const CACHE_CAPACITY_PROPERTY: &CStr = c"rocksdb.block-cache-capacity";
15
16fn mib(input: u64) -> f64 { f64::from(u32::try_from(input / 1024).unwrap_or(0)) / 1024.0 }
17
18#[implement(Engine)]
19pub fn memory_usage(&self) -> Result<String> {
20	let mut res = String::new();
21	let row_cache = self.ctx.row_cache.lock()?;
22	let row_usage = u64::try_from(row_cache.get_usage())?;
23	let row_capacity = u64::try_from(self.ctx.row_cache_capacity)?;
24	let stats =
25		get_memory_usage_stats(Some(&[&self.db]), Some(&[&*row_cache])).or_else(or_else)?;
26
27	writeln!(res, "- Memory buffers: {:.2} MiB", mib(stats.mem_table_total))?;
28	writeln!(res, "- Pending write:  {:.2} MiB", mib(stats.mem_table_unflushed))?;
29	writeln!(res, "- Table readers:  {:.2} MiB", mib(stats.mem_table_readers_total))?;
30	writeln!(
31		res,
32		"- Row cache:      {:.2} / {:.2} MiB ({:.1}%)",
33		mib(row_usage),
34		mib(row_capacity),
35		utilization_percent(row_usage, row_capacity),
36	)?;
37
38	drop(row_cache);
39
40	let pools = self.ctx.col_cache.lock()?;
41	if pools.is_empty() {
42		return Ok(res);
43	}
44
45	writeln!(res, "\n```")?;
46	writeln!(
47		res,
48		"{:<34}  {:>11}  {:>14}  {:>8}  {:>12}  {:>3}",
49		"POOL", "USAGE (MiB)", "CAPACITY (MiB)", "UTIL (%)", "PINNED (MiB)", "CFS",
50	)?;
51
52	for (name, pool) in &*pools {
53		self.write_pool(&mut res, name, pool)?;
54	}
55	writeln!(res, "```")?;
56
57	Ok(res)
58}
59
60#[implement(Engine)]
61fn write_pool(&self, out: &mut String, name: &str, pool: &ColCache) -> Result {
62	let label = if name == SHARED_POOL { "Shared" } else { name };
63	let pinned = u64::try_from(pool.cache.get_pinned_usage())?;
64	let usage = u64::try_from(pool.cache.get_usage())?;
65	let capacity = pool
66		.participants
67		.first()
68		.copied()
69		.map(|cf_name| self.cf(cf_name))
70		.and_then(|cf| {
71			self.property_integer(&cf, CACHE_CAPACITY_PROPERTY)
72				.ok()
73		})
74		.unwrap_or(0);
75
76	writeln!(
77		out,
78		"{label:<34}  {:>11.2}  {:>14.2}  {:>8.1}  {:>12.2}  {:>3}",
79		mib(usage),
80		mib(capacity),
81		utilization_percent(usage, capacity),
82		mib(pinned),
83		pool.participants.len(),
84	)?;
85
86	Ok(())
87}
88
89#[expect(clippy::as_conversions, clippy::cast_precision_loss)]
90fn utilization_percent(usage: u64, capacity: u64) -> f64 {
91	if capacity == 0 {
92		return 0.0;
93	}
94
95	(usage as f64 / capacity as f64) * 100.0
96}