Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the user to configure the range of DogStatsD metrics #698

Merged
merged 11 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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() -> u16 {
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