VictoriaMetrics/apptest/tests/key_concepts_test.go
Artem Fetishev d238011327
apptest: add tests for stale nans in instant query (#7621)
### Describe Your Changes

These are the integration tests that confirm that instant queries may
return stale NaNs when the query contains a rollup function.

The bug was reported at #5806. There is also a fix: #7275. The tests in
this PR will be used co confirm that the fix works.

Some test refactoring has been done along the way. Sorry, couldn't
resist.

### Checklist

The following checks are **mandatory**:

- [x] My change adheres [VictoriaMetrics contributing
guidelines](https://docs.victoriametrics.com/contributing/).

---------

Signed-off-by: Artem Fetishev <rtm@victoriametrics.com>
2024-11-22 17:38:13 +01:00

204 lines
8.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package tests
import (
"testing"
"github.com/VictoriaMetrics/VictoriaMetrics/apptest"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
// Data used in examples in
// https://docs.victoriametrics.com/keyconcepts/#instant-query and
// https://docs.victoriametrics.com/keyconcepts/#range-query
var docData = []string{
"foo_bar 1.00 1652169600000", // 2022-05-10T08:00:00Z
"foo_bar 2.00 1652169660000", // 2022-05-10T08:01:00Z
"foo_bar 3.00 1652169720000", // 2022-05-10T08:02:00Z
"foo_bar 5.00 1652169840000", // 2022-05-10T08:04:00Z, one point missed
"foo_bar 5.50 1652169960000", // 2022-05-10T08:06:00Z, one point missed
"foo_bar 5.50 1652170020000", // 2022-05-10T08:07:00Z
"foo_bar 4.00 1652170080000", // 2022-05-10T08:08:00Z
"foo_bar 3.50 1652170260000", // 2022-05-10T08:11:00Z, two points missed
"foo_bar 3.25 1652170320000", // 2022-05-10T08:12:00Z
"foo_bar 3.00 1652170380000", // 2022-05-10T08:13:00Z
"foo_bar 2.00 1652170440000", // 2022-05-10T08:14:00Z
"foo_bar 1.00 1652170500000", // 2022-05-10T08:15:00Z
"foo_bar 4.00 1652170560000", // 2022-05-10T08:16:00Z
}
// TestSingleKeyConceptsQuery verifies cases from https://docs.victoriametrics.com/keyconcepts/#query-data
// for vm-single.
func TestSingleKeyConceptsQuery(t *testing.T) {
tc := apptest.NewTestCase(t)
defer tc.Stop()
sut := tc.MustStartDefaultVmsingle()
testKeyConceptsQueryData(t, sut)
}
// TestClusterKeyConceptsQueryData verifies cases from https://docs.victoriametrics.com/keyconcepts/#query-data
// for vm-cluster.
func TestClusterKeyConceptsQueryData(t *testing.T) {
tc := apptest.NewTestCase(t)
defer tc.Stop()
sut := tc.MustStartDefaultCluster()
testKeyConceptsQueryData(t, sut)
}
// testClusterKeyConceptsQuery verifies cases from https://docs.victoriametrics.com/keyconcepts/#query-data
func testKeyConceptsQueryData(t *testing.T, sut apptest.PrometheusWriteQuerier) {
opts := apptest.QueryOpts{Timeout: "5s", Tenant: "0"}
// Insert example data from documentation.
sut.PrometheusAPIV1ImportPrometheus(t, docData, opts)
sut.ForceFlush(t)
testInstantQuery(t, sut, opts)
testRangeQuery(t, sut, opts)
testRangeQueryIsEquivalentToManyInstantQueries(t, sut, opts)
}
// testInstantQuery verifies the statements made in the `Instant query` section
// of the VictoriaMetrics documentation. See:
// https://docs.victoriametrics.com/keyconcepts/#instant-query
func testInstantQuery(t *testing.T, q apptest.PrometheusQuerier, opts apptest.QueryOpts) {
// Get the value of the foo_bar time series at 2022-05-10T08:03:00Z with the
// step of 5m and timeout 5s. There is no sample at exactly this timestamp.
// Therefore, VictoriaMetrics will search for the nearest sample within the
// [time-5m..time] interval.
got := q.PrometheusAPIV1Query(t, "foo_bar", "2022-05-10T08:03:00.000Z", "5m", opts)
want := apptest.NewPrometheusAPIV1QueryResponse(t, `{"data":{"result":[{"metric":{"__name__":"foo_bar"},"value":[1652169780,"3"]}]}}`)
opt := cmpopts.IgnoreFields(apptest.PrometheusAPIV1QueryResponse{}, "Status", "Data.ResultType")
if diff := cmp.Diff(want, got, opt); diff != "" {
t.Errorf("unexpected response (-want, +got):\n%s", diff)
}
// Get the value of the foo_bar time series at 2022-05-10T08:18:00Z with the
// step of 1m and timeout 5s. There is no sample at this timestamp.
// Therefore, VictoriaMetrics will search for the nearest sample within the
// [time-1m..time] interval. Since the nearest sample is 2m away and the
// step is 1m, then the VictoriaMetrics must return empty response.
got = q.PrometheusAPIV1Query(t, "foo_bar", "2022-05-10T08:18:00.000Z", "1m", opts)
if len(got.Data.Result) > 0 {
t.Errorf("unexpected response: got non-empty result, want empty result:\n%v", got)
}
}
// testRangeQuery verifies the statements made in the `Range query` section of
// the VictoriaMetrics documentation. See:
// https://docs.victoriametrics.com/keyconcepts/#range-query
func testRangeQuery(t *testing.T, q apptest.PrometheusQuerier, opts apptest.QueryOpts) {
f := func(start, end, step string, wantSamples []*apptest.Sample) {
t.Helper()
got := q.PrometheusAPIV1QueryRange(t, "foo_bar", start, end, step, opts)
want := apptest.NewPrometheusAPIV1QueryResponse(t, `{"data": {"result": [{"metric": {"__name__": "foo_bar"}, "values": []}]}}`)
want.Data.Result[0].Samples = wantSamples
opt := cmpopts.IgnoreFields(apptest.PrometheusAPIV1QueryResponse{}, "Status", "Data.ResultType")
if diff := cmp.Diff(want, got, opt); diff != "" {
t.Errorf("unexpected response (-want, +got):\n%s", diff)
}
}
// Verify the statement that the query result for
// [2022-05-10T07:59:00Z..2022-05-10T08:17:00Z] time range and 1m step will
// contain 17 points.
f("2022-05-10T07:59:00.000Z", "2022-05-10T08:17:00.000Z", "1m", []*apptest.Sample{
// Sample for 2022-05-10T07:59:00Z is missing because the time series has
// samples only starting from 8:00.
apptest.NewSample(t, "2022-05-10T08:00:00Z", 1),
apptest.NewSample(t, "2022-05-10T08:01:00Z", 2),
apptest.NewSample(t, "2022-05-10T08:02:00Z", 3),
apptest.NewSample(t, "2022-05-10T08:03:00Z", 3),
apptest.NewSample(t, "2022-05-10T08:04:00Z", 5),
apptest.NewSample(t, "2022-05-10T08:05:00Z", 5),
apptest.NewSample(t, "2022-05-10T08:06:00Z", 5.5),
apptest.NewSample(t, "2022-05-10T08:07:00Z", 5.5),
apptest.NewSample(t, "2022-05-10T08:08:00Z", 4),
apptest.NewSample(t, "2022-05-10T08:09:00Z", 4),
// Sample for 2022-05-10T08:10:00Z is missing because there is no sample
// within the [8:10 - 1m .. 8:10] interval.
apptest.NewSample(t, "2022-05-10T08:11:00Z", 3.5),
apptest.NewSample(t, "2022-05-10T08:12:00Z", 3.25),
apptest.NewSample(t, "2022-05-10T08:13:00Z", 3),
apptest.NewSample(t, "2022-05-10T08:14:00Z", 2),
apptest.NewSample(t, "2022-05-10T08:15:00Z", 1),
apptest.NewSample(t, "2022-05-10T08:16:00Z", 4),
apptest.NewSample(t, "2022-05-10T08:17:00Z", 4),
})
// Verify the statement that a query is executed at start, start+step,
// start+2*step, …, step+N*step timestamps, where N is the whole number
// of steps that fit between start and end.
f("2022-05-10T08:00:01.000Z", "2022-05-10T08:02:00.000Z", "1m", []*apptest.Sample{
apptest.NewSample(t, "2022-05-10T08:00:01Z", 1),
apptest.NewSample(t, "2022-05-10T08:01:01Z", 2),
})
// Verify the statement that a query is executed at start, start+step,
// start+2*step, …, end timestamps, when end = start + N*step.
f("2022-05-10T08:00:00.000Z", "2022-05-10T08:02:00.000Z", "1m", []*apptest.Sample{
apptest.NewSample(t, "2022-05-10T08:00:00Z", 1),
apptest.NewSample(t, "2022-05-10T08:01:00Z", 2),
apptest.NewSample(t, "2022-05-10T08:02:00Z", 3),
})
// If the step isnt set, then it defaults to 5m (5 minutes).
f("2022-05-10T07:59:00.000Z", "2022-05-10T08:17:00.000Z", "", []*apptest.Sample{
// Sample for 2022-05-10T07:59:00Z is missing because the time series has
// samples only starting from 8:00.
apptest.NewSample(t, "2022-05-10T08:04:00Z", 5),
apptest.NewSample(t, "2022-05-10T08:09:00Z", 4),
apptest.NewSample(t, "2022-05-10T08:14:00Z", 2),
})
}
// testRangeQueryIsEquivalentToManyInstantQueries verifies the statement made in
// the `Range query` section of the VictoriaMetrics documentation that a range
// query is actually an instant query executed 1 + (start-end)/step times on the
// time range from start to end. See:
// https://docs.victoriametrics.com/keyconcepts/#range-query
func testRangeQueryIsEquivalentToManyInstantQueries(t *testing.T, q apptest.PrometheusQuerier, opts apptest.QueryOpts) {
f := func(timestamp string, want *apptest.Sample) {
t.Helper()
gotInstant := q.PrometheusAPIV1Query(t, "foo_bar", timestamp, "1m", opts)
if want == nil {
if got, want := len(gotInstant.Data.Result), 0; got != want {
t.Errorf("unexpected instant result size: got %d, want %d", got, want)
}
} else {
got := gotInstant.Data.Result[0].Sample
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("unexpected instant sample (-want, +got):\n%s", diff)
}
}
}
rangeRes := q.PrometheusAPIV1QueryRange(t, "foo_bar", "2022-05-10T07:59:00.000Z", "2022-05-10T08:17:00.000Z", "1m", opts)
rangeSamples := rangeRes.Data.Result[0].Samples
f("2022-05-10T07:59:00.000Z", nil)
f("2022-05-10T08:00:00.000Z", rangeSamples[0])
f("2022-05-10T08:01:00.000Z", rangeSamples[1])
f("2022-05-10T08:02:00.000Z", rangeSamples[2])
f("2022-05-10T08:03:00.000Z", rangeSamples[3])
f("2022-05-10T08:04:00.000Z", rangeSamples[4])
f("2022-05-10T08:05:00.000Z", rangeSamples[5])
f("2022-05-10T08:06:00.000Z", rangeSamples[6])
f("2022-05-10T08:07:00.000Z", rangeSamples[7])
f("2022-05-10T08:08:00.000Z", rangeSamples[8])
f("2022-05-10T08:09:00.000Z", rangeSamples[9])
f("2022-05-10T08:10:00.000Z", nil)
f("2022-05-10T08:11:00.000Z", rangeSamples[10])
f("2022-05-10T08:12:00.000Z", rangeSamples[11])
f("2022-05-10T08:13:00.000Z", rangeSamples[12])
f("2022-05-10T08:14:00.000Z", rangeSamples[13])
f("2022-05-10T08:15:00.000Z", rangeSamples[14])
f("2022-05-10T08:16:00.000Z", rangeSamples[15])
f("2022-05-10T08:17:00.000Z", rangeSamples[16])
}