fix(promql): return no point for zero-interval irate/idelta (#9455)

fix(promql): irate/idelta return no point for a zero-ms sample gap

Signed-off-by: Niladri Adhikary <niladrix719@gmail.com>
(cherry picked from commit 0dde3a7b70)
Signed-off-by: WenyXu <wenymedia@gmail.com>
This commit is contained in:
Niladri Adhikary
2026-10-12 12:49:21 +00:00
committed by WenyXu
parent 0ce6729b40
commit 98d9012f00
3 changed files with 140 additions and 2 deletions
+33 -2
View File
@@ -140,8 +140,13 @@ impl<const IS_RATE: bool> IDelta<IS_RATE> {
let last_offset = ts_offset + last_position;
let prev_offset = ts_offset + prev_position;
let sampled_interval =
(ts_values[last_offset] - ts_values[prev_offset]) as f64 / 1000.0;
let sampled_interval_ms = ts_values[last_offset] - ts_values[prev_offset];
// Sub-millisecond samples round to the same ms; skip instead of dividing by zero.
if sampled_interval_ms == 0 {
result_builder.append_null();
continue;
}
let sampled_interval = sampled_interval_ms as f64 / 1000.0;
let last_value = value_values[value_offset + last_position];
let prev_value = value_values[value_offset + prev_position];
@@ -263,4 +268,30 @@ mod test {
vec![Some(3.0), None, None],
);
}
#[test]
fn idelta_skips_samples_in_same_millisecond() {
// The last two samples of the first range share 999ms; the second range is 1ms apart.
let ts_array = Arc::new(TimestampMillisecondArray::from_iter_values([
999i64, 999, 999, 1000,
]));
let values_array = Arc::new(Float64Array::from_iter([10.0, 13.0, 999.0, 1000.0]));
let ranges = [(0, 3), (2, 2)];
simple_range_udf_runner(
IDelta::<false>::scalar_udf(),
RangeArray::from_ranges(ts_array.clone(), ranges).unwrap(),
RangeArray::from_ranges(values_array.clone(), ranges).unwrap(),
vec![],
vec![None, Some(1.0)],
);
simple_range_udf_runner(
IDelta::<true>::scalar_udf(),
RangeArray::from_ranges(ts_array, ranges).unwrap(),
RangeArray::from_ranges(values_array, ranges).unwrap(),
vec![],
vec![None, Some(1000.0)],
);
}
}
@@ -0,0 +1,77 @@
-- The last two 'sub_ms' samples are 400ns apart and share one millisecond, which
-- irate and idelta cannot resolve, so they return no point instead of inf.
-- The 'one_ms' samples are 1ms apart and still produce a rate.
CREATE TABLE sub_ms_interval (
job STRING,
`value` DOUBLE,
ts TIMESTAMP(9) TIME INDEX,
PRIMARY KEY(job)
);
Affected Rows: 0
INSERT INTO sub_ms_interval VALUES
('sub_ms', 999, 999999900),
('sub_ms', 10, 999999100),
('sub_ms', 13, 999999500),
('one_ms', 10, 998000000),
('one_ms', 13, 999000000);
Affected Rows: 5
SELECT * FROM sub_ms_interval ORDER BY job, ts;
+--------+-------+-------------------------------+
| job | value | ts |
+--------+-------+-------------------------------+
| one_ms | 10.0 | 1970-01-01T00:00:00.998 |
| one_ms | 13.0 | 1970-01-01T00:00:00.999 |
| sub_ms | 10.0 | 1970-01-01T00:00:00.999999100 |
| sub_ms | 13.0 | 1970-01-01T00:00:00.999999500 |
| sub_ms | 999.0 | 1970-01-01T00:00:00.999999900 |
+--------+-------+-------------------------------+
TQL EVAL (1, 1, '1s') irate(sub_ms_interval[5s]);
+---------------------+----------------------------+--------+
| ts | prom_irate(ts_range,value) | job |
+---------------------+----------------------------+--------+
| 1970-01-01T00:00:01 | 3000.0 | one_ms |
+---------------------+----------------------------+--------+
TQL EVAL (1, 1, '1s') idelta(sub_ms_interval[5s]);
+---------------------+-----------------------------+--------+
| ts | prom_idelta(ts_range,value) | job |
+---------------------+-----------------------------+--------+
| 1970-01-01T00:00:01 | 3.0 | one_ms |
+---------------------+-----------------------------+--------+
ADMIN FLUSH_TABLE('sub_ms_interval');
+--------------------------------------+
| ADMIN FLUSH_TABLE('sub_ms_interval') |
+--------------------------------------+
| 0 |
+--------------------------------------+
TQL EVAL (1, 1, '1s') irate(sub_ms_interval[5s]);
+---------------------+----------------------------+--------+
| ts | prom_irate(ts_range,value) | job |
+---------------------+----------------------------+--------+
| 1970-01-01T00:00:01 | 3000.0 | one_ms |
+---------------------+----------------------------+--------+
TQL EVAL (1, 1, '1s') idelta(sub_ms_interval[5s]);
+---------------------+-----------------------------+--------+
| ts | prom_idelta(ts_range,value) | job |
+---------------------+-----------------------------+--------+
| 1970-01-01T00:00:01 | 3.0 | one_ms |
+---------------------+-----------------------------+--------+
DROP TABLE sub_ms_interval;
Affected Rows: 0
@@ -0,0 +1,30 @@
-- The last two 'sub_ms' samples are 400ns apart and share one millisecond, which
-- irate and idelta cannot resolve, so they return no point instead of inf.
-- The 'one_ms' samples are 1ms apart and still produce a rate.
CREATE TABLE sub_ms_interval (
job STRING,
`value` DOUBLE,
ts TIMESTAMP(9) TIME INDEX,
PRIMARY KEY(job)
);
INSERT INTO sub_ms_interval VALUES
('sub_ms', 999, 999999900),
('sub_ms', 10, 999999100),
('sub_ms', 13, 999999500),
('one_ms', 10, 998000000),
('one_ms', 13, 999000000);
SELECT * FROM sub_ms_interval ORDER BY job, ts;
TQL EVAL (1, 1, '1s') irate(sub_ms_interval[5s]);
TQL EVAL (1, 1, '1s') idelta(sub_ms_interval[5s]);
ADMIN FLUSH_TABLE('sub_ms_interval');
TQL EVAL (1, 1, '1s') irate(sub_ms_interval[5s]);
TQL EVAL (1, 1, '1s') idelta(sub_ms_interval[5s]);
DROP TABLE sub_ms_interval;