Skip to content

Commit 2905d45

Browse files
committed
feat(payload): adopt Probability for TimestampConfig::probability
Change the public `TimestampConfig::probability` field from `f32` to the `Probability` alias of `BoundedProbability<{ f32::to_bits(0.0) }>`. The `try_from` impl enforces the finite + `[0.0, 1.0]` invariant at deserialize time, so the matching check in `TimestampConfig::valid()` is removed. The new type threads through `MemberGenerator::new` and `MetricGenerator::new`. At the comparison sites in `metric::timestamp`, `.get()` extracts the inner `f32` so RNG sequences and bit-exact output are preserved.
1 parent 52539fb commit 2905d45

2 files changed

Lines changed: 34 additions & 33 deletions

File tree

lading_payload/src/dogstatsd.rs

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use tracing::{debug, warn};
99
use crate::{
1010
Serialize,
1111
common::{
12-
config::ConfRange,
12+
config::{ConfRange, Probability},
1313
strings,
1414
strings::{random_strings_with_length, random_strings_with_length_range},
1515
},
@@ -226,15 +226,15 @@ pub struct TimestampConfig {
226226
///
227227
/// The `DogStatsD` protocol only supports this field for count and gauge metrics.
228228
pub range: ConfRange<u32>,
229-
/// Probability between 0 and 1 that a generated count or gauge metric includes `|T`.
230-
pub probability: f32,
229+
/// Probability that a generated count or gauge metric includes `|T`.
230+
pub probability: Probability,
231231
}
232232

233233
impl Default for TimestampConfig {
234234
fn default() -> Self {
235235
Self {
236236
range: ConfRange::Constant(1),
237-
probability: 0.0,
237+
probability: Probability::try_new(0.0).expect("0.0 is in [0.0, 1.0]"),
238238
}
239239
}
240240
}
@@ -245,13 +245,7 @@ impl TimestampConfig {
245245
if !range_valid {
246246
return Result::Err(format!("Timestamp range is invalid: {reason}"));
247247
}
248-
if !self.probability.is_finite() || self.probability < 0.0 || self.probability > 1.0 {
249-
return Result::Err(format!(
250-
"Timestamp probability {} must be finite and in range [0.0, 1.0]",
251-
self.probability
252-
));
253-
}
254-
if self.probability > 0.0 && self.range.start() == 0 {
248+
if self.probability.get() > 0.0 && self.range.start() == 0 {
255249
return Result::Err(
256250
"Timestamp range start value cannot be 0 when timestamps are enabled".to_string(),
257251
);
@@ -450,7 +444,7 @@ impl MemberGenerator {
450444
external_data: Vec<String>,
451445
cardinality: Vec<String>,
452446
timestamp_range: ConfRange<u32>,
453-
timestamp_probability: f32,
447+
timestamp_probability: Probability,
454448
mut rng: &mut R,
455449
) -> Result<Self, crate::Error>
456450
where
@@ -833,12 +827,16 @@ impl DogStatsD {
833827

834828
#[cfg(test)]
835829
mod test {
836-
use super::{ConfRange, Config, KindWeights, MetricWeights, TimestampConfig};
830+
use super::{ConfRange, Config, KindWeights, MetricWeights, Probability, TimestampConfig};
837831
use proptest::prelude::*;
838832
use rand::{SeedableRng, rngs::SmallRng};
839833

840834
use crate::{DogStatsD, Serialize};
841835

836+
fn prob(value: f32) -> Probability {
837+
Probability::try_new(value).expect("value must be in [0.0, 1.0]")
838+
}
839+
842840
// Generate a batch of raw DogStatsD bytes using the given config and seed.
843841
fn generate_bytes(config: &Config, seed: u64) -> Vec<u8> {
844842
let mut rng = SmallRng::seed_from_u64(seed);
@@ -942,7 +940,7 @@ mod test {
942940
metric_weights: MetricWeights::new(100, 100, 0, 0, 0, 0),
943941
timestamp: Box::new(TimestampConfig {
944942
range: ConfRange::Constant(1_656_581_400),
945-
probability: 1.0,
943+
probability: prob(1.0),
946944
}),
947945
..Default::default()
948946
};
@@ -956,7 +954,7 @@ mod test {
956954
metric_weights: MetricWeights::new(100, 100, 0, 0, 0, 0),
957955
timestamp: Box::new(TimestampConfig {
958956
range: ConfRange::Constant(1_656_581_400),
959-
probability: 0.0,
957+
probability: prob(0.0),
960958
}),
961959
..Default::default()
962960
};
@@ -974,7 +972,7 @@ mod test {
974972
metric_weights: MetricWeights::new(100, 100, 0, 0, 0, 0),
975973
timestamp: Box::new(TimestampConfig {
976974
range: ConfRange::Inclusive { min: 10, max: 20 },
977-
probability: 1.0,
975+
probability: prob(1.0),
978976
}),
979977
..Default::default()
980978
};
@@ -1001,7 +999,7 @@ mod test {
1001999
metric_weights: MetricWeights::new(0, 0, 100, 0, 0, 0),
10021000
timestamp: Box::new(TimestampConfig {
10031001
range: ConfRange::Constant(1_656_581_400),
1004-
probability: 1.0,
1002+
probability: prob(1.0),
10051003
}),
10061004
..Default::default()
10071005
};
@@ -1013,23 +1011,22 @@ mod test {
10131011
}
10141012

10151013
#[test]
1016-
fn timestamp_probability_must_be_valid() {
1017-
let config = Config {
1018-
timestamp: Box::new(TimestampConfig {
1019-
probability: 2.0,
1020-
..Default::default()
1021-
}),
1022-
..Default::default()
1023-
};
1024-
assert!(config.valid().is_err());
1014+
fn timestamp_probability_rejects_out_of_range_on_deserialize() {
1015+
let yaml = "range: !constant 1\nprobability: 2.0\n";
1016+
let err = serde_yaml::from_str::<TimestampConfig>(yaml)
1017+
.expect_err("probability 2.0 must be rejected at deserialize time");
1018+
assert!(
1019+
err.to_string().contains("exceeds 1.0"),
1020+
"unexpected error: {err}"
1021+
);
10251022
}
10261023

10271024
#[test]
10281025
fn timestamp_range_must_be_positive_when_enabled() {
10291026
let config = Config {
10301027
timestamp: Box::new(TimestampConfig {
10311028
range: ConfRange::Constant(0),
1032-
probability: 1.0,
1029+
probability: prob(1.0),
10331030
}),
10341031
..Default::default()
10351032
};
@@ -1064,7 +1061,7 @@ mod test {
10641061
metric_weights: MetricWeights::new(100, 100, 0, 0, 0, 0),
10651062
timestamp: Box::new(TimestampConfig {
10661063
range: ConfRange::Inclusive { min: 10, max: 20 },
1067-
probability: 0.5,
1064+
probability: prob(0.5),
10681065
}),
10691066
..Default::default()
10701067
};

lading_payload/src/dogstatsd/metric.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ use rand::{
88
seq::IteratorRandom,
99
};
1010

11-
use crate::{Error, Generator, common::strings, dogstatsd::metric::template::Template};
11+
use crate::{
12+
Error, Generator,
13+
common::{config::Probability, strings},
14+
dogstatsd::metric::template::Template,
15+
};
1216
use tracing::debug;
1317

1418
use self::strings::{Pool, choose_or_not_ref};
@@ -26,7 +30,7 @@ pub(crate) struct MetricGenerator {
2630
pub(crate) external_data: Vec<String>,
2731
pub(crate) cardinality: Vec<String>,
2832
pub(crate) timestamp_range: ConfRange<u32>,
29-
pub(crate) timestamp_probability: f32,
33+
pub(crate) timestamp_probability: Probability,
3034
pub(crate) templates: Vec<template::Template>,
3135
pub(crate) multivalue_count: ConfRange<u16>,
3236
pub(crate) multivalue_pack_probability: f32,
@@ -54,7 +58,7 @@ impl MetricGenerator {
5458
external_data: Vec<String>,
5559
cardinality: Vec<String>,
5660
timestamp_range: ConfRange<u32>,
57-
timestamp_probability: f32,
61+
timestamp_probability: Probability,
5862
tags_generator: &mut common::tags::Generator,
5963
pools: &StringPools,
6064
value_conf: ValueConf,
@@ -109,12 +113,12 @@ impl MetricGenerator {
109113
where
110114
R: Rng + ?Sized,
111115
{
112-
if self.timestamp_probability == 0.0 {
116+
if self.timestamp_probability.get() == 0.0 {
113117
return None;
114118
}
115119

116120
let prob: f32 = StandardUniform.sample(rng);
117-
if prob < self.timestamp_probability {
121+
if prob < self.timestamp_probability.get() {
118122
Some(self.timestamp_range.sample(rng))
119123
} else {
120124
None

0 commit comments

Comments
 (0)