sc_storage_monitor/
lib.rs1use clap::Args;
20use sp_core::traits::SpawnEssentialNamed;
21use std::{
22 io,
23 path::{Path, PathBuf},
24 time::Duration,
25};
26
27const LOG_TARGET: &str = "storage-monitor";
28
29pub type Result<T> = std::result::Result<T, Error>;
31
32#[derive(Debug, thiserror::Error)]
34pub enum Error {
35 #[error("IO Error")]
36 IOError(#[from] io::Error),
37 #[error("Out of storage space: available {0}MiB, required {1}MiB")]
38 StorageOutOfSpace(u64, u64),
39}
40
41#[derive(Default, Debug, Clone, Args)]
43pub struct StorageMonitorParams {
44 #[arg(long = "db-storage-threshold", value_name = "MiB", default_value_t = 1024)]
51 pub threshold: u64,
52
53 #[arg(long = "db-storage-polling-period", value_name = "SECONDS", default_value_t = 5, value_parser = clap::value_parser!(u32).range(1..))]
55 pub polling_period: u32,
56}
57
58pub struct StorageMonitorService {
60 path: PathBuf,
62 threshold: u64,
64 polling_period: Duration,
66}
67
68impl StorageMonitorService {
69 pub fn try_spawn(
71 parameters: StorageMonitorParams,
72 path: PathBuf,
73 spawner: &impl SpawnEssentialNamed,
74 ) -> Result<()> {
75 if parameters.threshold == 0 {
76 log::info!(
77 target: LOG_TARGET,
78 "StorageMonitorService: threshold `0` given, storage monitoring disabled",
79 );
80 } else {
81 log::debug!(
82 target: LOG_TARGET,
83 "Initializing StorageMonitorService for db path: {}",
84 path.display()
85 );
86
87 Self::check_free_space(&path, parameters.threshold)?;
88
89 let storage_monitor_service = StorageMonitorService {
90 path,
91 threshold: parameters.threshold,
92 polling_period: Duration::from_secs(parameters.polling_period.into()),
93 };
94
95 spawner.spawn_essential(
96 "storage-monitor",
97 None,
98 Box::pin(storage_monitor_service.run()),
99 );
100 }
101
102 Ok(())
103 }
104
105 async fn run(self) {
108 loop {
109 tokio::time::sleep(self.polling_period).await;
110 if Self::check_free_space(&self.path, self.threshold).is_err() {
111 break
112 };
113 }
114 }
115
116 fn free_space(path: &Path) -> Result<u64> {
118 Ok(fs4::available_space(path).map(|s| s / 1024 / 1024)?)
119 }
120
121 fn check_free_space(path: &Path, threshold: u64) -> Result<()> {
125 match StorageMonitorService::free_space(path) {
126 Ok(available_space) => {
127 log::trace!(
128 target: LOG_TARGET,
129 "free: {available_space} , threshold: {threshold}.",
130 );
131
132 if available_space < threshold {
133 log::error!(target: LOG_TARGET, "Available space {available_space}MiB for path `{}` dropped below threshold: {threshold}MiB , terminating...", path.display());
134 Err(Error::StorageOutOfSpace(available_space, threshold))
135 } else {
136 Ok(())
137 }
138 },
139 Err(e) => {
140 log::error!(target: LOG_TARGET, "Could not read available space: {e:?}.");
141 Err(e)
142 },
143 }
144 }
145}