mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-03-11 15:34:56 +00:00
app/vmselect/promql: return at most one time series from absent_over_time()
in the same way as Prometheus does
See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2130
This commit is contained in:
parent
70cfb87c0e
commit
989668beba
3 changed files with 51 additions and 19 deletions
|
@ -650,6 +650,9 @@ func evalRollupFuncWithoutAt(ec *EvalConfig, funcName string, rf rollupFunc, exp
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if funcName == "absent_over_time" {
|
||||||
|
rvs = aggregateAbsentOverTime(ec, re.Expr, rvs)
|
||||||
|
}
|
||||||
ec.updateIsPartialResponse(ecNew.IsPartialResponse)
|
ec.updateIsPartialResponse(ecNew.IsPartialResponse)
|
||||||
if offset != 0 && len(rvs) > 0 {
|
if offset != 0 && len(rvs) > 0 {
|
||||||
// Make a copy of timestamps, since they may be used in other values.
|
// Make a copy of timestamps, since they may be used in other values.
|
||||||
|
@ -665,6 +668,27 @@ func evalRollupFuncWithoutAt(ec *EvalConfig, funcName string, rf rollupFunc, exp
|
||||||
return rvs, nil
|
return rvs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// aggregateAbsentOverTime collapses tss to a single time series with 1 and nan values.
|
||||||
|
//
|
||||||
|
// Values for returned series are set to nan if at least a single tss series contains nan at that point.
|
||||||
|
// This means that tss contains a series with non-empty results at that point.
|
||||||
|
// This follows Prometheus logic - see https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2130
|
||||||
|
func aggregateAbsentOverTime(ec *EvalConfig, expr metricsql.Expr, tss []*timeseries) []*timeseries {
|
||||||
|
rvs := getAbsentTimeseries(ec, expr)
|
||||||
|
if len(tss) == 0 {
|
||||||
|
return rvs
|
||||||
|
}
|
||||||
|
for i := range tss[0].Values {
|
||||||
|
for _, ts := range tss {
|
||||||
|
if math.IsNaN(ts.Values[i]) {
|
||||||
|
rvs[0].Values[i] = nan
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rvs
|
||||||
|
}
|
||||||
|
|
||||||
func evalRollupFuncWithSubquery(ec *EvalConfig, funcName string, rf rollupFunc, expr metricsql.Expr, re *metricsql.RollupExpr) ([]*timeseries, error) {
|
func evalRollupFuncWithSubquery(ec *EvalConfig, funcName string, rf rollupFunc, expr metricsql.Expr, re *metricsql.RollupExpr) ([]*timeseries, error) {
|
||||||
// TODO: determine whether to use rollupResultCacheV here.
|
// TODO: determine whether to use rollupResultCacheV here.
|
||||||
step := re.Step.Duration(ec.Step)
|
step := re.Step.Duration(ec.Step)
|
||||||
|
@ -688,10 +712,6 @@ func evalRollupFuncWithSubquery(ec *EvalConfig, funcName string, rf rollupFunc,
|
||||||
}
|
}
|
||||||
ec.updateIsPartialResponse(ecSQ.IsPartialResponse)
|
ec.updateIsPartialResponse(ecSQ.IsPartialResponse)
|
||||||
if len(tssSQ) == 0 {
|
if len(tssSQ) == 0 {
|
||||||
if funcName == "absent_over_time" {
|
|
||||||
tss := evalNumber(ec, 1)
|
|
||||||
return tss, nil
|
|
||||||
}
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
sharedTimestamps := getTimestamps(ec.Start, ec.End, ec.Step)
|
sharedTimestamps := getTimestamps(ec.Start, ec.End, ec.Step)
|
||||||
|
@ -842,14 +862,7 @@ func evalRollupFuncWithMetricExpr(ec *EvalConfig, funcName string, rf rollupFunc
|
||||||
rssLen := rss.Len()
|
rssLen := rss.Len()
|
||||||
if rssLen == 0 {
|
if rssLen == 0 {
|
||||||
rss.Cancel()
|
rss.Cancel()
|
||||||
var tss []*timeseries
|
tss := mergeTimeseries(tssCached, nil, start, ec)
|
||||||
if funcName == "absent_over_time" {
|
|
||||||
tss = getAbsentTimeseries(ec, me)
|
|
||||||
}
|
|
||||||
// Add missing points until ec.End.
|
|
||||||
// Do not cache the result, since missing points
|
|
||||||
// may be backfilled in the future.
|
|
||||||
tss = mergeTimeseries(tssCached, tss, start, ec)
|
|
||||||
return tss, nil
|
return tss, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -877,19 +877,37 @@ func TestExecSuccess(t *testing.T) {
|
||||||
resultExpected := []netstorage.Result{r}
|
resultExpected := []netstorage.Result{r}
|
||||||
f(q, resultExpected)
|
f(q, resultExpected)
|
||||||
})
|
})
|
||||||
t.Run(`absent_over_time(scalar(multi-timeseries))`, func(t *testing.T) {
|
t.Run(`absent_over_time(non-nan)`, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
q := `
|
q := `
|
||||||
absent_over_time(label_set(scalar(1 or label_set(2, "xx", "foo")), "yy", "foo"))`
|
absent_over_time(time())`
|
||||||
|
resultExpected := []netstorage.Result{}
|
||||||
|
f(q, resultExpected)
|
||||||
|
})
|
||||||
|
t.Run(`absent_over_time(nan)`, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
q := `
|
||||||
|
absent_over_time((time() < 1500)[300s:])`
|
||||||
r := netstorage.Result{
|
r := netstorage.Result{
|
||||||
MetricName: metricNameExpected,
|
MetricName: metricNameExpected,
|
||||||
Values: []float64{1, 1, 1, 1, 1, 1},
|
Values: []float64{nan, nan, nan, nan, 1, 1},
|
||||||
|
Timestamps: timestampsExpected,
|
||||||
|
}
|
||||||
|
resultExpected := []netstorage.Result{r}
|
||||||
|
f(q, resultExpected)
|
||||||
|
})
|
||||||
|
t.Run(`absent_over_time(multi-ts)`, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
q := `
|
||||||
|
absent_over_time((
|
||||||
|
alias((time() < 1400)[200s:], "one"),
|
||||||
|
alias((time() > 1600)[200s:], "two"),
|
||||||
|
))`
|
||||||
|
r := netstorage.Result{
|
||||||
|
MetricName: metricNameExpected,
|
||||||
|
Values: []float64{nan, nan, nan, 1, nan, nan},
|
||||||
Timestamps: timestampsExpected,
|
Timestamps: timestampsExpected,
|
||||||
}
|
}
|
||||||
r.MetricName.Tags = []storage.Tag{{
|
|
||||||
Key: []byte("yy"),
|
|
||||||
Value: []byte("foo"),
|
|
||||||
}}
|
|
||||||
resultExpected := []netstorage.Result{r}
|
resultExpected := []netstorage.Result{r}
|
||||||
f(q, resultExpected)
|
f(q, resultExpected)
|
||||||
})
|
})
|
||||||
|
|
|
@ -34,6 +34,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
|
||||||
* FEATURE: add `__meta_kubernetes_endpointslice_label*` and `__meta_kubernetes_endpointslice_annotation*` labels for `role: endpointslice` targets in [kubernetes_sd_config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config) to be consistent with other `role` values. See [this issue](https://github.com/prometheus/prometheus/issues/10284).
|
* FEATURE: add `__meta_kubernetes_endpointslice_label*` and `__meta_kubernetes_endpointslice_annotation*` labels for `role: endpointslice` targets in [kubernetes_sd_config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config) to be consistent with other `role` values. See [this issue](https://github.com/prometheus/prometheus/issues/10284).
|
||||||
* FEATURE: vmagent: support Prometheus-like durations in `-promscrape.config`. See [this comment](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/817#issuecomment-1033384766).
|
* FEATURE: vmagent: support Prometheus-like durations in `-promscrape.config`. See [this comment](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/817#issuecomment-1033384766).
|
||||||
|
|
||||||
|
* BUGFIX: calculate [absent_over_time()](https://docs.victoriametrics.com/MetricsQL.html#absent_over_time) in the same way as Prometheus does. Previously it could return multiple time series instead of at most one time series like Prometheus does. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2130).
|
||||||
* BUGFIX: return proper results from `highestMax()` function at [Graphite render API](https://docs.victoriametrics.com/#graphite-render-api-usage). Previously it was incorrectly returning timeseries with min peaks instead of max peaks.
|
* BUGFIX: return proper results from `highestMax()` function at [Graphite render API](https://docs.victoriametrics.com/#graphite-render-api-usage). Previously it was incorrectly returning timeseries with min peaks instead of max peaks.
|
||||||
* BUGFIX: properly limit indexdb cache sizes. Previously they could exceed values set via `-memory.allowedPercent` and/or `-memory.allowedBytes` when `indexdb` contained many data parts. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2007).
|
* BUGFIX: properly limit indexdb cache sizes. Previously they could exceed values set via `-memory.allowedPercent` and/or `-memory.allowedBytes` when `indexdb` contained many data parts. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2007).
|
||||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix a bug, which could break time range picker when editing `From` or `To` input fields. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2080).
|
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix a bug, which could break time range picker when editing `From` or `To` input fields. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2080).
|
||||||
|
|
Loading…
Reference in a new issue