chore: merge feat/narthex-window-eval-rate into feat/narthex-window-eval-at

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

# Conflicts:
#	src/promql/src/range_eval/tests.rs
This commit is contained in:
Dennis Zhuang
2026-10-17 23:19:55 +08:00
2 changed files with 34 additions and 3 deletions
+5 -2
View File
@@ -184,11 +184,14 @@ fn quantile(q: f64, values: &[f64], sorted: &mut Vec<f64>) -> f64 {
}
sorted.clear();
sorted.extend_from_slice(values);
sorted.sort_unstable_by(|a, b| match (a.is_nan(), b.is_nan()) {
// Go's `vectorByValueHeap` puts NaN first and treats +0 and -0 as equal; Go sorts short
// inputs by insertion, which keeps equal values in window order, and which zero is picked
// decides the sign of the result.
sorted.sort_by(|a, b| match (a.is_nan(), b.is_nan()) {
(true, true) => std::cmp::Ordering::Equal,
(true, false) => std::cmp::Ordering::Less,
(false, true) => std::cmp::Ordering::Greater,
(false, false) => a.total_cmp(b),
(false, false) => a.partial_cmp(b).expect("neither is NaN"),
});
let n = sorted.len() as f64;
let rank = q * (n - 1.0);
+29 -1
View File
@@ -27,7 +27,9 @@ use datatypes::series::histogram::{
BucketSideView, CounterResetHint, HistogramBatchBuilder, HistogramView,
};
use datatypes::series::raw::{RawSeriesChunk, RawTimestamps};
use datatypes::series::samples::{FieldSpec, Samples, Series, SeriesFields, SeriesSchema};
use datatypes::series::samples::{
FieldSpec, SampleRef, Samples, Series, SeriesFields, SeriesSchema,
};
use datatypes::series::time::{EvalGrid, GridSpec};
use datatypes::series::{Label, MetricName};
use rand::rngs::StdRng;
@@ -986,3 +988,29 @@ fn waiting_for_a_point_drops_samples_behind_its_window() {
let last = chunk(&[(100_000, vec![Some(1.0)])], 1, 0, true);
assert!(evaluator.push(&last).unwrap().is_some());
}
/// Equal values keep window order in `quantile_over_time`, so the zero a quantile lands on is
/// the one Prometheus picks: `1 / quantile_over_time(1, m[3s])` over +0, -0, -0 is -Inf.
#[test]
fn quantile_keeps_the_window_order_of_signed_zeros() {
let window = RangeWindow {
range_ms: 3_000,
offset_ms: 0,
anchor_ms: None,
};
let rows: Vec<Row> = [(0, 0.0), (1_000, -0.0), (2_000, -0.0)]
.map(|(ts, value)| (ts, vec![Some(value)]))
.to_vec();
let mut evaluator = RangeEvaluator::try_new(
&raw_schema(1),
grid(2_000, 1_000, 1),
window,
RangeFunction::QuantileOverTime(1.0),
)
.unwrap();
let series = evaluator.push(&chunk(&rows, 1, 0, true)).unwrap().unwrap();
let SampleRef::Float(value) = series.fields.as_slice()[0].sample(0) else {
panic!("expected a float");
};
assert_eq!(1.0 / value, f64::NEG_INFINITY);
}