Skip to content

Commit abcf22d

Browse files
authored
Added background scan option (#7)
* Added background thread option * added startup block to avoid returning empty data * Updated README
1 parent 1e168e5 commit abcf22d

File tree

6 files changed

+71
-9
lines changed

6 files changed

+71
-9
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "prometheus_folder_size_exporter"
3-
version = "0.3.0"
3+
version = "0.4.0"
44
authors = ["Francesco Cogno <francesco.cogno@outlook.com>", "Guido Scatena <guido.scatena@unipi.it>"]
55
description = "Prometheus Folder Size Exporter"
66
edition = "2018"

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Start the binary with `-h` to get the complete syntax. The parameters are:
4141
| `-v` | no | <switch> | | Enable verbose mode.
4242
| `-p` | no | any valid port number | 9974 | Specify the serivce port. This is the port your Prometheus instance should point to.
4343
| `-i` | yes | a valid config json file | - | The configuration file. This json is detailed below (you can find an example here: [example.json](example.json)).
44+
| `-b` | no | Any number > 0 | Off | Enables the async storage calculation. The value specifies how often the calculation will be done. If not specified, the values will be calculated synchronously at each HTTP Get.
4445

4546
Once started, the tool will listen on the specified port (or the default one, 9974, if not specified) and return a Prometheus valid response at the url `/metrics`. So to check if the tool is working properly simply browse the `http://localhost:9974` (or whichever port you choose).
4647

src/main.rs

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,33 @@ use hyper::Body;
77
use std::env;
88
use std::sync::Arc;
99
mod options;
10+
mod state;
1011
use options::Options;
12+
pub use state::State;
1113
mod exporter_error;
1214
mod folder_scanner;
1315
mod render_to_prometheus;
1416
use prometheus_exporter_base::prelude::*;
17+
use std::time::Duration;
1518

1619
async fn perform_request(
1720
_req: http::request::Request<Body>,
18-
options: Arc<Options>,
21+
state: Arc<Arc<State>>,
1922
) -> Result<String, failure::Error> {
20-
let results = options.folders_to_scan.scan()?;
23+
let results = if state.options.background_poll_seconds.is_some() {
24+
// loop until we have some data.
25+
// This is needed because the first scan can take a lot of time
26+
// and we must block until we have something. It will happen only
27+
// at startup though.
28+
let mut results = state.shared_vec.read().unwrap().clone();
29+
while results.is_empty() {
30+
std::thread::sleep(Duration::from_millis(100));
31+
results = state.shared_vec.read().unwrap().clone();
32+
}
33+
results
34+
} else {
35+
state.options.folders_to_scan.scan()?
36+
};
2137

2238
let mut metric = PrometheusMetric::build()
2339
.with_name("folder_size")
@@ -28,7 +44,7 @@ async fn perform_request(
2844
.build();
2945

3046
for result in results {
31-
if let Some(user) = result.folder.user {
47+
if let Some(user) = &result.folder.user {
3248
metric.render_and_append_instance(
3349
&PrometheusInstance::new()
3450
.with_label("path", result.folder.path.as_str())
@@ -88,27 +104,48 @@ async fn main() {
88104
.help("verbose logging")
89105
.takes_value(false),
90106
)
107+
.arg(
108+
Arg::with_name("background_poll_seconds")
109+
.short("b")
110+
.help("enables background scanning every <sec> seconds")
111+
.required(false)
112+
.takes_value(true),
113+
)
91114
.get_matches();
92115

93116
let options = Options::from_claps(&matches);
94-
95117
if options.verbose {
96118
env::set_var("RUST_LOG", "prometheus_folder_size_exporter=trace");
97119
} else {
98120
env::set_var("RUST_LOG", "prometheus_folder_size_exporter=info");
99121
}
100122
env_logger::init();
101-
102123
log::info!("using options: {:?}", options);
103124

125+
let state = Arc::new(State::new(options));
126+
127+
// start the background thread only
128+
// if requested
129+
if let Some(background_poll_seconds) = state.options.background_poll_seconds {
130+
let state = state.clone();
131+
std::thread::spawn(move || loop {
132+
log::info!("starting background folder structure update");
133+
let results = state.options.folders_to_scan.scan().unwrap();
134+
*state.shared_vec.write().unwrap() = results;
135+
log::info!("background folder structure update completed");
136+
137+
std::thread::sleep(background_poll_seconds);
138+
});
139+
}
140+
104141
let bind = matches.value_of("port").unwrap();
105142
let bind = u16::from_str_radix(&bind, 10).expect("port must be a valid number");
106143
let addr = ([0, 0, 0, 0], bind).into();
107144

108145
log::info!("starting exporter on {}", addr);
109146

110-
prometheus_exporter_base::render_prometheus(addr, options, |request, options| {
111-
Box::pin(perform_request(request, options))
147+
prometheus_exporter_base::render_prometheus(addr, state, |request, state| {
148+
Box::pin(perform_request(request, state))
112149
})
113150
.await
114151
}

src/options.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@ pub(crate) struct Options {
77
pub folders_file: String,
88
pub verbose: bool,
99
pub folders_to_scan: FolderScanner,
10+
pub background_poll_seconds: Option<std::time::Duration>,
1011
}
1112

1213
impl Options {
1314
pub fn from_claps(matches: &clap::ArgMatches<'_>) -> Options {
1415
let folders_file = matches.value_of("folders_file").unwrap().to_owned();
16+
let background_poll_seconds = matches
17+
.value_of("background_poll_seconds")
18+
.map(|s| std::time::Duration::from_secs(s.parse().unwrap()));
1519

1620
let mut file = File::open(&folders_file).unwrap();
1721
let mut file_contents = String::new();
@@ -22,6 +26,7 @@ impl Options {
2226
folders_file,
2327
verbose: matches.is_present("verbose"),
2428
folders_to_scan: FolderScanner::from_json(&file_contents).unwrap(),
29+
background_poll_seconds,
2530
}
2631
}
2732
}

src/state.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use crate::folder_scanner::FolderWithSize;
2+
use crate::Options;
3+
use std::sync::Arc;
4+
use std::sync::RwLock;
5+
6+
#[derive(Debug, Clone)]
7+
pub struct State {
8+
pub(crate) options: Options,
9+
pub(crate) shared_vec: Arc<RwLock<Vec<FolderWithSize>>>,
10+
}
11+
12+
impl State {
13+
pub(crate) fn new(options: Options) -> Self {
14+
Self {
15+
options,
16+
shared_vec: Arc::new(RwLock::new(Vec::new())),
17+
}
18+
}
19+
}

0 commit comments

Comments
 (0)