]> git.proxmox.com Git - proxmox-backup.git/blob - proxmox-rrd/src/cache.rs
proxmox-rrd: improve developer docs
[proxmox-backup.git] / proxmox-rrd / src / cache.rs
1 use std::path::{Path, PathBuf};
2 use std::collections::HashMap;
3 use std::sync::{RwLock};
4
5 use anyhow::{format_err, Error};
6
7 use proxmox::tools::fs::{create_path, CreateOptions};
8
9 use proxmox_rrd_api_types::{RRDMode, RRDTimeFrameResolution};
10
11 use crate::{DST, rrd::RRD};
12
13 /// RRD cache - keep RRD data in RAM, but write updates to disk
14 ///
15 /// This cache is designed to run as single instance (no concurrent
16 /// access from other processes).
17 pub struct RRDCache {
18 basedir: PathBuf,
19 file_options: CreateOptions,
20 dir_options: CreateOptions,
21 cache: RwLock<HashMap<String, RRD>>,
22 }
23
24 impl RRDCache {
25
26 /// Creates a new instance
27 pub fn new<P: AsRef<Path>>(
28 basedir: P,
29 file_options: Option<CreateOptions>,
30 dir_options: Option<CreateOptions>,
31 ) -> Self {
32 let basedir = basedir.as_ref().to_owned();
33 Self {
34 basedir,
35 file_options: file_options.unwrap_or_else(|| CreateOptions::new()),
36 dir_options: dir_options.unwrap_or_else(|| CreateOptions::new()),
37 cache: RwLock::new(HashMap::new()),
38 }
39 }
40
41 /// Create rrdd stat dir with correct permission
42 pub fn create_rrdb_dir(&self) -> Result<(), Error> {
43
44 create_path(&self.basedir, Some(self.dir_options.clone()), Some(self.file_options.clone()))
45 .map_err(|err: Error| format_err!("unable to create rrdb stat dir - {}", err))?;
46
47 Ok(())
48 }
49
50 /// Update data in RAM and write file back to disk (if `save` is set)
51 pub fn update_value(
52 &self,
53 rel_path: &str,
54 value: f64,
55 dst: DST,
56 save: bool,
57 ) -> Result<(), Error> {
58
59 let mut path = self.basedir.clone();
60 path.push(rel_path);
61
62 create_path(path.parent().unwrap(), Some(self.dir_options.clone()), Some(self.file_options.clone()))?;
63
64 let mut map = self.cache.write().unwrap();
65 let now = proxmox::tools::time::epoch_f64();
66
67 if let Some(rrd) = map.get_mut(rel_path) {
68 rrd.update(now, value);
69 if save { rrd.save(&path, self.file_options.clone())?; }
70 } else {
71 let mut rrd = match RRD::load(&path) {
72 Ok(rrd) => rrd,
73 Err(err) => {
74 if err.kind() != std::io::ErrorKind::NotFound {
75 eprintln!("overwriting RRD file {:?}, because of load error: {}", path, err);
76 }
77 RRD::new(dst)
78 },
79 };
80 rrd.update(now, value);
81 if save {
82 rrd.save(&path, self.file_options.clone())?;
83 }
84 map.insert(rel_path.into(), rrd);
85 }
86
87 Ok(())
88 }
89
90 /// Extract data from cached RRD
91 pub fn extract_cached_data(
92 &self,
93 base: &str,
94 name: &str,
95 now: f64,
96 timeframe: RRDTimeFrameResolution,
97 mode: RRDMode,
98 ) -> Option<(u64, u64, Vec<Option<f64>>)> {
99
100 let map = self.cache.read().unwrap();
101
102 match map.get(&format!("{}/{}", base, name)) {
103 Some(rrd) => Some(rrd.extract_data(now, timeframe, mode)),
104 None => None,
105 }
106 }
107 }