miden_node_store/state/
disk_monitor.rs1use std::path::{Path, PathBuf};
2use std::time::Duration;
3
4use miden_node_utils::shutdown::CancellationToken;
5use miden_node_utils::spawn::spawn_blocking_in_current_span;
6use miden_node_utils::tracing::{miden_instrument, miden_span_record};
7
8use crate::COMPONENT;
9use crate::state::State;
10
11impl State {
12 pub fn spawn_disk_monitor(&self, shutdown: CancellationToken) -> 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 tokio::select! {
21 () = shutdown.cancelled() => return,
22 _ = interval.tick() => {},
23 }
24 let _ = measure_disk_space_usage(data_directory.clone()).await;
25 }
26 })
27 }
28}
29
30#[miden_instrument(
31 target = COMPONENT,
32 name = "measure_disk_space_usage",
33 skip_all,
34 err,
35)]
36async fn measure_disk_space_usage(data_dir: PathBuf) -> Result<(), tokio::task::JoinError> {
37 let usage = spawn_blocking_in_current_span(move || measure_disk_usage_bytes(&data_dir)).await?;
38 miden_span_record!(
39 db.sqlite.size = usage.sqlite_db,
40 db.sqlite.wal.size = usage.sqlite_wal,
41 db.block_store.size = usage.block_store,
42 );
43 #[cfg(feature = "rocksdb")]
44 {
45 miden_span_record!(
46 db.account_tree.size = usage.account_tree,
47 db.nullifier_tree.size = usage.nullifier_tree,
48 db.account_state_forest.size = usage.account_state_forest,
49 );
50 }
51
52 Ok(())
53}
54
55struct DiskUsage {
57 sqlite_db: u64,
58 sqlite_wal: u64,
59 block_store: u64,
60 #[cfg(feature = "rocksdb")]
61 account_tree: u64,
62 #[cfg(feature = "rocksdb")]
63 nullifier_tree: u64,
64 #[cfg(feature = "rocksdb")]
65 account_state_forest: u64,
66}
67
68fn measure_disk_usage_bytes(data_dir: &Path) -> DiskUsage {
70 DiskUsage {
71 sqlite_db: path_size_bytes(&data_dir.join("miden-store.sqlite3")),
72 sqlite_wal: path_size_bytes(&data_dir.join("miden-store.sqlite3-wal")),
73 block_store: dir_size_bytes(&data_dir.join("blocks")),
74 #[cfg(feature = "rocksdb")]
75 account_tree: dir_size_bytes(&data_dir.join("accounttree")),
76 #[cfg(feature = "rocksdb")]
77 nullifier_tree: dir_size_bytes(&data_dir.join("nullifiertree")),
78 #[cfg(feature = "rocksdb")]
79 account_state_forest: dir_size_bytes(&data_dir.join("accountstateforest")),
80 }
81}
82
83fn path_size_bytes(path: &Path) -> u64 {
85 fs_err::metadata(path).map(|m| m.len()).unwrap_or(0)
86}
87
88fn dir_size_bytes(path: &Path) -> u64 {
90 let mut to_process = vec![path.to_path_buf()];
91 let mut total = 0u64;
92 while let Some(dir) = to_process.pop() {
93 let Ok(entries) = fs_err::read_dir(&dir) else {
94 continue;
95 };
96 for entry in entries.flatten() {
97 if let Ok(meta) = entry.metadata() {
98 if meta.is_dir() {
99 to_process.push(entry.path());
100 } else {
101 total += meta.len();
102 }
103 }
104 }
105 }
106 total
107}