]>
Commit | Line | Data |
---|---|---|
09340f28 DM |
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 | ||
d1c3bc53 | 9 | use proxmox_rrd_api_types::{RRDMode, RRDTimeFrameResolution}; |
09340f28 | 10 | |
01917593 | 11 | use crate::{DST, rrd::RRD}; |
09340f28 DM |
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 | } | |
09340f28 DM |
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 | ||
a9730135 | 62 | create_path(path.parent().unwrap(), Some(self.dir_options.clone()), Some(self.file_options.clone()))?; |
09340f28 DM |
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 | } | |
09340f28 | 107 | } |