Skip to content

Commit a1c9654

Browse files
fix(query-engine): validate sliding counter bounds early
1 parent c3eedc9 commit a1c9654

2 files changed

Lines changed: 50 additions & 27 deletions

File tree

‎asap-query-engine/src/engines/simple_engine/mod.rs‎

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1883,6 +1883,26 @@ impl SimpleEngine {
18831883
use crate::engines::query_result::RangeVectorElement;
18841884
use crate::engines::window_merger::create_window_merger;
18851885

1886+
if context.window_type == WindowType::Sliding
1887+
&& context.tumbling_window_ms > 0
1888+
&& matches!(
1889+
context.base.metadata.statistic_to_compute,
1890+
Statistic::Increase | Statistic::Rate
1891+
)
1892+
{
1893+
if let Some(&off_grid_timestamp) = context
1894+
.output_timestamps
1895+
.iter()
1896+
.find(|&&timestamp| !timestamp.is_multiple_of(context.tumbling_window_ms))
1897+
{
1898+
return Err(format!(
1899+
"Exact Prometheus counter bounds are unavailable for off-grid Sliding \
1900+
timestamp {} (grid interval {}ms)",
1901+
off_grid_timestamp, context.tumbling_window_ms
1902+
));
1903+
}
1904+
}
1905+
18861906
let lookback_ms = (context.lookback_bucket_count as u64) * context.tumbling_window_ms;
18871907

18881908
// Step 1: Fetch all data needed for the entire range. Sliding
@@ -2137,20 +2157,6 @@ impl SimpleEngine {
21372157
// (#581). One loop shape for topk and non-topk alike, rather than
21382158
// maintaining two.
21392159
for &current_time in &context.output_timestamps {
2140-
if context.window_type == WindowType::Sliding
2141-
&& context.tumbling_window_ms > 0
2142-
&& matches!(
2143-
context.base.metadata.statistic_to_compute,
2144-
Statistic::Increase | Statistic::Rate
2145-
)
2146-
&& !current_time.is_multiple_of(context.tumbling_window_ms)
2147-
{
2148-
return Err(format!(
2149-
"Exact Prometheus counter bounds are unavailable for off-grid Sliding \
2150-
timestamp {} (grid interval {}ms)",
2151-
current_time, context.tumbling_window_ms
2152-
));
2153-
}
21542160
let current_time_i64 = i64::try_from(current_time)
21552161
.map_err(|_| "Output timestamp exceeds signed timestamp range".to_string())?;
21562162
let query_range_ms = i64::try_from(context.query_range_ms)

‎asap-query-engine/src/tests/native_range_query_tests.rs‎

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -154,35 +154,35 @@ mod tests {
154154
let host = Some(vec!["host-a".to_string()]);
155155
let data = vec![
156156
(
157-
1_000,
157+
10_000,
158158
host.clone(),
159159
Box::new(IncreaseAccumulator::new_with_sample_count(
160160
crate::data_model::Measurement::new(100.0),
161-
0,
161+
9_000,
162162
crate::data_model::Measurement::new(110.0),
163-
1_000,
163+
10_000,
164164
2,
165165
)) as Box<dyn AggregateCore>,
166166
),
167167
(
168-
2_000,
168+
11_000,
169169
host.clone(),
170170
Box::new(IncreaseAccumulator::new_with_sample_count(
171171
crate::data_model::Measurement::new(110.0),
172-
1_000,
172+
10_000,
173173
crate::data_model::Measurement::new(120.0),
174-
2_000,
174+
11_000,
175175
2,
176176
)) as Box<dyn AggregateCore>,
177177
),
178178
(
179-
3_000,
179+
12_000,
180180
host,
181181
Box::new(IncreaseAccumulator::new_with_sample_count(
182182
crate::data_model::Measurement::new(120.0),
183-
2_000,
183+
11_000,
184184
crate::data_model::Measurement::new(130.0),
185-
3_000,
185+
12_000,
186186
2,
187187
)) as Box<dyn AggregateCore>,
188188
),
@@ -199,11 +199,28 @@ mod tests {
199199
);
200200

201201
// The stored Sliding windows are aligned to the 1s grid, so native
202-
// execution cannot represent the exact [t-2s, t] range at t=1.5s.
202+
// execution cannot represent the exact [t-2s, t] range at t=10.5s.
203203
// Returning None lets the caller use an exact Prometheus fallback.
204-
assert!(engine
205-
.handle_range_query_promql("rate(http_requests_total[2s])".to_string(), 1.5, 2.5, 1.0,)
206-
.is_none());
204+
assert!(
205+
engine
206+
.handle_range_query_promql(
207+
"rate(http_requests_total[2s])".to_string(),
208+
10.5,
209+
12.5,
210+
1.0,
211+
)
212+
.is_none()
213+
);
214+
215+
let (_, on_grid_result) = engine
216+
.handle_range_query_promql("rate(http_requests_total[2s])".to_string(), 11.0, 12.0, 1.0)
217+
.expect("on-grid Sliding counter query should use native execution");
218+
let on_grid_samples = matrix_values(on_grid_result)
219+
.into_iter()
220+
.find(|element| element.labels.labels.contains(&"host-a".to_string()))
221+
.expect("host-a result missing")
222+
.samples;
223+
assert_eq!(on_grid_samples.len(), 2);
207224
}
208225

209226
/// Checks every `(label_values, ts, expected_present, reason)` case

0 commit comments

Comments
 (0)