diff --git a/src/promql/src/aggregate.rs b/src/promql/src/aggregate.rs index b40b2e15c21..228f67ba4bc 100644 --- a/src/promql/src/aggregate.rs +++ b/src/promql/src/aggregate.rs @@ -35,7 +35,8 @@ use datatypes::series::samples::{SampleRef, Series, SeriesSchema}; use crate::go_format::format_float_f; use crate::grid_series::{GridSeriesBuilder, external}; -use crate::map::{drop_empty_labels, validate_float_series, validate_scalar}; +use crate::histogram_eval::histogram_string; +use crate::map::{drop_empty_labels, validate_float_series, validate_grid_series, validate_scalar}; use crate::range_eval::quantile; /// Float aggregations without a parameter. @@ -277,7 +278,8 @@ pub fn aggregate_quantile( /// `count_values(label, v)`: at each grid point, the number of samples in each group of series /// that share a value, with the value written to `label` in Prometheus' `FormatFloat(v, 'f', -1)` -/// form. With `by`, `label` is part of the grouping; with `without`, it is dropped if listed. +/// form, or for a native histogram as its `FloatHistogram.String`. With `by`, `label` is part of +/// the grouping; with `without`, it is dropped if listed. pub fn count_values( schema: &Arc, input: &[Series], @@ -298,7 +300,11 @@ pub fn count_values( } without => without, }; - validate_input(schema, input, checked)?; + // Unlike the other aggregations, this one reads histogram samples too. + validate_grouping(schema, checked)?; + for series in input { + validate_grid_series(schema, series)?; + } if label.is_empty() { return Err(DataFusionError::Execution(format!( "invalid label name {label:?}" @@ -319,20 +325,26 @@ pub fn count_values( let mut groups: Counts = Vec::new(); for series in input { // Equal bits format to the same label value, so each distinct value of a series - // resolves its group once. - let mut group_of_value: HashMap = HashMap::new(); + // resolves its group once. A histogram is keyed by the text it is written as. + let mut group_of_value: HashMap = HashMap::new(); for (field_index, field) in series.fields.iter().enumerate() { for ((point, _), sample) in series.time.positions().zip(field.iter()) { - let SampleRef::Float(value) = sample else { - continue; + let key = match sample { + SampleRef::Missing => continue, + SampleRef::Float(value) => Value::Float(value.to_bits()), + SampleRef::Histogram(h) => Value::Histogram(histogram_string(&h)?), }; - let group = match group_of_value.entry(value.to_bits()) { + let group = match group_of_value.entry(key) { Entry::Occupied(entry) => *entry.get(), Entry::Vacant(entry) => { + // Prometheus writes a float in Go's `'f'` form and a histogram as + // `FloatHistogram.String` does. + let text = match entry.key() { + Value::Float(bits) => format_float_f(f64::from_bits(*bits)), + Value::Histogram(text) => text.clone(), + }; let mut metric = series.metric.clone(); - metric - .set(label, Arc::from(format_float_f(value))) - .map_err(external)?; + metric.set(label, Arc::from(text)).map_err(external)?; let metric = grouping.apply(&metric)?; let index = *group_index.entry(label_key(&metric)).or_insert_with(|| { groups.push((metric, BTreeMap::new())); @@ -370,18 +382,29 @@ fn count_series(schema: &Arc, groups: Counts) -> Result) -> Result<()> { + validate_grouping(schema, grouping)?; + for series in input { + validate_float_series(schema, series)?; + } + Ok(()) +} + +fn validate_grouping(schema: &SeriesSchema, grouping: Grouping<'_>) -> Result<()> { if schema.eval_grid.is_none() { return Err(DataFusionError::Execution( "Aggregate requires an evaluated grid".into(), )); } - grouping.validate(schema)?; - for series in input { - validate_float_series(schema, series)?; - } - Ok(()) + grouping.validate(schema) } impl Grouping<'_> { diff --git a/src/promql/src/aggregate/tests.rs b/src/promql/src/aggregate/tests.rs index fef8bb651ac..31082bd784a 100644 --- a/src/promql/src/aggregate/tests.rs +++ b/src/promql/src/aggregate/tests.rs @@ -1391,3 +1391,48 @@ fn count_values_keeps_neighbours_of_a_power_of_two_apart() { ] ); } + +/// Prometheus counts a histogram sample under the text `FloatHistogram.String` writes it as, so +/// equal histograms share a group and a float beside them has its own. +#[test] +fn count_values_counts_histograms_by_their_text() { + use crate::grid_series::reference::OwnedHistogram; + use crate::grid_series::{GridSample, GridSeriesBuilder}; + use crate::histogram_eval::float_histogram::FloatHistogram; + use crate::histogram_eval::histogram_string; + + let schema = schema_with_grid_len(&["value"], 1); + let histogram = + OwnedHistogram::parse("0/0/0/4000000000000000/3ff0000000000000/0/1_1/4000000000000000///"); + let text = histogram_string(&histogram.view()).unwrap(); + let series = |job: &str, sample: GridSample| { + let mut builder = GridSeriesBuilder::try_new(schema.clone()).unwrap(); + builder.push_samples(0, &[Some(sample)]).unwrap(); + builder + .finish(metric("m", &[("job", job)])) + .unwrap() + .unwrap() + }; + let sample = || { + GridSample::Histogram(Box::new( + FloatHistogram::try_from_view(&histogram.view()).unwrap(), + )) + }; + let input = [ + series("a", sample()), + series("b", sample()), + series("c", GridSample::Float(2.0)), + ]; + let mut counts: Vec<(String, f64)> = count_values(&schema, &input, "v", Grouping::By(&[])) + .unwrap() + .iter() + .map(|series| { + let SampleRef::Float(count) = series.fields.as_slice()[0].sample(0) else { + panic!("a count is a float"); + }; + (series.metric.get("v").unwrap().to_string(), count) + }) + .collect(); + counts.sort_by(|a, b| a.0.cmp(&b.0)); + assert_eq!(counts, [("2".to_string(), 1.0), (text, 2.0)]); +} diff --git a/src/promql/src/go_format.rs b/src/promql/src/go_format.rs index bf19922df25..1c3e640a442 100644 --- a/src/promql/src/go_format.rs +++ b/src/promql/src/go_format.rs @@ -44,31 +44,38 @@ pub(crate) fn format_open_metrics_float(value: f64) -> String { if let Some(text) = special(value) { return text.to_string(); } - let (negative, digits, point) = shortest(value); - // The shortest `'g'` format switches to an exponent below 1e-4 and from 1e6 on. - let exponent = point - 1; - let mut text = if !(-4..6).contains(&exponent) { - let mut text = String::from(if negative { "-" } else { "" }); - text.push_str(&digits[..1]); - if digits.len() > 1 { - text.push('.'); - text.push_str(&digits[1..]); - } - text.push_str(&format!( - "e{}{:02}", - if exponent < 0 { '-' } else { '+' }, - exponent.abs() - )); - text - } else { - positional(negative, &digits, point) - }; + let mut text = format_float_g(value); if !text.contains(['e', '.']) { text.push_str(".0"); } text } +/// Go's `strconv.FormatFloat(value, 'g', -1, 64)`, which `%g` and `%v` print a float in: the +/// shortest digits, with an exponent of at least two digits below 1e-4 and from 1e6 on. +pub(crate) fn format_float_g(value: f64) -> String { + if let Some(text) = special(value) { + return text.to_string(); + } + let (negative, digits, point) = shortest(value); + let exponent = point - 1; + if (-4..6).contains(&exponent) { + return positional(negative, &digits, point); + } + let mut text = String::from(if negative { "-" } else { "" }); + text.push_str(&digits[..1]); + if digits.len() > 1 { + text.push('.'); + text.push_str(&digits[1..]); + } + text.push_str(&format!( + "e{}{:02}", + if exponent < 0 { '-' } else { '+' }, + exponent.abs() + )); + text +} + fn special(value: f64) -> Option<&'static str> { match value { f64::INFINITY => Some("+Inf"), diff --git a/src/promql/src/histogram_eval.rs b/src/promql/src/histogram_eval.rs index 4a53635c52a..d38b860c99d 100644 --- a/src/promql/src/histogram_eval.rs +++ b/src/promql/src/histogram_eval.rs @@ -254,6 +254,12 @@ fn has_histograms(series: &Series) -> bool { }) } +/// A native histogram as Go's `FloatHistogram.String` writes it, the label value `count_values` +/// gives a histogram sample. +pub(crate) fn histogram_string(h: &HistogramView<'_>) -> Result { + native::go_string(h) +} + /// A function of one native histogram. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum NativeHistogramFunction { diff --git a/src/promql/src/histogram_eval/native.rs b/src/promql/src/histogram_eval/native.rs index 4ec4dcc2c31..cc72515a1d7 100644 --- a/src/promql/src/histogram_eval/native.rs +++ b/src/promql/src/histogram_eval/native.rs @@ -19,6 +19,7 @@ use datafusion::error::{DataFusionError, Result}; use datatypes::series::histogram::{BucketSideView, HistogramView}; use crate::functions::kahan_inc; +use crate::go_format::format_float_g; use crate::histogram_eval::bounds::EXPONENTIAL_BOUNDS; pub(super) const CUSTOM_BUCKETS_SCHEMA: i32 = -53; @@ -28,6 +29,8 @@ pub(super) struct NativeBucket { lower: f64, upper: f64, count: f64, + /// The bucket's index on its side; meaningless for the zero bucket. + index: i32, } /// The buckets of `h` from the lowest to the highest, or the reverse: negative buckets, the zero @@ -57,6 +60,7 @@ pub(super) fn all_buckets( lower: -h.zero_threshold, upper: h.zero_threshold, count: h.zero_count, + index: 0, }); } side(h, high, !reverse, out)?; @@ -107,16 +111,74 @@ fn bucket(h: &HistogramView<'_>, idx: i32, count: f64, positive: bool) -> Result lower: bound(idx - 1)?, upper: bound(idx)?, count, + index: idx, } } else { NativeBucket { lower: -bound(idx)?, upper: -bound(idx - 1)?, count, + index: idx, } }) } +/// Go `FloatHistogram.String`, the form `count_values` writes a histogram value in: the count and +/// sum, then the buckets holding observations from the lowest up -- negative, zero, positive -- +/// each as `[lower,upper):count` with its bounds' inclusiveness, every number in Go's `%g`. +/// Unlike [`all_buckets`], the buckets keep the bounds their index gives, unclipped by the zero +/// bucket. +pub(super) fn go_string(h: &HistogramView<'_>) -> Result { + let mut text = format!( + "{{count:{}, sum:{}", + format_float_g(h.count), + format_float_g(h.sum) + ); + let custom = h.schema == CUSTOM_BUCKETS_SCHEMA; + let mut buckets = Vec::new(); + side(h, h.negative, false, &mut buckets)?; + for bucket in buckets.iter().rev().filter(|bucket| bucket.count != 0.0) { + push_bucket(&mut text, bucket, custom); + } + if h.zero_count != 0.0 { + if custom { + return Err(DataFusionError::Execution( + "histograms with custom buckets have no zero bucket".into(), + )); + } + text.push_str(&format!( + ", [{},{}]:{}", + format_float_g(-h.zero_threshold), + format_float_g(h.zero_threshold), + format_float_g(h.zero_count) + )); + } + buckets.clear(); + side(h, h.positive, true, &mut buckets)?; + for bucket in buckets.iter().filter(|bucket| bucket.count != 0.0) { + push_bucket(&mut text, bucket, custom); + } + text.push('}'); + Ok(text) +} + +/// Go `Bucket.String`, with the inclusiveness `baseBucketIterator.at` gives the bucket. +fn push_bucket(text: &mut String, bucket: &NativeBucket, custom: bool) { + let (lower_inclusive, upper_inclusive) = if custom { + (bucket.index == 0, true) + } else { + (bucket.lower < 0.0, bucket.upper > 0.0) + }; + text.push_str(&format!( + ", {}{},{}{}:{}", + if lower_inclusive { '[' } else { '(' }, + format_float_g(bucket.lower), + format_float_g(bucket.upper), + if upper_inclusive { ']' } else { ')' }, + format_float_g(bucket.count) + )); +} + /// Go `getBound`. pub(super) fn bound(idx: i32, schema: i32, custom_values: &[f64]) -> Result { if schema == CUSTOM_BUCKETS_SCHEMA { @@ -203,6 +265,7 @@ pub(super) fn quantile( lower: 0.0, upper: 0.0, count: 0.0, + index: 0, }; let mut count = 0.0; let mut visited = 0; diff --git a/src/promql/src/histogram_eval/string_cases.txt b/src/promql/src/histogram_eval/string_cases.txt new file mode 100644 index 00000000000..fa6870f843a --- /dev/null +++ b/src/promql/src/histogram_eval/string_cases.txt @@ -0,0 +1,47 @@ +0/0/4000000000000000/4014000000000000/400c000000000000/0/0_3/3ff0000000000000,0,4000000000000000/// {count:5, sum:3.5, [-0,0]:2, (0.5,1]:1, (2,4]:2} +1/3f50624dd2f1a9fc/0/4132d68700000000/be7ad7f29abcaf48/0/20_1/0/-2_2,3_1/3ff0000000000000,4000000000000000,4132d68400000000/ {count:1.234567e+06, sum:-1e-07, [-2.82842712474619,-2):1.234564e+06, [-0.7071067811865475,-0.5):2, [-0.5,-0.35355339059327373):1} +-53/0/0/4018000000000000/4028000000000000/0/0_3/3ff0000000000000,4000000000000000,4008000000000000///3ff0000000000000,4014000000000000 {count:6, sum:12, [-Inf,1]:1, (1,5]:2, (5,+Inf]:3} +-53/0/0/4000000000000000/7ff8000000000001/0/1_2/3fe0000000000000,3ff8000000000000///bff0000000000000,0,3fe0000000000000 {count:2, sum:NaN, (-1,0]:0.5, (0,0.5]:1.5} +3/0/0/7ff0000000000000/fff0000000000000/0///// {count:+Inf, sum:-Inf} +0/0/0/4008000000000000/4000000000000000/0/0_1,0_0,2_1/3ff0000000000000,7ff8000000000001/// {count:3, sum:2, (0.5,1]:1, (4,8]:NaN} +-2/3fe0000000000000/3ff0000000000000/4008000000000000/3ff0000000000000/0/-1_2/3ff0000000000000,3ff0000000000000/// {count:3, sum:1, [-0.5,0.5]:1, (0.00390625,0.0625]:1, (0.0625,1]:1} +3/3fe0000000000000/0/4055001175c76074/40adb352ce0f464f/3/-2_1/0/-19_1,2_4/4042800000000000,3f2aa1d1faf86ea9,0,3f4c431a422a645b,4047800000000000/ {count:84.00106567831409, sum:3801.6617283604933, [-0.3242098886627524,-0.29730177875068026):47, [-0.29730177875068026,-0.2726269331663144):0.0008624914374478864, [-0.25,-0.22925101080116778):0.00020318687664732285, [-0.1927763531759926,-0.17677669529663687):37} +-53/0/0/405bc00000000000/c068dcd4a171fac8/3/0_2/4040800000000000,4053800000000000///3ff0000000000000,4014000000000000,4024000000000000 {count:111, sum:-198.90095588933605, [-Inf,1]:33, (1,5]:78} +0/255bba08cf8c979d/0/0/8000000000000000/1///// {count:0, sum:-0} +5/255bba08cf8c979d/404c000000000000/404c000000000000/40a0bdb814b160c1/3///// {count:56, sum:2142.8595328741653, [-1e-128,1e-128]:56} +-1/255bba08cf8c979d/3f36cdb2e4f0a26e/3f52159408fe4cb1/bf9324164071cb30/3/4_1/3f48c44e9f84482b/// {count:0.0011037774711198207, sum:-0.018692348178272755, [-1e-128,1e-128]:0.0003479539636282229, (64,256]:0.0007558235074915978} +-1/3f847ae147ae147b/0/406308039013db44/40b9864b9433bdfa/3/-9_1,0_4/0,4053a00000000000,4014000000000000,3f3c809eda1bc597,4051300000000000/// {count:152.2504349124739, sum:6534.2952301348505, (3.814697265625e-06,1.52587890625e-05]:78.5, (1.52587890625e-05,6.103515625e-05]:5, (6.103515625e-05,0.000244140625]:0.0004349124738914577, (0.000244140625,0.0009765625]:68.75} +-2/0/0/407a5c05888d260f/40d2aa08d6c06f03/1/-7_1,1_4,1_4/0,3f43aa8dcd5315f5,4053c00000000000,4051800000000000,4055800000000000,4059700000000000,0,4055400000000000,3f4899db6326e870/// {count:421.7513509286518, sum:19112.138107403192, (5.960464477539063e-08,9.5367431640625e-07]:0.0006001655953233308, (9.5367431640625e-07,1.52587890625e-05]:79, (1.52587890625e-05,0.000244140625]:70, (0.000244140625,0.00390625]:86, (0.0625,1]:101.75, (16,256]:85, (256,4096]:0.0007507630564795986} +-4/3fe0000000000000/0/0/0/3///// {count:0, sum:0} +-1/255bba08cf8c979d/4014000000000000/4073bc05b4869dbb/40d03bb730a8c0e4/0/-7_3/3f483e1bc460aff5,4053800000000000,0/-17_4,1_3/0,4044000000000000,3f4566192979cc81,4054700000000000,4043800000000000,402a000000000000,404d800000000000/ {count:315.75139286598613, sum:16622.862344921465, [-9.5367431640625e-07,-2.384185791015625e-07):59, [-2.384185791015625e-07,-5.960464477539063e-08):13, [-5.960464477539063e-08,-1.4901161193847656e-08):39, [-3.725290298461914e-09,-9.313225746154785e-10):81.75, [-9.313225746154785e-10,-2.3283064365386963e-10):0.0006530402051353608, [-2.3283064365386963e-10,-5.820766091346741e-11):40, [-1e-128,1e-128]:5, (1.52587890625e-05,6.103515625e-05]:0.0007398257810158337, (6.103515625e-05,0.000244140625]:78} +0/3f50624dd2f1a9fc/404f000000000000/4076e803fd306cca/40ca9dc9569918a8/0///-4_1,2_2,0_4/403f600000000000,3f4fe983664f7c80,0,405d880000000000,4026000000000000,4056000000000000,404c000000000000/ {count:366.5009738818741, sum:13627.572955262222, [-16,-8):56, [-8,-4):88, [-4,-2):11, [-2,-1):118.125, [-0.5,-0.25):0.0009738818740706728, [-0.0625,-0.03125):31.375, [-0.001,0.001]:62} +1/0/0/406c800000000000/40cb50bfd34524c0/0/3_2,1_1,1_1/404e000000000000,403d000000000000,404e000000000000,4053c00000000000/// {count:228, sum:13985.498634951538, (2,2.82842712474619]:60, (2.82842712474619,4]:29, (5.65685424949238,8]:60, (11.31370849898476,16]:79} +2/0/0/4054480000000000/40b0f29c9a52a00d/0/-13_1/4054480000000000/// {count:81.125, sum:4338.611729778361, (0.08838834764831843,0.1051120519067143]:81.125} +-4/0/3f4b4dbd2c7610e7/40826a048a221018/40a75eb15b3b6ea7/2/-15_3,2_2,2_1/3f3bc20af1adbdf4,4057c00000000000,0,4038000000000000,4018000000000000,3f4b85fd5a261456/-8_4,2_3,1_1/4058100000000000,404b800000000000,4045800000000000,4050800000000000,4050c00000000000,3f1f6b081088c64f,404a800000000000,4055000000000000/ {count:589.2522165928995, sum:2991.346399171121, [-4.294967296e+09,-65536):84, [-1,-1.52587890625e-05):53, [-1.52587890625e-05,-2.3283064365386963e-10):0.0001198505089028631, [-2.3283064365386963e-10,-3.552713678800501e-15):67, [-8.271806125530277e-25,-1.262177448353619e-29):66, [-1.262177448353619e-29,-1.925929944387236e-34):43, [-1.925929944387236e-34,-2.938735877055719e-39):55, [-2.938735877055719e-39,-4.484155085839415e-44):96.25, [-0,0]:0.0008332418155281546, (8.636168555094445e-78,5.659799424266695e-73]:0.00042355315388584104, (5.659799424266695e-73,3.7092061506874214e-68]:95, (1.044048714879764e-53,6.842277657836021e-49]:24, (6.842277657836021e-49,4.484155085839415e-44]:6, (1.925929944387236e-34,1.262177448353619e-29]:0.0008399474211705598} +5/255bba08cf8c979d/0/40770c0000000000/40b5fa8d74c0ae79/1/4_3,0_1/404c800000000000,4043800000000000,404ef00000000000,4056380000000000/-4_1/405e800000000000/ {count:368.75, sum:5626.552562754246, [-0.9170040432046711,-0.8973545375015533):122, (1.0671404006768235,1.0905077326652577]:57, (1.0905077326652577,1.1143867425958924]:39, (1.1143867425958924,1.1387886347566916]:61.875, (1.1387886347566916,1.1637248587775775]:88.875} +5/0/0/4064400710556858/40c19327f5a61c63/3/-2_3,2_2,2_2/3f4015141ed88149,4014000000000000,3f385883050fd155,0,0,4054000000000000,4053400000000000/// {count:162.00086228066607, sum:8998.312184108601, (0.9370838170551498,0.9576032806985735]:0.0004907940144143554, (0.9576032806985735,0.9785720620876999]:5, (0.9785720620876999,1]:0.0003714866516582223, (1.1387886347566916,1.1637248587775775]:80, (1.1637248587775775,1.189207115002721]:77} +0/3f847ae147ae147b/0/4075b203e4a2a823/40b7d2e96b597241/0/-15_3,0_3/4052000000000000,405a480000000000,4058000000000000,3f4d7879838a47e5,4052800000000000,3f0ac9bbd92a4212/// {count:347.1259504655629, sum:6098.911794271845, (1.52587890625e-05,3.0517578125e-05]:72, (3.0517578125e-05,6.103515625e-05]:105.125, (6.103515625e-05,0.0001220703125]:96, (0.0001220703125,0.000244140625]:0.0008993714646570143, (0.000244140625,0.00048828125]:74, (0.00048828125,0.0009765625]:5.1094098258212245e-05} +5/3f847ae147ae147b/4055c00000000000/4055c00000000000/4094695b49a7cbae/2///// {count:87, sum:1306.33914816075, [-0.01,0.01]:87} +-2/3f50624dd2f1a9fc/0/4050000000000000/c074ba1db757b67f/1/4_1,0_1/401c000000000000,404c800000000000/// {count:64, sum:-331.6322549272808, (4096,65536]:7, (65536,1.048576e+06]:57} +0/3fe0000000000000/0/4075ce01dd4d0692/40ce28744fb7f0d2/1/-11_4/4053b80000000000,405e400000000000,4052000000000000,4053400000000000/3_4/3f3dd4d069223133,0,0,0/ {count:348.8754551895919, sum:15440.908682816113, [-8,-4):0.00045518959189718154, (0.000244140625,0.00048828125]:78.875, (0.00048828125,0.0009765625]:121, (0.0009765625,0.001953125]:72, (0.001953125,0.00390625]:77} +0/3fe0000000000000/3f20901594b4379e/406c14063509d9f0/40c9c9a9524a50e8/1/-6_2,1_2,1_1/4041000000000000,4042500000000000,4055c00000000000,3f44b0220295c4b2,4050c00000000000/// {count:224.6257577125939, sum:13203.322823800569, [-0.5,0.5]:0.00012636435926832457, (0.0078125,0.015625]:34, (0.015625,0.03125]:36.625, (0.0625,0.125]:87, (0.125,0.25]:0.0006313482346468528, (0.5,1]:67} +-53/0/0/4024000000000000/4038a4e446df92d7/2/0_2/4024000000000000,0///3ff0000000000000,4014000000000000,4024000000000000 {count:10, sum:24.644108228286168, [-Inf,1]:10} +-1/255bba08cf8c979d/0/4081a80000000000/c0c61d084603aa97/1/-16_4,0_4,1_3/404bd00000000000,403e000000000000,0,4041000000000000,4050800000000000,4046800000000000,403c600000000000,4057800000000000,4052800000000000,4049000000000000,4056000000000000/// {count:565, sum:-11322.064636667534, (5.820766091346741e-11,2.3283064365386963e-10]:55.625, (2.3283064365386963e-10,9.313225746154785e-10]:30, (3.725290298461914e-09,1.4901161193847656e-08]:34, (1.4901161193847656e-08,5.960464477539063e-08]:66, (5.960464477539063e-08,2.384185791015625e-07]:45, (2.384185791015625e-07,9.5367431640625e-07]:28.375, (9.5367431640625e-07,3.814697265625e-06]:94, (1.52587890625e-05,6.103515625e-05]:74, (6.103515625e-05,0.000244140625]:50, (0.000244140625,0.0009765625]:88} +-4/255bba08cf8c979d/402e400000000000/407caa0113ee4a12/40de3cd98f49b46d/1/-3_3,2_4,1_2/403b000000000000,404a000000000000,0,4047800000000000,4028000000000000,0,4010000000000000,3f313ee4a128a361,4037000000000000/-16_4,2_1,2_2/402a000000000000,403c000000000000,4036800000000000,4054c00000000000,4049000000000000,4044000000000000,4045000000000000/ {count:458.62526314813397, sum:30963.399370599935, [-1.262177448353619e-29,-1.925929944387236e-34):42, [-1.925929944387236e-34,-2.938735877055719e-39):40, [-6.842277657836021e-49,-1.044048714879764e-53):50, [-2.4308653429145085e-63,-3.7092061506874214e-68):83, [-3.7092061506874214e-68,-5.659799424266695e-73):22.5, [-5.659799424266695e-73,-8.636168555094445e-78):28, [-8.636168555094445e-78,-1.3177747429038154e-82):13, [-1e-128,1e-128]:15.125, (5.421010862427522e-20,3.552713678800501e-15]:27, (3.552713678800501e-15,2.3283064365386963e-10]:52, (65536,4.294967296e+09]:47, (4.294967296e+09,2.81474976710656e+14]:12, (1.8446744073709552e+19,1.2089258196146292e+24]:4, (7.922816251426434e+28,5.192296858534828e+33]:0.00026314813399835017, (5.192296858534828e+33,3.402823669209385e+38]:23} +5/3f50624dd2f1a9fc/0/0/8000000000000000/3///// {count:0, sum:-0} +8/3f847ae147ae147b/4054800000000000/40716a00d423d86a/c088bf7fb4b025f5/2/-16_1,1_3/4039000000000000,3f2a847b0d4f57f6,4049800000000000,405e280000000000/// {count:278.6252023124895, sum:-791.9373563539108, [-0.01,0.01]:82, (0.9550139751351947,0.9576032806985735]:25, (0.9601996065815236,0.9628029718180622]:0.0002023124895049186, (0.9628029718180622,0.9654133954938133]:51, (0.9654133954938133,0.9680308967461471]:120.625} +-1/255bba08cf8c979d/4055400000000000/4072be0000000000/40a8ecb19995b81f/2/-9_1,2_4/4048000000000000,0,403b600000000000,4053a00000000000,404e800000000000/// {count:299.875, sum:3190.346874884344, [-1e-128,1e-128]:85, (9.5367431640625e-07,3.814697265625e-06]:48, (0.000244140625,0.0009765625]:27.375, (0.0009765625,0.00390625]:78.5, (0.00390625,0.015625]:61} +0/3f847ae147ae147b/404a000000000000/4063e8030e6cf560/4081209123fc57ba/3/-20_1,2_3/4036400000000000,4036000000000000,3f387367ab034118,404f000000000000/-13_2/3ff0000000000000,0/ {count:159.25037308960873, sum:548.0708694185516, [-0.0001220703125,-6.103515625e-05):1, [-0.01,0.01]:52, (4.76837158203125e-07,9.5367431640625e-07]:22.25, (3.814697265625e-06,7.62939453125e-06]:22, (7.62939453125e-06,1.52587890625e-05]:0.00037308960874062177, (1.52587890625e-05,3.0517578125e-05]:62} +-4/3f50624dd2f1a9fc/0/408b9402008af180/c0b46b244e186d84/2/4_3,0_4/0,4010000000000000,404a000000000000,4053200000000000,404a800000000000,4052400000000000,4059000000000000/5_3,2_4,1_4/404e000000000000,4057400000000000,4028000000000000,4054400000000000,4053c00000000000,4055400000000000,4048000000000000,3f5004578c010910,0,404e000000000000,4018000000000000/ {count:882.5009775977087, sum:-5227.141816641557, [-4.9732323640978664e+86,-7.588550360256754e+81):6, [-7.588550360256754e+81,-1.157920892373162e+77):60, [-1.7668470647783843e+72,-2.695994666715064e+67):0.0009775977087121447, [-4.113761393303015e+62,-6.277101735386681e+57):48, [-6.277101735386681e+57,-9.578097130411805e+52):85, [-9.578097130411805e+52,-1.461501637330903e+48):79, [-1.461501637330903e+48,-2.2300745198530623e+43):81, [-5.192296858534828e+33,-7.922816251426434e+28):12, [-7.922816251426434e+28,-1.2089258196146292e+24):93, [-1.2089258196146292e+24,-1.8446744073709552e+19):60, (1.8446744073709552e+19,1.2089258196146292e+24]:4, (1.2089258196146292e+24,7.922816251426434e+28]:52, (7.922816251426434e+28,5.192296858534828e+33]:76.5, (5.192296858534828e+33,3.402823669209385e+38]:53, (3.402823669209385e+38,2.2300745198530623e+43]:73, (2.2300745198530623e+43,1.461501637330903e+48]:100} +-53/0/0/0/0/0/////4000000000000000 {count:0, sum:0} +-1/3fe0000000000000/3f451c23ba20a83c/4064d005d3d0315d/40a5598b12aa98db/1/-17_4,1_3/4056000000000000,4044800000000000,3ff8000000000000,0,4042000000000000,0,3f1198e85aa23b6b/// {count:166.50071135199224, sum:2732.771626788291, [-0.5,0.5]:0.0006442236374415498, (1.4551915228366852e-11,5.820766091346741e-11]:88, (5.820766091346741e-11,2.3283064365386963e-10]:41, (2.3283064365386963e-10,9.313225746154785e-10]:1.5, (1.4901161193847656e-08,5.960464477539063e-08]:36, (2.384185791015625e-07,9.5367431640625e-07]:6.712835480214361e-05} +5/3f50624dd2f1a9fc/0/407da004bdf97b56/40bf8a2ca0dadb2a/3/9_3,0_1,0_2/0,402e000000000000,3f4d33d2909fe965,3f3177f2941fd20e,4010000000000000,0/-1_2,0_4,2_2/4056b00000000000,404f000000000000,404d000000000000,4043000000000000,4056000000000000,4047200000000000,0,4052000000000000/ {count:474.00115773633786, sum:8074.174329451091, [-1.189207115002721,-1.1637248587775775):72, [-1.0905077326652577,-1.0671404006768235):46.25, [-1.0671404006768235,-1.0442737824274138):88, [-1.0442737824274138,-1.0218971486541166):38, [-1.0218971486541166,-1):58, [-1,-0.9785720620876999):62, [-0.9785720620876999,-0.9576032806985735):90.75, (1.2152473599804687,1.241857812073484]:15, (1.241857812073484,1.2690509571917332]:0.0008911874913331395, (1.2690509571917332,1.2968395546510096]:0.0002665488465248008, (1.2968395546510096,1.325236643159741]:4} +-53/0/0/0/0/2/1_1/0///3fe0000000000000,3ff0000000000000,4000000000000000,4014000000000000 {count:0, sum:0} +-1/255bba08cf8c979d/0/0/0/2///// {count:0, sum:0} +8/255bba08cf8c979d/0/0/0/3///// {count:0, sum:0} +-53/0/0/0/0/0/////3ff0000000000000,4014000000000000,4024000000000000 {count:0, sum:0} +-2/255bba08cf8c979d/0/407aec0000000000/c0c5f6b501bf6e81/3/-16_4,1_1,0_3/4047800000000000,0,4050000000000000,4059500000000000,4054400000000000,0,4034200000000000,0/-5_2/405c580000000000,4010000000000000/ {count:430.75, sum:-11245.41411583801, [-1.52587890625e-05,-9.5367431640625e-07):4, [-9.5367431640625e-07,-5.960464477539063e-08):113.375, (3.3881317890172014e-21,5.421010862427522e-20]:47, (8.673617379884035e-19,1.3877787807814457e-17]:64, (1.3877787807814457e-17,2.220446049250313e-16]:101.25, (3.552713678800501e-15,5.684341886080802e-14]:81, (9.094947017729282e-13,1.4551915228366852e-11]:20.125} +-2/0/0/4064b80edc14cbae/40b53ea5f0cc1841/0/-10_1,0_3,0_2/4052000000000000,4050b80000000000,3f4262e9d0820b8b,4030e00000000000,0,0/-10_2,2_2/4024000000000000,3efb4dc57a9c81a3,3f3d19806b2a8e0f,3f49a63afccc53b9/ {count:165.75181392728467, sum:5438.64820552437, [-9.5367431640625e-07,-5.960464477539063e-08):0.0007827556633822542, [-5.960464477539063e-08,-3.725290298461914e-09):0.000444024898721391, [-1.4551915228366852e-11,-9.094947017729282e-13):2.6038927592898807e-05, [-9.094947017729282e-13,-5.684341886080802e-14):10, (5.684341886080802e-14,9.094947017729282e-13]:72, (9.094947017729282e-13,1.4551915228366852e-11]:66.875, (1.4551915228366852e-11,2.3283064365386963e-10]:0.0005611077949797411, (2.3283064365386963e-10,3.725290298461914e-09]:16.875} +-1/3f847ae147ae147b/0/4052b80000000000/4095b92e3d3d165a/1/6_2/4051b80000000000,4010000000000000/// {count:74.875, sum:1390.295155481805, (1024,4096]:70.875, (4096,16384]:4} +1/3fe0000000000000/0/40718a00b3c301d4/c0acbe7f64b2f3a9/1/-11_3/3f2678603a7aeac0,4050380000000000,403f000000000000/-12_3,2_2/0,4052f00000000000,402e000000000000,0,4057800000000000/ {count:280.62517143416085, sum:-3679.2488151476323, [-0.125,-0.08838834764831843):94, [-0.03125,-0.022097086912079608):15, [-0.022097086912079608,-0.015625):75.75, (0.015625,0.022097086912079608]:0.00017143416083860961, (0.022097086912079608,0.03125]:64.875, (0.03125,0.044194173824159216]:31} +3/3f50624dd2f1a9fc/4044400000000000/406290088825a224/40a922533829a9d0/2/4_1,1_2,0_3/0,3f435c1278746a1d,3f3d89082035127a,403d000000000000,4036000000000000,404c800000000000/// {count:148.50104148245111, sum:3217.1625378627577, [-0.001,0.001]:40.5, (1.5422108254079407,1.6817928305074288]:0.0005908098400227466, (1.6817928305074288,1.8340080864093422]:0.00045067261108425844, (1.8340080864093422,2]:29, (2,2.1810154653305154]:22, (2.1810154653305154,2.378414230005442]:57} diff --git a/src/promql/src/histogram_eval/tests.rs b/src/promql/src/histogram_eval/tests.rs index 41db4cd3ba5..122a94fc047 100644 --- a/src/promql/src/histogram_eval/tests.rs +++ b/src/promql/src/histogram_eval/tests.rs @@ -25,7 +25,7 @@ use datatypes::series::time::{EvalGrid, GridRange, GridSpec, TimeAxis}; use super::float_histogram::{AddOutcome, FloatHistogram}; use super::{ HistogramAggregate, HistogramFunction, NativeHistogramFunction, aggregate_histograms, - evaluate_histograms, histogram_quantiles, map_native_histogram, + evaluate_histograms, histogram_quantiles, histogram_string, map_native_histogram, }; use crate::grid_series::merge_same_labelset; use crate::grid_series::reference::{ @@ -862,3 +862,18 @@ fn native_histogram_functions_keep_the_runs_of_a_sparse_series() { ); } } + +/// Go `FloatHistogram.String` over fixed and random histograms (`narthex-golden-histstring`): +/// bucket order, inclusiveness, empty buckets skipped, the zero bucket, custom bounds and `%g`. +#[test] +fn histogram_string_matches_prometheus() { + for line in include_str!("string_cases.txt").lines() { + let (encoded, expected) = line.split_once('\t').expect("tab-separated case"); + let histogram = OwnedHistogram::parse(encoded); + assert_eq!( + histogram_string(&histogram.view()).unwrap(), + expected, + "{encoded}" + ); + } +}