iota_metric_checker/
query.rs1use anyhow::anyhow;
6use base64::{Engine, engine::general_purpose};
7use prometheus_http_query::Client;
8use reqwest::header::{AUTHORIZATION, HeaderValue};
9use tracing::{debug, info};
10
11use crate::unix_seconds_to_timestamp_string;
12
13pub async fn instant_query(
14 auth_header: &str,
15 client: Client,
16 query: &str,
17) -> Result<f64, anyhow::Error> {
18 debug!("Executing {query}");
19 let response = client
20 .query(query)
21 .header(
22 AUTHORIZATION,
23 HeaderValue::from_str(&format!(
24 "Basic {}",
25 general_purpose::STANDARD.encode(auth_header)
26 ))?,
27 )
28 .get()
29 .await?;
30
31 let result = response
32 .data()
33 .as_vector()
34 .unwrap_or_else(|| panic!("Expected result of type vector for {query}"));
35
36 if !result.is_empty() {
37 let first = result.first().unwrap();
38 debug!("Got value {}", first.sample().value());
39 Ok(first.sample().value())
40 } else {
41 Err(anyhow!(
42 "Did not get expected response from server for {query}"
43 ))
44 }
45}
46
47pub async fn range_query(
50 auth_header: &str,
51 client: Client,
52 query: &str,
53 start: i64,
54 end: i64,
55 step: f64,
56 percentile: u8,
57) -> Result<f64, anyhow::Error> {
58 debug!("Executing {query}");
59 let response = client
60 .query_range(query, start, end, step)
61 .header(
62 AUTHORIZATION,
63 HeaderValue::from_str(&format!(
64 "Basic {}",
65 general_purpose::STANDARD.encode(auth_header)
66 ))?,
67 )
68 .get()
69 .await?;
70
71 let result = response
72 .data()
73 .as_matrix()
74 .unwrap_or_else(|| panic!("Expected result of type matrix for {query}"));
75
76 if result.is_empty() {
77 return Err(anyhow!(
78 "Did not get expected response from server for {query}"
79 ));
80 }
81
82 let mut samples: Vec<f64> = result
83 .first()
84 .unwrap()
85 .samples()
86 .iter()
87 .filter_map(|sample| {
88 let v = sample.value();
89 if v.is_nan() { None } else { Some(v) }
90 })
91 .collect();
92 if samples.is_empty() {
93 return Err(anyhow!("Query returned zero data point! {query}"));
94 }
95 samples.sort_by(|a, b| a.partial_cmp(b).unwrap());
96
97 assert!(
98 (1..=100).contains(&percentile),
99 "Invalid percentile {percentile}"
100 );
101 let index = samples.len() * percentile as usize / 100;
102 let result = samples[index];
103 info!(
104 "{query}: got p{percentile} value {result}, over {} data points in time range {} - {}",
105 samples.len(),
106 unix_seconds_to_timestamp_string(start),
107 unix_seconds_to_timestamp_string(end)
108 );
109 Ok(result)
110}