]> git.proxmox.com Git - proxmox-backup.git/blob - src/rrd/cache.rs
rrd: reduce io by saving data only once a minute
[proxmox-backup.git] / src / rrd / cache.rs
1 use std::time::{SystemTime, UNIX_EPOCH};
2 use std::path::PathBuf;
3 use std::collections::HashMap;
4 use std::sync::{RwLock};
5
6 use anyhow::{format_err, Error};
7 use lazy_static::lazy_static;
8 use serde_json::{json, Value};
9
10 use proxmox::tools::fs::{create_path, CreateOptions};
11
12 use crate::api2::types::{RRDMode, RRDTimeFrameResolution};
13
14 use super::*;
15
16 const PBS_RRD_BASEDIR: &str = "/var/lib/proxmox-backup/rrdb";
17
18 lazy_static!{
19 static ref RRD_CACHE: RwLock<HashMap<String, RRD>> = {
20 RwLock::new(HashMap::new())
21 };
22 }
23
24 /// Create rrdd stat dir with correct permission
25 pub fn create_rrdb_dir() -> Result<(), Error> {
26
27 let backup_user = crate::backup::backup_user()?;
28 let opts = CreateOptions::new()
29 .owner(backup_user.uid)
30 .group(backup_user.gid);
31
32 create_path(PBS_RRD_BASEDIR, None, Some(opts))
33 .map_err(|err: Error| format_err!("unable to create rrdb stat dir - {}", err))?;
34
35 Ok(())
36 }
37
38 fn now() -> Result<f64, Error> {
39 let time = SystemTime::now().duration_since(UNIX_EPOCH)?;
40 Ok(time.as_secs_f64())
41 }
42
43 pub fn update_value(rel_path: &str, value: f64, dst: DST, save: bool) -> Result<(), Error> {
44
45 let mut path = PathBuf::from(PBS_RRD_BASEDIR);
46 path.push(rel_path);
47
48 std::fs::create_dir_all(path.parent().unwrap())?;
49
50 let mut map = RRD_CACHE.write().unwrap();
51 let now = now()?;
52
53 if let Some(rrd) = map.get_mut(rel_path) {
54 rrd.update(now, value);
55 if save { rrd.save(&path)?; }
56 } else {
57 let mut rrd = match RRD::load(&path) {
58 Ok(rrd) => rrd,
59 Err(err) => {
60 if err.kind() != std::io::ErrorKind::NotFound {
61 eprintln!("overwriting RRD file {:?}, because of load error: {}", path, err);
62 }
63 RRD::new(dst)
64 },
65 };
66 rrd.update(now, value);
67 if save { rrd.save(&path)?; }
68 map.insert(rel_path.into(), rrd);
69 }
70
71 Ok(())
72 }
73
74 pub fn extract_data(
75 base: &str,
76 items: &[&str],
77 timeframe: RRDTimeFrameResolution,
78 mode: RRDMode,
79 ) -> Result<Value, Error> {
80
81 let now = now()?;
82
83 let map = RRD_CACHE.read().unwrap();
84
85 let mut result = Vec::new();
86
87 for name in items.iter() {
88 let rrd = match map.get(&format!("{}/{}", base, name)) {
89 Some(rrd) => rrd,
90 None => continue,
91 };
92 let (start, reso, list) = rrd.extract_data(now, timeframe, mode);
93 let mut t = start;
94 for index in 0..RRD_DATA_ENTRIES {
95 if result.len() <= index {
96 if let Some(value) = list[index] {
97 result.push(json!({ "time": t, *name: value }));
98 } else {
99 result.push(json!({ "time": t }));
100 }
101 } else {
102 if let Some(value) = list[index] {
103 result[index][name] = value.into();
104 }
105 }
106 t += reso;
107 }
108 }
109
110 Ok(result.into())
111 }