mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-11-14 06:46:56 +00:00
fix(promql): drop metadata labels from range function results
Prometheus drops __name__, __type__ and __unit__ from the result of every range function except last_over_time and first_over_time; the evaluator passed the metric through unchanged. Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
This commit is contained in:
@@ -70,8 +70,12 @@ pub struct RangeEvaluator {
|
||||
series: Option<SeriesState>,
|
||||
fields: Vec<FieldSamples>,
|
||||
row: Vec<Option<f64>>,
|
||||
drops_metric_name: bool,
|
||||
}
|
||||
|
||||
/// Prometheus `schema.IsMetadataLabel`: the labels removed with the metric name.
|
||||
const METADATA_LABELS: [&str; 3] = ["__name__", "__type__", "__unit__"];
|
||||
|
||||
struct SeriesState {
|
||||
primary_key: Buffer,
|
||||
metric: MetricName,
|
||||
@@ -119,7 +123,10 @@ impl RangeEvaluator {
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::with_kernel(input, grid, window, function.kernel(window.offset_ms))
|
||||
let mut evaluator =
|
||||
Self::with_kernel(input, grid, window, function.kernel(window.offset_ms))?;
|
||||
evaluator.drops_metric_name = function.drops_metric_name();
|
||||
Ok(evaluator)
|
||||
}
|
||||
|
||||
pub(crate) fn with_kernel(
|
||||
@@ -156,6 +163,7 @@ impl RangeEvaluator {
|
||||
.map(|_| FieldSamples::default())
|
||||
.collect(),
|
||||
row: Vec::with_capacity(input.fields.len()),
|
||||
drops_metric_name: false,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -182,12 +190,15 @@ impl RangeEvaluator {
|
||||
return Ok(None);
|
||||
}
|
||||
self.evaluate(None)?;
|
||||
let state = self.series.take().expect("checked by check_chunk");
|
||||
let mut state = self.series.take().expect("checked by check_chunk");
|
||||
for field in &mut self.fields {
|
||||
field.timestamps.clear();
|
||||
field.values.clear();
|
||||
field.head = 0;
|
||||
}
|
||||
if self.drops_metric_name {
|
||||
state.metric.remove_all(&METADATA_LABELS);
|
||||
}
|
||||
self.output.finish(state.metric)
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,12 @@ pub enum RangeFunction {
|
||||
}
|
||||
|
||||
impl RangeFunction {
|
||||
/// Whether the result drops the metric name, as Prometheus does for every range function
|
||||
/// except `last_over_time` and `first_over_time`.
|
||||
pub(crate) fn drops_metric_name(self) -> bool {
|
||||
!matches!(self, Self::LastOverTime)
|
||||
}
|
||||
|
||||
/// Returns the kernel computing this function over windows read with `offset_ms`.
|
||||
pub(crate) fn kernel(self, offset_ms: i64) -> WindowKernel {
|
||||
match self {
|
||||
|
||||
@@ -99,6 +99,15 @@ fn probe_evaluator(fields: usize, grid: Arc<EvalGrid>, window: RangeWindow) -> R
|
||||
}
|
||||
|
||||
/// Builds a chunk whose timestamp and value arrays are slices at a nonzero offset.
|
||||
/// The metric of a result of `function` over the series of `metric(host)`.
|
||||
fn output_metric(function: RangeFunction, host: u64) -> MetricName {
|
||||
let mut metric = metric(host);
|
||||
if function.drops_metric_name() {
|
||||
metric.remove("__name__");
|
||||
}
|
||||
metric
|
||||
}
|
||||
|
||||
fn chunk(rows: &[Row], fields: usize, host: u64, is_last: bool) -> RawSeriesChunk {
|
||||
let padding = 3;
|
||||
let timestamps: Vec<_> = (0..padding)
|
||||
@@ -408,7 +417,7 @@ fn functions_match_range_udfs() {
|
||||
for piece in random_chunks(&mut rng, &rows, fields, host) {
|
||||
result = evaluator.push(&piece).unwrap();
|
||||
}
|
||||
assert_series_eq(result.as_ref(), &expected, &metric(host));
|
||||
assert_series_eq(result.as_ref(), &expected, &output_metric(function, host));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -690,7 +699,12 @@ fn range_functions_match_prometheus() {
|
||||
.push(&chunk(&rows[split..], 1, 0, true))
|
||||
.unwrap()
|
||||
.or(result);
|
||||
assert_series_eq_any_nan(result.as_ref(), &expected, &metric(0), line);
|
||||
assert_series_eq_any_nan(
|
||||
result.as_ref(),
|
||||
&expected,
|
||||
&output_metric(function, 0),
|
||||
line,
|
||||
);
|
||||
checked += expected_points.len();
|
||||
}
|
||||
assert!(checked > 1_000, "{checked} points checked");
|
||||
@@ -714,3 +728,43 @@ fn rejects_double_exponential_smoothing_factors_outside_the_unit_interval() {
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Range functions drop the metric name and the other metadata labels, except
|
||||
/// `last_over_time`, as Prometheus does.
|
||||
#[test]
|
||||
fn range_functions_drop_metadata_labels() {
|
||||
let labels = |names: &[&str]| -> Vec<Label> {
|
||||
names
|
||||
.iter()
|
||||
.map(|name| Label {
|
||||
name: Arc::from(*name),
|
||||
value: Arc::from("v"),
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
let input = MetricName::try_new(
|
||||
Some(Arc::from("m")),
|
||||
labels(&["__type__", "__unit__", "host"]),
|
||||
)
|
||||
.unwrap();
|
||||
let window = RangeWindow {
|
||||
range_ms: 10_000,
|
||||
offset_ms: 0,
|
||||
};
|
||||
let rows: Vec<Row> = vec![(1_000, vec![Some(1.0)]), (2_000, vec![Some(2.0)])];
|
||||
for (function, expected) in [
|
||||
(
|
||||
RangeFunction::Rate,
|
||||
MetricName::try_new(None, labels(&["host"])).unwrap(),
|
||||
),
|
||||
(RangeFunction::LastOverTime, input.clone()),
|
||||
] {
|
||||
let mut chunk = chunk(&rows, 1, 0, true);
|
||||
chunk.metric = Some(input.clone());
|
||||
let mut evaluator =
|
||||
RangeEvaluator::try_new(&raw_schema(1), grid(2_000, 1_000, 1), window, function)
|
||||
.unwrap();
|
||||
let series = evaluator.push(&chunk).unwrap().unwrap();
|
||||
assert_eq!(series.metric, expected, "{function:?}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user