From 881dffd6a296e4384f6393dc2da680b41193ac6e Mon Sep 17 00:00:00 2001 From: Alexander Marshalov <_@marshalov.org> Date: Thu, 22 Feb 2024 04:19:32 +0100 Subject: [PATCH] [metricsql] for subqueries fixed behavior of functions that use query time range in their logic (like `start`, `end`, and `running_*`), from now on when using in subqueries they respect time range from query parameters (#5794) --- app/vmselect/promql/eval.go | 15 +++++++++++ app/vmselect/promql/transform.go | 45 +++++++++++++++++++++++++------- docs/CHANGELOG.md | 1 + 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/app/vmselect/promql/eval.go b/app/vmselect/promql/eval.go index dbb8446f6..98d0fd2c4 100644 --- a/app/vmselect/promql/eval.go +++ b/app/vmselect/promql/eval.go @@ -111,6 +111,11 @@ type EvalConfig struct { End int64 Step int64 + // RealStart contains the original start of the interval when executed in subqueries (because Start can be changed for subqueries) + RealStart int64 + // RealEnd contains the original end of the interval when executed in subqueries (because End can be changed for subqueries) + RealEnd int64 + // MaxSeries is the maximum number of time series, which can be scanned by the query. // Zero means 'no limit' MaxSeries int @@ -152,7 +157,17 @@ type EvalConfig struct { func copyEvalConfig(src *EvalConfig) *EvalConfig { var ec EvalConfig ec.Start = src.Start + if ec.RealStart > 0 { + ec.RealStart = src.RealStart + } else { + ec.RealStart = src.Start + } ec.End = src.End + if ec.RealEnd > 0 { + ec.RealEnd = src.RealEnd + } else { + ec.RealEnd = src.End + } ec.Step = src.Step ec.MaxSeries = src.MaxSeries ec.MaxPointsPerSeries = src.MaxPointsPerSeries diff --git a/app/vmselect/promql/transform.go b/app/vmselect/promql/transform.go index a52055dbb..d2b27d1b0 100644 --- a/app/vmselect/promql/transform.go +++ b/app/vmselect/promql/transform.go @@ -1248,8 +1248,13 @@ func newTransformFuncRunning(rf func(a, b float64, idx int) float64) transformFu prevValue := values[0] values = values[1:] for i, v := range values { - if !math.IsNaN(v) { - prevValue = rf(prevValue, v, i+1) + if tfa.ec.RealEnd > 0 && ts.Timestamps[i] > tfa.ec.RealEnd { + break + } + if ts.Timestamps[i] >= tfa.ec.RealStart { + if !math.IsNaN(v) { + prevValue = rf(prevValue, v, i+1) + } } values[i] = prevValue } @@ -1265,7 +1270,7 @@ func newTransformFuncRange(rf func(a, b float64, idx int) float64) transformFunc if err != nil { return nil, err } - setLastValues(rvs) + setLastValues(tfa, rvs) return rvs, nil } } @@ -1539,7 +1544,7 @@ func transformRangeQuantile(tfa *transformFuncArg) ([]*timeseries, error) { } a.A = values putFloat64s(a) - setLastValues(rvs) + setLastValues(tfa, rvs) return rvs, nil } @@ -1554,9 +1559,14 @@ func transformRangeFirst(tfa *transformFuncArg) ([]*timeseries, error) { if len(values) == 0 { continue } + vFirstSet := false vFirst := values[0] for i, v := range values { - if math.IsNaN(v) { + if !vFirstSet && ts.Timestamps[i] >= tfa.ec.RealStart { + vFirst = v + vFirstSet = true + } + if !vFirstSet && math.IsNaN(v) { continue } values[i] = vFirst @@ -1571,17 +1581,28 @@ func transformRangeLast(tfa *transformFuncArg) ([]*timeseries, error) { return nil, err } rvs := args[0] - setLastValues(rvs) + setLastValues(tfa, rvs) return rvs, nil } -func setLastValues(tss []*timeseries) { +func setLastValues(tfa *transformFuncArg, tss []*timeseries) { for _, ts := range tss { values := skipTrailingNaNs(ts.Values) if len(values) == 0 { continue } vLast := values[len(values)-1] + if tfa.ec.RealEnd > 0 { + for i := len(values) - 1; i >= 0; i-- { + if math.IsNaN(values[i]) { + continue + } + if ts.Timestamps[i] > tfa.ec.RealEnd { + continue + } + vLast = values[i] + } + } for i, v := range values { if math.IsNaN(v) { continue @@ -2713,11 +2734,17 @@ func transformStep(tfa *transformFuncArg) float64 { } func transformStart(tfa *transformFuncArg) float64 { - return float64(tfa.ec.Start) / 1e3 + if tfa.ec.RealStart == 0 { + return float64(tfa.ec.Start) / 1e3 + } + return float64(tfa.ec.RealStart) / 1e3 } func transformEnd(tfa *transformFuncArg) float64 { - return float64(tfa.ec.End) / 1e3 + if tfa.ec.RealEnd == 0 { + return float64(tfa.ec.End) / 1e3 + } + return float64(tfa.ec.RealEnd) / 1e3 } // copyTimeseries returns a copy of tss. diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index a7c498ec5..eaa6f64a0 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -41,6 +41,7 @@ See also [LTS releases](https://docs.victoriametrics.com/LTS-releases.html). * BUGFIX: fix the misleading error `0ms is out of allowed range [0 ...` when passing `step=0` to [/api/v1/query](https://docs.victoriametrics.com/keyconcepts/#instant-query) or [/api/v1/query_range](https://docs.victoriametrics.com/keyconcepts/#range-query). See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5795). * BUGFIX: [vmalert](https://docs.victoriametrics.com/#vmalert): consistently sort groups by name and filename on `/groups` page in UI. This should prevent non-deterministic sorting for groups with identical names. +* BUGFIX: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): For subqueries fix behavior of functions that use query time range in their logic (like `start`, `end`, and `running_*`), from now on when using in subqueries they respect time range from query parameters. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5794) for details. ## [v1.98.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.98.0)