diff --git a/shotover-proxy/benches/windsock/profilers/shotover_metrics.rs b/shotover-proxy/benches/windsock/profilers/shotover_metrics.rs index a9a85dea2..09a4d0793 100644 --- a/shotover-proxy/benches/windsock/profilers/shotover_metrics.rs +++ b/shotover-proxy/benches/windsock/profilers/shotover_metrics.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, time::Duration}; use time::OffsetDateTime; use tokio::task::JoinHandle; use tokio::{sync::mpsc, time::MissedTickBehavior}; -use windsock::{Goal, Metric, ReportArchive}; +use windsock::{Goal, LatencyPercentile, Metric, ReportArchive}; pub struct ShotoverMetrics { shutdown_tx: mpsc::Sender<()>, @@ -60,6 +60,7 @@ impl ShotoverMetrics { } parsed_metrics } + pub fn windsock_metrics(parsed_metrics: ParsedMetrics) -> Vec { let mut new_metrics = vec![]; for (name, value) in parsed_metrics { @@ -102,16 +103,13 @@ impl ShotoverMetrics { }; let values = summary .iter() - .map(|x| { - ( - x.count, - format!("{} - {:.4}ms", x.quantile, x.count * 1000.0), - Goal::SmallerIsBetter, - ) + .map(|x| LatencyPercentile { + value: x.count, + value_display: format!("{:.4}ms", x.count * 1000.0), + quantile: x.quantile.to_string(), }) .collect(); - // TODO: add a Metric::QuantileLatency and use instead - new_metrics.push(Metric::EachSecond { name, values }); + new_metrics.push(Metric::LatencyPercentiles { name, values }); } _ => { tracing::warn!("Unused shotover metric: {name}") diff --git a/windsock/src/lib.rs b/windsock/src/lib.rs index b6b69adaa..2d64f8c77 100644 --- a/windsock/src/lib.rs +++ b/windsock/src/lib.rs @@ -8,7 +8,10 @@ mod report; mod tables; pub use bench::{Bench, BenchParameters, BenchTask, Profiling}; -pub use report::{ExternalReport, Metric, OperationsReport, PubSubReport, Report, ReportArchive}; +pub use report::{ + ExternalReport, LatencyPercentile, Metric, OperationsReport, PubSubReport, Report, + ReportArchive, +}; pub use tables::Goal; use anyhow::{anyhow, Result}; diff --git a/windsock/src/report.rs b/windsock/src/report.rs index a9b6db51f..cd20d53cc 100644 --- a/windsock/src/report.rs +++ b/windsock/src/report.rs @@ -158,6 +158,27 @@ pub enum Metric { name: String, values: Vec<(f64, String, Goal)>, }, + LatencyPercentiles { + name: String, + values: Vec, + }, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct LatencyPercentile { + pub quantile: String, + pub value: f64, + pub value_display: String, +} + +impl LatencyPercentile { + pub(crate) fn to_measurement(&self) -> (f64, String, Goal) { + ( + self.value, + self.value_display.clone(), + Goal::SmallerIsBetter, + ) + } } impl Metric { @@ -165,6 +186,7 @@ impl Metric { match self { Metric::Total { name, .. } => name, Metric::EachSecond { name, .. } => name, + Metric::LatencyPercentiles { name, .. } => name, } } @@ -176,6 +198,9 @@ impl Metric { Metric::EachSecond { name, .. } => MetricIdentifier::EachSecond { name: name.to_owned(), }, + Metric::LatencyPercentiles { name, .. } => MetricIdentifier::LatencyPercentiles { + name: name.to_owned(), + }, } } @@ -184,6 +209,7 @@ impl Metric { match self { Metric::Total { .. } => 1, Metric::EachSecond { values, .. } => values.len(), + Metric::LatencyPercentiles { values, .. } => values.len(), } } } @@ -192,6 +218,7 @@ impl Metric { pub enum MetricIdentifier { Total { name: String }, EachSecond { name: String }, + LatencyPercentiles { name: String }, } fn error_message_insertion(messages: &mut Vec, new_message: String) { diff --git a/windsock/src/tables.rs b/windsock/src/tables.rs index 239d5c13e..c33f69971 100644 --- a/windsock/src/tables.rs +++ b/windsock/src/tables.rs @@ -530,13 +530,13 @@ fn base(reports: &[ReportColumn], table_type: &str) { .iter() .find(|metric| metric.identifier() == metric_identifier) .map(|metric| match metric { - Metric::EachSecond { .. } => unreachable!(), Metric::Total { compare, value, goal, .. } => (*compare, value.to_owned(), *goal), + _ => unreachable!(), }) })); } @@ -561,12 +561,50 @@ fn base(reports: &[ReportColumn], table_type: &str) { .iter() .find(|x| x.identifier() == metric_identifier) .and_then(|metric| match metric { - Metric::Total { .. } => unreachable!(), Metric::EachSecond { values, .. } => values.get(i).cloned(), + _ => unreachable!(), }) })); } } + MetricIdentifier::LatencyPercentiles { name } => { + rows.push(Row::Heading(format!("{name} Percentiles"))); + for (i, largest_col) in reports + .iter() + .map(|x| { + x.current + .metrics + .iter() + .find(|x| x.identifier() == metric_identifier) + .map(|metric| match metric { + Metric::LatencyPercentiles { values, .. } => values.clone(), + _ => unreachable!(), + }) + .unwrap_or(vec![]) + }) + .max_by_key(|x| x.len()) + .unwrap() + .into_iter() + .enumerate() + { + rows.push(Row::measurements( + reports, + &largest_col.quantile, + |report| { + report + .metrics + .iter() + .find(|x| x.identifier() == metric_identifier) + .and_then(|metric| match metric { + Metric::LatencyPercentiles { values, .. } => { + values.get(i).map(|x| x.to_measurement()) + } + _ => unreachable!(), + }) + }, + )); + } + } } }