Skip to content

Commit

Permalink
Allow the user to configure the range of DogStatsD metrics (#698)
Browse files Browse the repository at this point in the history
This commit introduces a configuration option to allow the user to define the
range of values that appear in DogStatsD metrics. This range applies to all
metric kinds. The distribution is changed from Standard -- "numerically uniform"
-- to actually Uniform. We do not allow users to configure the distribution.

REF SMP-694

Signed-off-by: Brian L. Troutwine <[email protected]>
  • Loading branch information
blt authored Sep 20, 2023
1 parent b4781e4 commit 0aafcb3
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 21 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## [0.18.1-rc0]
## [0.18.1]
### Added
- `lading-payload` crate is now split out from the `lading` crate
- It is now possible for users to configure the range of DogStatsD payloads
values. Previously the range was 64-bits wide. The range is inclusive or
constant. Additionally, users may configure a probability for values being a
floating point or not.
### Changed
- The block mechanism is reworked to provide a 'fixed' and 'streaming' model,
running in a separate OS thread from the tokio runtime.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lading/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "lading"
version = "0.18.1-rc0"
version = "0.18.1"
authors = ["Brian L. Troutwine <[email protected]>", "George Hahn <[email protected]"]
edition = "2021"
license = "MIT"
Expand Down
4 changes: 4 additions & 0 deletions lading/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ impl Cache {
multivalue_count_maximum,
kind_weights,
metric_weights,
value,
}) => {
let context_range = *contexts_minimum..*contexts_maximum;
let tags_per_msg_range = *tags_per_msg_minimum..*tags_per_msg_maximum;
Expand All @@ -173,6 +174,7 @@ impl Cache {
*multivalue_pack_probability,
*kind_weights,
*metric_weights,
*value,
&mut rng,
);

Expand Down Expand Up @@ -286,6 +288,7 @@ fn stream_inner(
multivalue_count_maximum,
kind_weights,
metric_weights,
value,
}) => {
let context_range = *contexts_minimum..*contexts_maximum;
let tags_per_msg_range = *tags_per_msg_minimum..*tags_per_msg_maximum;
Expand All @@ -304,6 +307,7 @@ fn stream_inner(
*multivalue_pack_probability,
*kind_weights,
*metric_weights,
*value,
&mut rng,
);

Expand Down
55 changes: 51 additions & 4 deletions lading_payload/src/dogstatsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ fn contexts_maximum() -> u32 {
10_000
}

fn value_config() -> ValueConf {
ValueConf {
float_probability: 0.5, // 50%
range: ValueRange::Inclusive {
min: i64::MIN,
max: i64::MAX,
},
}
}

// https://docs.datadoghq.com/developers/guide/what-best-practices-are-recommended-for-naming-metrics-and-tags/#rules-and-best-practices-for-naming-metrics
fn name_length_minimum() -> u16 {
1
Expand Down Expand Up @@ -118,6 +128,30 @@ impl Default for MetricWeights {
}
}

/// Configuration for the values of a metric.
#[derive(Debug, Deserialize, Clone, PartialEq, Copy)]
pub struct ValueConf {
/// Odds out of 256 that the value will be a float and not an integer.
float_probability: f32,
range: ValueRange,
}

/// Configuration for the values range of a metric.
#[derive(Debug, Deserialize, Clone, PartialEq, Copy)]
#[serde(rename_all = "snake_case")]
pub enum ValueRange {
/// Metric values are always constant.
Constant(i64),
/// Metric values are uniformly distributed between min and max, inclusive
/// of max.
Inclusive {
/// The minimum of the range.
min: i64,
/// The maximum of the range.
max: i64,
},
}

/// Configure the `DogStatsD` payload.
#[derive(Debug, Deserialize, Clone, PartialEq, Copy)]
pub struct Config {
Expand Down Expand Up @@ -184,9 +218,14 @@ pub struct Config {
/// payload.
#[serde(default)]
pub kind_weights: KindWeights,

/// Defines the relative probability of each kind of DogStatsD metric.
#[serde(default)]
pub metric_weights: MetricWeights,

/// The configuration of values that appear in all metrics.
#[serde(default = "value_config")]
pub value: ValueConf,
}

fn choose_or_not_ref<'a, R, T>(mut rng: &mut R, pool: &'a [T]) -> Option<&'a T>
Expand Down Expand Up @@ -272,6 +311,7 @@ impl MemberGenerator {
multivalue_pack_probability: f32,
kind_weights: KindWeights,
metric_weights: MetricWeights,
value_conf: ValueConf,
mut rng: &mut R,
) -> Self
where
Expand Down Expand Up @@ -343,6 +383,7 @@ impl MemberGenerator {
small_strings,
tagsets.clone(),
pool.as_ref(),
value_conf,
&mut rng,
);

Expand Down Expand Up @@ -425,6 +466,7 @@ impl DogStatsD {
multivalue_pack_probability(),
KindWeights::default(),
MetricWeights::default(),
value_config(),
rng,
)
}
Expand Down Expand Up @@ -454,6 +496,7 @@ impl DogStatsD {
multivalue_pack_probability: f32,
kind_weights: KindWeights,
metric_weights: MetricWeights,
value_conf: ValueConf,
rng: &mut R,
) -> Self
where
Expand All @@ -469,6 +512,7 @@ impl DogStatsD {
multivalue_pack_probability,
kind_weights,
metric_weights,
value_conf,
rng,
);

Expand Down Expand Up @@ -509,8 +553,8 @@ mod test {
contexts_maximum, contexts_minimum, multivalue_count_maximum, multivalue_count_minimum,
multivalue_pack_probability, name_length_maximum, name_length_minimum,
tag_key_length_maximum, tag_key_length_minimum, tag_value_length_maximum,
tag_value_length_minimum, tags_per_msg_maximum, tags_per_msg_minimum, KindWeights,
MetricWeights,
tag_value_length_minimum, tags_per_msg_maximum, tags_per_msg_minimum, value_config,
KindWeights, MetricWeights,
},
DogStatsD, Serialize,
};
Expand All @@ -529,11 +573,14 @@ mod test {
let tags_per_msg_range = tags_per_msg_minimum()..tags_per_msg_maximum();
let multivalue_count_range = multivalue_count_minimum()..multivalue_count_maximum();
let multivalue_pack_probability = multivalue_pack_probability();
let value_conf = value_config();

let kind_weights = KindWeights::default();
let metric_weights = MetricWeights::default();
let dogstatsd = DogStatsD::new(context_range, name_length_range, tag_key_length_range, tag_value_length_range, tags_per_msg_range, multivalue_count_range, multivalue_pack_probability, kind_weights,
metric_weights, &mut rng);
let dogstatsd = DogStatsD::new(context_range, name_length_range, tag_key_length_range,
tag_value_length_range, tags_per_msg_range,
multivalue_count_range, multivalue_pack_probability, kind_weights,
metric_weights, value_conf, &mut rng);

let mut bytes = Vec::with_capacity(max_bytes);
dogstatsd.to_bytes(rng, max_bytes, &mut bytes).unwrap();
Expand Down
82 changes: 72 additions & 10 deletions lading_payload/src/dogstatsd/common.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,95 @@
use std::fmt;

use rand::{distributions::Standard, prelude::Distribution, Rng};
use rand::{
distributions::{OpenClosed01, Standard, Uniform},
prelude::Distribution,
Rng,
};

use crate::Generator;

use super::{ValueConf, ValueRange};

pub(crate) mod tags;

#[derive(Clone, Debug)]
pub(crate) enum NumValue {
Float(f64),
Int(i64),
Float(f64),
}

#[derive(Clone, Debug)]
pub(crate) enum NumValueGenerator {
Constant {
float_probability: f32,
int: i64,
float: f64,
},
Uniform {
float_probability: f32,
int_distr: Uniform<i64>,
float_distr: Uniform<f64>,
},
}

impl NumValueGenerator {
#[allow(clippy::cast_possible_truncation)]
pub(crate) fn new(conf: ValueConf) -> Self {
match conf.range {
ValueRange::Constant(c) => Self::Constant {
float_probability: conf.float_probability,
int: c,
float: c as f64,
},
ValueRange::Inclusive { min, max } => Self::Uniform {
float_probability: conf.float_probability,
int_distr: Uniform::new_inclusive(min, max),
float_distr: Uniform::new_inclusive(min as f64, max as f64),
},
}
}
}

impl Distribution<NumValue> for Standard {
fn sample<R>(&self, rng: &mut R) -> NumValue
impl<'a> Generator<'a> for NumValueGenerator {
type Output = NumValue;

fn generate<R>(&'a self, rng: &mut R) -> Self::Output
where
R: Rng + ?Sized,
R: rand::Rng + ?Sized,
{
match rng.gen_range(0..=1) {
0 => NumValue::Float(rng.gen()),
1 => NumValue::Int(rng.gen()),
_ => unreachable!(),
let prob: f32 = OpenClosed01.sample(rng);
match self {
Self::Constant {
float_probability,
int,
float,
} => {
if prob < *float_probability {
NumValue::Float(*float)
} else {
NumValue::Int(*int)
}
}
Self::Uniform {
float_probability,
int_distr,
float_distr,
} => {
if prob < *float_probability {
NumValue::Float(float_distr.sample(rng))
} else {
NumValue::Int(int_distr.sample(rng))
}
}
}
}
}

impl fmt::Display for NumValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Float(val) => write!(f, "{val}"),
Self::Int(val) => write!(f, "{val}"),
Self::Float(val) => write!(f, "{val}"),
}
}
}
Expand Down
15 changes: 11 additions & 4 deletions lading_payload/src/dogstatsd/metric.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
use std::{fmt, ops::Range};

use rand::{
distributions::{OpenClosed01, Standard, WeightedIndex},
distributions::{OpenClosed01, WeightedIndex},
prelude::{Distribution, SliceRandom},
Rng,
};

use crate::{common::strings, dogstatsd::metric::template::Template, Generator};
use tracing::debug;

use super::{choose_or_not_ref, common};
use super::{
choose_or_not_ref,
common::{self, NumValueGenerator},
ValueConf,
};

mod template;

Expand All @@ -19,6 +23,7 @@ pub(crate) struct MetricGenerator {
pub(crate) templates: Vec<template::Template>,
pub(crate) multivalue_count_range: Range<u16>,
pub(crate) multivalue_pack_probability: f32,
pub(crate) num_value_generator: NumValueGenerator,
}

impl MetricGenerator {
Expand All @@ -32,6 +37,7 @@ impl MetricGenerator {
container_ids: Vec<String>,
tagsets: common::tags::Tagsets,
str_pool: &strings::Pool,
value_conf: ValueConf,
mut rng: &mut R,
) -> Self
where
Expand Down Expand Up @@ -65,6 +71,7 @@ impl MetricGenerator {
templates,
multivalue_count_range,
multivalue_pack_probability,
num_value_generator: NumValueGenerator::new(value_conf),
}
}
}
Expand All @@ -88,14 +95,14 @@ impl<'a> Generator<'a> for MetricGenerator {
let sample_rate = rng.gen();

let mut values = Vec::with_capacity(self.multivalue_count_range.end as usize);
let value: common::NumValue = Standard.sample(&mut rng);
let value: common::NumValue = self.num_value_generator.generate(&mut rng);
values.push(value);

let prob: f32 = OpenClosed01.sample(&mut rng);
if prob < self.multivalue_pack_probability {
let num_desired_values = rng.gen_range(self.multivalue_count_range.clone());
for _ in 1..num_desired_values {
values.push(Standard.sample(&mut rng));
values.push(self.num_value_generator.generate(&mut rng));
}
}

Expand Down

0 comments on commit 0aafcb3

Please sign in to comment.