soil_client/storage_monitor/
mod.rs1use clap::Args;
8use std::{
9 io,
10 path::{Path, PathBuf},
11 time::Duration,
12};
13use subsoil::core::traits::SpawnEssentialNamed;
14
15const LOG_TARGET: &str = "storage-monitor";
16
17pub type Result<T> = std::result::Result<T, Error>;
19
20#[derive(Debug, thiserror::Error)]
22pub enum Error {
23 #[error("IO Error")]
24 IOError(#[from] io::Error),
25 #[error("Out of storage space: available {0}MiB, required {1}MiB")]
26 StorageOutOfSpace(u64, u64),
27}
28
29#[derive(Default, Debug, Clone, Args)]
31pub struct StorageMonitorParams {
32 #[arg(long = "db-storage-threshold", value_name = "MiB", default_value_t = 1024)]
39 pub threshold: u64,
40
41 #[arg(long = "db-storage-polling-period", value_name = "SECONDS", default_value_t = 5, value_parser = clap::value_parser!(u32).range(1..))]
43 pub polling_period: u32,
44}
45
46pub struct StorageMonitorService {
48 path: PathBuf,
50 threshold: u64,
52 polling_period: Duration,
54}
55
56impl StorageMonitorService {
57 pub fn try_spawn(
59 parameters: StorageMonitorParams,
60 path: PathBuf,
61 spawner: &impl SpawnEssentialNamed,
62 ) -> Result<()> {
63 if parameters.threshold == 0 {
64 log::info!(
65 target: LOG_TARGET,
66 "StorageMonitorService: threshold `0` given, storage monitoring disabled",
67 );
68 } else {
69 log::debug!(
70 target: LOG_TARGET,
71 "Initializing StorageMonitorService for db path: {}",
72 path.display()
73 );
74
75 Self::check_free_space(&path, parameters.threshold)?;
76
77 let storage_monitor_service = StorageMonitorService {
78 path,
79 threshold: parameters.threshold,
80 polling_period: Duration::from_secs(parameters.polling_period.into()),
81 };
82
83 spawner.spawn_essential(
84 "storage-monitor",
85 None,
86 Box::pin(storage_monitor_service.run()),
87 );
88 }
89
90 Ok(())
91 }
92
93 async fn run(self) {
96 loop {
97 tokio::time::sleep(self.polling_period).await;
98 if Self::check_free_space(&self.path, self.threshold).is_err() {
99 break;
100 };
101 }
102 }
103
104 fn free_space(path: &Path) -> Result<u64> {
106 Ok(fs4::available_space(path).map(|s| s / 1024 / 1024)?)
107 }
108
109 fn check_free_space(path: &Path, threshold: u64) -> Result<()> {
113 match StorageMonitorService::free_space(path) {
114 Ok(available_space) => {
115 log::trace!(
116 target: LOG_TARGET,
117 "free: {available_space} , threshold: {threshold}.",
118 );
119
120 if available_space < threshold {
121 log::error!(target: LOG_TARGET, "Available space {available_space}MiB for path `{}` dropped below threshold: {threshold}MiB , terminating...", path.display());
122 Err(Error::StorageOutOfSpace(available_space, threshold))
123 } else {
124 Ok(())
125 }
126 },
127 Err(e) => {
128 log::error!(target: LOG_TARGET, "Could not read available space: {e:?}.");
129 Err(e)
130 },
131 }
132 }
133}