mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-23 20:37:12 +01:00
b09272ccac
Some checks are pending
build / Build (push) Waiting to run
CodeQL Go / Analyze (push) Waiting to run
main / lint (push) Waiting to run
main / test (test-full) (push) Blocked by required conditions
main / test (test-full-386) (push) Blocked by required conditions
main / test (test-pure) (push) Blocked by required conditions
publish-docs / Build (push) Waiting to run
1. Avoid storing the last evaluation results outside of rules, check for stale time series as soon as possible; 2. remove duplicated template `Clone()`. This pull request is primarily reducing memory usage when rules produce large volumes of results, as seen in https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6894. The CPU time spent on garbage collection remains high and may be addressed in a separate PR.
104 lines
3.4 KiB
Go
104 lines
3.4 KiB
Go
package unittest
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"reflect"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/datasource"
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
|
|
"github.com/VictoriaMetrics/metricsql"
|
|
)
|
|
|
|
// metricsqlTestCase holds metricsql_expr_test cases defined in test file
|
|
type metricsqlTestCase struct {
|
|
Expr string `yaml:"expr"`
|
|
EvalTime *promutils.Duration `yaml:"eval_time"`
|
|
ExpSamples []expSample `yaml:"exp_samples"`
|
|
}
|
|
|
|
type expSample struct {
|
|
Labels string `yaml:"labels"`
|
|
Value float64 `yaml:"value"`
|
|
}
|
|
|
|
// checkMetricsqlCase will check metricsql_expr_test cases
|
|
func checkMetricsqlCase(cases []metricsqlTestCase, q datasource.QuerierBuilder) (checkErrs []error) {
|
|
queries := q.BuildWithParams(datasource.QuerierParams{QueryParams: url.Values{"nocache": {"1"}, "latency_offset": {"1ms"}}, DataSourceType: "prometheus"})
|
|
Outer:
|
|
for _, mt := range cases {
|
|
result, _, err := queries.Query(context.Background(), mt.Expr, durationToTime(mt.EvalTime))
|
|
if err != nil {
|
|
checkErrs = append(checkErrs, fmt.Errorf(" expr: %q, time: %s, err: %w", mt.Expr,
|
|
mt.EvalTime.Duration().String(), err))
|
|
continue
|
|
}
|
|
var gotSamples []parsedSample
|
|
for _, s := range result.Data {
|
|
sort.Slice(s.Labels, func(i, j int) bool {
|
|
return s.Labels[i].Name < s.Labels[j].Name
|
|
})
|
|
gotSamples = append(gotSamples, parsedSample{
|
|
Labels: s.Labels,
|
|
Value: s.Values[0],
|
|
})
|
|
}
|
|
var expSamples []parsedSample
|
|
for _, s := range mt.ExpSamples {
|
|
expLb := []prompbmarshal.Label{}
|
|
if s.Labels != "" {
|
|
metricsqlExpr, err := metricsql.Parse(s.Labels)
|
|
if err != nil {
|
|
checkErrs = append(checkErrs, fmt.Errorf("\n expr: %q, time: %s, err: %v", mt.Expr,
|
|
mt.EvalTime.Duration().String(), fmt.Errorf("failed to parse labels %q: %w", s.Labels, err)))
|
|
continue Outer
|
|
}
|
|
metricsqlMetricExpr, ok := metricsqlExpr.(*metricsql.MetricExpr)
|
|
if !ok || len(metricsqlMetricExpr.LabelFilterss) > 1 {
|
|
checkErrs = append(checkErrs, fmt.Errorf("\n expr: %q, time: %s, err: %v", mt.Expr,
|
|
mt.EvalTime.Duration().String(), fmt.Errorf("got invalid exp_samples: %q", s.Labels)))
|
|
continue Outer
|
|
}
|
|
if len(metricsqlMetricExpr.LabelFilterss) > 0 {
|
|
for _, l := range metricsqlMetricExpr.LabelFilterss[0] {
|
|
expLb = append(expLb, prompbmarshal.Label{
|
|
Name: l.Label,
|
|
Value: l.Value,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
sort.Slice(expLb, func(i, j int) bool {
|
|
return expLb[i].Name < expLb[j].Name
|
|
})
|
|
expSamples = append(expSamples, parsedSample{
|
|
Labels: expLb,
|
|
Value: s.Value,
|
|
})
|
|
}
|
|
sort.Slice(expSamples, func(i, j int) bool {
|
|
return datasource.LabelCompare(expSamples[i].Labels, expSamples[j].Labels) <= 0
|
|
})
|
|
sort.Slice(gotSamples, func(i, j int) bool {
|
|
return datasource.LabelCompare(gotSamples[i].Labels, gotSamples[j].Labels) <= 0
|
|
})
|
|
if !reflect.DeepEqual(expSamples, gotSamples) {
|
|
checkErrs = append(checkErrs, fmt.Errorf("\n expr: %q, time: %s,\n exp: %v\n got: %v", mt.Expr,
|
|
mt.EvalTime.Duration().String(), parsedSamplesString(expSamples), parsedSamplesString(gotSamples)))
|
|
}
|
|
|
|
}
|
|
return
|
|
}
|
|
|
|
func durationToTime(pd *promutils.Duration) time.Time {
|
|
if pd == nil {
|
|
return time.Time{}
|
|
}
|
|
return time.UnixMilli(pd.Duration().Milliseconds())
|
|
}
|