miden_node_store/state/
disk_monitor.rs1use std::path::Path;
2use std::time::Duration;
3
4use miden_node_utils::spawn::spawn_blocking_in_span;
5use miden_node_utils::tracing::OpenTelemetrySpanExt;
6use tracing::info_span;
7
8use crate::COMPONENT;
9use crate::state::State;
10
11impl State {
12 pub fn spawn_disk_monitor(&self) -> tokio::task::JoinHandle<()> {
15 let data_directory = self.data_directory.clone();
16
17 tokio::spawn(async move {
18 let mut interval = tokio::time::interval(Duration::from_mins(5));
19 loop {
20 interval.tick().await;
21 let dir = data_directory.clone();
22 let span = info_span!(target: COMPONENT, "measure_disk_space_usage");
23 let result =
24 spawn_blocking_in_span(move || measure_disk_usage_bytes(&dir), span.clone())
25 .await;
26 match result {
27 Ok(usage) => {
28 span.set_attribute("db.sqlite.size", usage.sqlite_db);
29 span.set_attribute("db.sqlite.wal.size", usage.sqlite_wal);
30 span.set_attribute("db.block_store.size", usage.block_store);
31 #[cfg(feature = "rocksdb")]
32 {
33 span.set_attribute("db.account_tree.size", usage.account_tree);
34 span.set_attribute("db.nullifier_tree.size", usage.nullifier_tree);
35 span.set_attribute(
36 "db.account_state_forest.size",
37 usage.account_state_forest,
38 );
39 }
40 },
41 Err(err) => span.set_error(&err),
42 }
43 }
44 })
45 }
46}
47
48struct DiskUsage {
50 sqlite_db: u64,
51 sqlite_wal: u64,
52 block_store: u64,
53 #[cfg(feature = "rocksdb")]
54 account_tree: u64,
55 #[cfg(feature = "rocksdb")]
56 nullifier_tree: u64,
57 #[cfg(feature = "rocksdb")]
58 account_state_forest: u64,
59}
60
61fn measure_disk_usage_bytes(data_dir: &Path) -> DiskUsage {
63 DiskUsage {
64 sqlite_db: path_size_bytes(&data_dir.join("miden-store.sqlite3")),
65 sqlite_wal: path_size_bytes(&data_dir.join("miden-store.sqlite3-wal")),
66 block_store: dir_size_bytes(&data_dir.join("blocks")),
67 #[cfg(feature = "rocksdb")]
68 account_tree: dir_size_bytes(&data_dir.join("accounttree")),
69 #[cfg(feature = "rocksdb")]
70 nullifier_tree: dir_size_bytes(&data_dir.join("nullifiertree")),
71 #[cfg(feature = "rocksdb")]
72 account_state_forest: dir_size_bytes(&data_dir.join("accountstateforest")),
73 }
74}
75
76fn path_size_bytes(path: &Path) -> u64 {
78 fs_err::metadata(path).map(|m| m.len()).unwrap_or(0)
79}
80
81fn dir_size_bytes(path: &Path) -> u64 {
83 let mut to_process = vec![path.to_path_buf()];
84 let mut total = 0u64;
85 while let Some(dir) = to_process.pop() {
86 let Ok(entries) = fs_err::read_dir(&dir) else {
87 continue;
88 };
89 for entry in entries.flatten() {
90 if let Ok(meta) = entry.metadata() {
91 if meta.is_dir() {
92 to_process.push(entry.path());
93 } else {
94 total += meta.len();
95 }
96 }
97 }
98 }
99 total
100}