Skip to content

Commit

Permalink
chore: work around serde issue with f32 NaNs
Browse files Browse the repository at this point in the history
Serde serializes f32s of value NaN to `null`, but doesn't serialize them
back from `null` to NaN. For that we need a custom deserialize.

Also see: <serde-rs/json#202>
  • Loading branch information
ctron committed Aug 28, 2024
1 parent e089265 commit c90e1c3
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 15 deletions.
2 changes: 2 additions & 0 deletions src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@

mod common;
mod delta;
mod nullable;

pub(crate) use common::{load_baseline_file, ReportData};
pub(crate) use delta::*;
pub(crate) use nullable::NullableFloat;

use crate::config::GooseDefaults;
use crate::goose::{get_base_url, GooseMethod, Scenario};
Expand Down
5 changes: 2 additions & 3 deletions src/metrics/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ use super::{
delta::*, merge_times, per_second_calculations, prepare_status_codes, update_max_time,
update_min_time, GooseMetrics,
};
use crate::report::ErrorMetric;
use crate::{
report::{
get_response_metric, CORequestMetric, RequestMetric, ResponseMetric, ScenarioMetric,
StatusCodeMetric, TransactionMetric,
get_response_metric, CORequestMetric, ErrorMetric, RequestMetric, ResponseMetric,
ScenarioMetric, StatusCodeMetric, TransactionMetric,
},
util, GooseError,
};
Expand Down
7 changes: 7 additions & 0 deletions src/metrics/delta.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::metrics::NullableFloat;
use num_format::{Format, ToFormattedString};
use std::fmt::{Debug, Display, Formatter, Write};

Expand Down Expand Up @@ -68,6 +69,12 @@ impl<T: DeltaValue> From<T> for Value<T> {
}
}

impl From<f32> for Value<NullableFloat> {
fn from(value: f32) -> Self {
Self::Plain(NullableFloat(value))
}
}

impl<T: DeltaValue> Value<T> {
pub fn diff(&mut self, other: T) {
match self {
Expand Down
58 changes: 58 additions & 0 deletions src/metrics/nullable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use crate::metrics::DeltaValue;
use serde::Deserializer;
use std::fmt::{Display, Formatter};
use std::ops::{Deref, DerefMut};

/// An `f32` which can deserialize from `null` as `NaN`.
///
/// Also see: <https://github.com/serde-rs/json/issues/202>
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, serde::Serialize)]
pub struct NullableFloat(pub f32);

impl Deref for NullableFloat {
type Target = f32;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for NullableFloat {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl From<f32> for NullableFloat {
fn from(value: f32) -> Self {
Self(value)
}
}

impl<'de> serde::Deserialize<'de> for NullableFloat {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = Option::<f32>::deserialize(deserializer)?;
Ok(Self(value.unwrap_or(f32::NAN)))
}
}

impl Display for NullableFloat {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}

impl DeltaValue for NullableFloat {
type Delta = NullableFloat;

fn delta(self, value: Self) -> Self::Delta {
NullableFloat(self.0.delta(value.0))
}

fn is_delta_positive(value: Self::Delta) -> bool {
f32::is_delta_positive(value.0)
}
}
24 changes: 12 additions & 12 deletions src/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub(crate) use markdown::write_markdown_report;

use crate::{
goose::GooseMethod,
metrics::{self, format_value, DeltaEval, DeltaTo, Value},
metrics::{self, format_value, DeltaEval, DeltaTo, NullableFloat, Value},
report::common::OrEmpty,
};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -35,11 +35,11 @@ pub(crate) struct RequestMetric {
pub name: String,
pub number_of_requests: Value<usize>,
pub number_of_failures: Value<usize>,
pub response_time_average: Value<f32>,
pub response_time_average: Value<NullableFloat>,
pub response_time_minimum: Value<usize>,
pub response_time_maximum: Value<usize>,
pub requests_per_second: Value<f32>,
pub failures_per_second: Value<f32>,
pub requests_per_second: Value<NullableFloat>,
pub failures_per_second: Value<NullableFloat>,
}

impl DeltaTo for RequestMetric {
Expand All @@ -59,8 +59,8 @@ impl DeltaTo for RequestMetric {
pub(crate) struct CORequestMetric {
pub method: String,
pub name: String,
pub response_time_average: Value<f32>,
pub response_time_standard_deviation: Value<f32>,
pub response_time_average: Value<NullableFloat>,
pub response_time_standard_deviation: Value<NullableFloat>,
pub response_time_maximum: Value<usize>,
}

Expand Down Expand Up @@ -109,11 +109,11 @@ pub(crate) struct TransactionMetric {
pub name: String,
pub number_of_requests: Value<usize>,
pub number_of_failures: Value<usize>,
pub response_time_average: Option<Value<f32>>,
pub response_time_average: Option<Value<NullableFloat>>,
pub response_time_minimum: Value<usize>,
pub response_time_maximum: Value<usize>,
pub requests_per_second: Option<Value<f32>>,
pub failures_per_second: Option<Value<f32>>,
pub requests_per_second: Option<Value<NullableFloat>>,
pub failures_per_second: Option<Value<NullableFloat>>,
}

impl DeltaTo for TransactionMetric {
Expand All @@ -134,11 +134,11 @@ pub(crate) struct ScenarioMetric {
pub name: String,
pub users: Value<usize>,
pub count: Value<usize>,
pub response_time_average: Value<f32>,
pub response_time_average: Value<NullableFloat>,
pub response_time_minimum: Value<usize>,
pub response_time_maximum: Value<usize>,
pub count_per_second: Value<f32>,
pub iterations: Value<f32>,
pub count_per_second: Value<NullableFloat>,
pub iterations: Value<NullableFloat>,
}

impl DeltaTo for ScenarioMetric {
Expand Down

0 comments on commit c90e1c3

Please sign in to comment.