diff --git a/promql/engine_test.go b/promql/engine_test.go index 1950b2cbb..28db8af19 100644 --- a/promql/engine_test.go +++ b/promql/engine_test.go @@ -3128,7 +3128,6 @@ func TestRangeQuery(t *testing.T) { func TestSparseHistogramRate(t *testing.T) { // TODO(beorn7): Integrate histograms into the PromQL testing framework // and write more tests there. - // TODO(marctc): Add similar test for float histograms test, err := NewTest(t, "") require.NoError(t, err) defer test.Close() @@ -3167,10 +3166,50 @@ func TestSparseHistogramRate(t *testing.T) { require.Equal(t, expectedHistogram, actualHistogram) } +func TestSparseFloatHistogramRate(t *testing.T) { + // TODO(beorn7): Integrate histograms into the PromQL testing framework + // and write more tests there. + test, err := NewTest(t, "") + require.NoError(t, err) + defer test.Close() + + seriesName := "sparse_histogram_series" + lbls := labels.FromStrings("__name__", seriesName) + + app := test.Storage().Appender(context.TODO()) + for i, fh := range tsdb.GenerateTestFloatHistograms(100) { + _, err := app.AppendHistogram(0, lbls, int64(i)*int64(15*time.Second/time.Millisecond), nil, fh) + require.NoError(t, err) + } + require.NoError(t, app.Commit()) + + require.NoError(t, test.Run()) + engine := test.QueryEngine() + + queryString := fmt.Sprintf("rate(%s[1m])", seriesName) + qry, err := engine.NewInstantQuery(test.Queryable(), nil, queryString, timestamp.Time(int64(5*time.Minute/time.Millisecond))) + require.NoError(t, err) + res := qry.Exec(test.Context()) + require.NoError(t, res.Err) + vector, err := res.Vector() + require.NoError(t, err) + require.Len(t, vector, 1) + actualHistogram := vector[0].H + expectedHistogram := &histogram.FloatHistogram{ + Schema: 1, + ZeroThreshold: 0.001, + ZeroCount: 1. / 15., + Count: 4. / 15., + Sum: 1.226666666666667, + PositiveSpans: []histogram.Span{{Offset: 0, Length: 2}, {Offset: 1, Length: 2}}, + PositiveBuckets: []float64{1. / 15., 1. / 15., 1. / 15., 1. / 15.}, + } + require.Equal(t, expectedHistogram, actualHistogram) +} + func TestSparseHistogram_HistogramCountAndSum(t *testing.T) { // TODO(codesome): Integrate histograms into the PromQL testing framework // and write more tests there. - // TODO(marctc): Add similar test for float histograms h := &histogram.Histogram{ Count: 24, ZeroCount: 4, @@ -3188,54 +3227,68 @@ func TestSparseHistogram_HistogramCountAndSum(t *testing.T) { }, NegativeBuckets: []int64{2, 1, -2, 3}, } + for _, floatHisto := range []bool{true, false} { + t.Run(fmt.Sprintf("floatHistogram=%t", floatHisto), func(t *testing.T) { + test, err := NewTest(t, "") + require.NoError(t, err) + t.Cleanup(test.Close) - test, err := NewTest(t, "") - require.NoError(t, err) - t.Cleanup(test.Close) + seriesName := "sparse_histogram_series" + lbls := labels.FromStrings("__name__", seriesName) + engine := test.QueryEngine() - seriesName := "sparse_histogram_series" - lbls := labels.FromStrings("__name__", seriesName) - engine := test.QueryEngine() + ts := int64(10 * time.Minute / time.Millisecond) + app := test.Storage().Appender(context.TODO()) + if floatHisto { + _, err = app.AppendHistogram(0, lbls, ts, nil, h.ToFloat()) + } else { + _, err = app.AppendHistogram(0, lbls, ts, h, nil) + } + require.NoError(t, err) + require.NoError(t, app.Commit()) - ts := int64(10 * time.Minute / time.Millisecond) - app := test.Storage().Appender(context.TODO()) - _, err = app.AppendHistogram(0, lbls, ts, h, nil) - require.NoError(t, err) - require.NoError(t, app.Commit()) + queryString := fmt.Sprintf("histogram_count(%s)", seriesName) + qry, err := engine.NewInstantQuery(test.Queryable(), nil, queryString, timestamp.Time(ts)) + require.NoError(t, err) - queryString := fmt.Sprintf("histogram_count(%s)", seriesName) - qry, err := engine.NewInstantQuery(test.Queryable(), nil, queryString, timestamp.Time(ts)) - require.NoError(t, err) + res := qry.Exec(test.Context()) + require.NoError(t, res.Err) - res := qry.Exec(test.Context()) - require.NoError(t, res.Err) + vector, err := res.Vector() + require.NoError(t, err) - vector, err := res.Vector() - require.NoError(t, err) + require.Len(t, vector, 1) + require.Nil(t, vector[0].H) + if floatHisto { + require.Equal(t, float64(h.ToFloat().Count), vector[0].V) + } else { + require.Equal(t, float64(h.Count), vector[0].V) + } - require.Len(t, vector, 1) - require.Nil(t, vector[0].H) - require.Equal(t, float64(h.Count), vector[0].V) + queryString = fmt.Sprintf("histogram_sum(%s)", seriesName) + qry, err = engine.NewInstantQuery(test.Queryable(), nil, queryString, timestamp.Time(ts)) + require.NoError(t, err) - queryString = fmt.Sprintf("histogram_sum(%s)", seriesName) - qry, err = engine.NewInstantQuery(test.Queryable(), nil, queryString, timestamp.Time(ts)) - require.NoError(t, err) + res = qry.Exec(test.Context()) + require.NoError(t, res.Err) - res = qry.Exec(test.Context()) - require.NoError(t, res.Err) + vector, err = res.Vector() + require.NoError(t, err) - vector, err = res.Vector() - require.NoError(t, err) - - require.Len(t, vector, 1) - require.Nil(t, vector[0].H) - require.Equal(t, h.Sum, vector[0].V) + require.Len(t, vector, 1) + require.Nil(t, vector[0].H) + if floatHisto { + require.Equal(t, h.ToFloat().Sum, vector[0].V) + } else { + require.Equal(t, h.Sum, vector[0].V) + } + }) + } } func TestSparseHistogram_HistogramQuantile(t *testing.T) { // TODO(codesome): Integrate histograms into the PromQL testing framework // and write more tests there. - // TODO(marctc): Add similar test for float histograms type subCase struct { quantile string value float64 @@ -3429,43 +3482,49 @@ func TestSparseHistogram_HistogramQuantile(t *testing.T) { test, err := NewTest(t, "") require.NoError(t, err) t.Cleanup(test.Close) - for i, c := range cases { - t.Run(c.text, func(t *testing.T) { - seriesName := "sparse_histogram_series" - lbls := labels.FromStrings("__name__", seriesName) - engine := test.QueryEngine() + idx := int64(0) + for _, floatHisto := range []bool{true, false} { + for _, c := range cases { + t.Run(fmt.Sprintf("%s floatHistogram=%t", c.text, floatHisto), func(t *testing.T) { + seriesName := "sparse_histogram_series" + lbls := labels.FromStrings("__name__", seriesName) + engine := test.QueryEngine() + ts := idx * int64(10*time.Minute/time.Millisecond) + app := test.Storage().Appender(context.TODO()) + if floatHisto { + _, err = app.AppendHistogram(0, lbls, ts, nil, c.h.ToFloat()) + } else { + _, err = app.AppendHistogram(0, lbls, ts, c.h, nil) + } + require.NoError(t, err) + require.NoError(t, app.Commit()) - ts := int64(i+1) * int64(10*time.Minute/time.Millisecond) - app := test.Storage().Appender(context.TODO()) - _, err = app.AppendHistogram(0, lbls, ts, c.h, nil) - require.NoError(t, err) - require.NoError(t, app.Commit()) + for j, sc := range c.subCases { + t.Run(fmt.Sprintf("%d %s", j, sc.quantile), func(t *testing.T) { + queryString := fmt.Sprintf("histogram_quantile(%s, %s)", sc.quantile, seriesName) + qry, err := engine.NewInstantQuery(test.Queryable(), nil, queryString, timestamp.Time(ts)) + require.NoError(t, err) - for j, sc := range c.subCases { - t.Run(fmt.Sprintf("%d %s", j, sc.quantile), func(t *testing.T) { - queryString := fmt.Sprintf("histogram_quantile(%s, %s)", sc.quantile, seriesName) - qry, err := engine.NewInstantQuery(test.Queryable(), nil, queryString, timestamp.Time(ts)) - require.NoError(t, err) + res := qry.Exec(test.Context()) + require.NoError(t, res.Err) - res := qry.Exec(test.Context()) - require.NoError(t, res.Err) + vector, err := res.Vector() + require.NoError(t, err) - vector, err := res.Vector() - require.NoError(t, err) - - require.Len(t, vector, 1) - require.Nil(t, vector[0].H) - require.True(t, almostEqual(sc.value, vector[0].V)) - }) - } - }) + require.Len(t, vector, 1) + require.Nil(t, vector[0].H) + require.True(t, almostEqual(sc.value, vector[0].V)) + }) + } + idx++ + }) + } } } func TestSparseHistogram_HistogramFraction(t *testing.T) { // TODO(codesome): Integrate histograms into the PromQL testing framework // and write more tests there. - // TODO(marctc): Add similar test for float histograms type subCase struct { lower, upper string value float64 @@ -3849,52 +3908,58 @@ func TestSparseHistogram_HistogramFraction(t *testing.T) { }, invariantCases...), }, } + idx := int64(0) + for _, floatHisto := range []bool{true, false} { + for _, c := range cases { + t.Run(fmt.Sprintf("%s floatHistogram=%t", c.text, floatHisto), func(t *testing.T) { + test, err := NewTest(t, "") + require.NoError(t, err) + t.Cleanup(test.Close) - for i, c := range cases { - t.Run(c.text, func(t *testing.T) { - test, err := NewTest(t, "") - require.NoError(t, err) - t.Cleanup(test.Close) + seriesName := "sparse_histogram_series" + lbls := labels.FromStrings("__name__", seriesName) + engine := test.QueryEngine() - seriesName := "sparse_histogram_series" - lbls := labels.FromStrings("__name__", seriesName) - engine := test.QueryEngine() + ts := idx * int64(10*time.Minute/time.Millisecond) + app := test.Storage().Appender(context.TODO()) + if floatHisto { + _, err = app.AppendHistogram(0, lbls, ts, nil, c.h.ToFloat()) + } else { + _, err = app.AppendHistogram(0, lbls, ts, c.h, nil) + } + require.NoError(t, err) + require.NoError(t, app.Commit()) - ts := int64(i+1) * int64(10*time.Minute/time.Millisecond) - app := test.Storage().Appender(context.TODO()) - _, err = app.AppendHistogram(0, lbls, ts, c.h, nil) - require.NoError(t, err) - require.NoError(t, app.Commit()) + for j, sc := range c.subCases { + t.Run(fmt.Sprintf("%d %s %s", j, sc.lower, sc.upper), func(t *testing.T) { + queryString := fmt.Sprintf("histogram_fraction(%s, %s, %s)", sc.lower, sc.upper, seriesName) + qry, err := engine.NewInstantQuery(test.Queryable(), nil, queryString, timestamp.Time(ts)) + require.NoError(t, err) - for j, sc := range c.subCases { - t.Run(fmt.Sprintf("%d %s %s", j, sc.lower, sc.upper), func(t *testing.T) { - queryString := fmt.Sprintf("histogram_fraction(%s, %s, %s)", sc.lower, sc.upper, seriesName) - qry, err := engine.NewInstantQuery(test.Queryable(), nil, queryString, timestamp.Time(ts)) - require.NoError(t, err) + res := qry.Exec(test.Context()) + require.NoError(t, res.Err) - res := qry.Exec(test.Context()) - require.NoError(t, res.Err) + vector, err := res.Vector() + require.NoError(t, err) - vector, err := res.Vector() - require.NoError(t, err) - - require.Len(t, vector, 1) - require.Nil(t, vector[0].H) - if math.IsNaN(sc.value) { - require.True(t, math.IsNaN(vector[0].V)) - return - } - require.Equal(t, sc.value, vector[0].V) - }) - } - }) + require.Len(t, vector, 1) + require.Nil(t, vector[0].H) + if math.IsNaN(sc.value) { + require.True(t, math.IsNaN(vector[0].V)) + return + } + require.Equal(t, sc.value, vector[0].V) + }) + } + idx++ + }) + } } } func TestSparseHistogram_Sum_Count_AddOperator(t *testing.T) { // TODO(codesome): Integrate histograms into the PromQL testing framework // and write more tests there. - // TODO(marctc): Add similar test for float histograms cases := []struct { histograms []histogram.Histogram expected histogram.FloatHistogram @@ -3978,60 +4043,68 @@ func TestSparseHistogram_Sum_Count_AddOperator(t *testing.T) { }, } - for i, c := range cases { - t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - test, err := NewTest(t, "") - require.NoError(t, err) - t.Cleanup(test.Close) - - seriesName := "sparse_histogram_series" - - engine := test.QueryEngine() - - ts := int64(i+1) * int64(10*time.Minute/time.Millisecond) - app := test.Storage().Appender(context.TODO()) - for idx, h := range c.histograms { - lbls := labels.FromStrings("__name__", seriesName, "idx", fmt.Sprintf("%d", idx)) - // Since we mutate h later, we need to create a copy here. - _, err = app.AppendHistogram(0, lbls, ts, h.Copy(), nil) + idx0 := int64(0) + for _, c := range cases { + for _, floatHisto := range []bool{true, false} { + t.Run(fmt.Sprintf("floatHistogram=%t %d", floatHisto, idx0), func(t *testing.T) { + test, err := NewTest(t, "") require.NoError(t, err) - } - require.NoError(t, app.Commit()) + t.Cleanup(test.Close) - queryAndCheck := func(queryString string, exp Vector) { - qry, err := engine.NewInstantQuery(test.Queryable(), nil, queryString, timestamp.Time(ts)) - require.NoError(t, err) + seriesName := "sparse_histogram_series" - res := qry.Exec(test.Context()) - require.NoError(t, res.Err) + engine := test.QueryEngine() - vector, err := res.Vector() - require.NoError(t, err) + ts := idx0 * int64(10*time.Minute/time.Millisecond) + app := test.Storage().Appender(context.TODO()) + for idx1, h := range c.histograms { + lbls := labels.FromStrings("__name__", seriesName, "idx", fmt.Sprintf("%d", idx1)) + // Since we mutate h later, we need to create a copy here. + if floatHisto { + _, err = app.AppendHistogram(0, lbls, ts, nil, h.Copy().ToFloat()) + } else { + _, err = app.AppendHistogram(0, lbls, ts, h.Copy(), nil) + } + require.NoError(t, err) + } + require.NoError(t, app.Commit()) - require.Equal(t, exp, vector) - } + queryAndCheck := func(queryString string, exp Vector) { + qry, err := engine.NewInstantQuery(test.Queryable(), nil, queryString, timestamp.Time(ts)) + require.NoError(t, err) - // sum(). - queryString := fmt.Sprintf("sum(%s)", seriesName) - queryAndCheck(queryString, []Sample{ - {Point{T: ts, H: &c.expected}, labels.EmptyLabels()}, + res := qry.Exec(test.Context()) + require.NoError(t, res.Err) + + vector, err := res.Vector() + require.NoError(t, err) + + require.Equal(t, exp, vector) + } + + // sum(). + queryString := fmt.Sprintf("sum(%s)", seriesName) + queryAndCheck(queryString, []Sample{ + {Point{T: ts, H: &c.expected}, labels.EmptyLabels()}, + }) + + // + operator. + queryString = fmt.Sprintf(`%s{idx="0"}`, seriesName) + for idx := 1; idx < len(c.histograms); idx++ { + queryString += fmt.Sprintf(` + ignoring(idx) %s{idx="%d"}`, seriesName, idx) + } + queryAndCheck(queryString, []Sample{ + {Point{T: ts, H: &c.expected}, labels.EmptyLabels()}, + }) + + // count(). + queryString = fmt.Sprintf("count(%s)", seriesName) + queryAndCheck(queryString, []Sample{ + {Point{T: ts, V: 3}, labels.EmptyLabels()}, + }) }) - - // + operator. - queryString = fmt.Sprintf(`%s{idx="0"}`, seriesName) - for idx := 1; idx < len(c.histograms); idx++ { - queryString += fmt.Sprintf(` + ignoring(idx) %s{idx="%d"}`, seriesName, idx) - } - queryAndCheck(queryString, []Sample{ - {Point{T: ts, H: &c.expected}, labels.EmptyLabels()}, - }) - - // count(). - queryString = fmt.Sprintf("count(%s)", seriesName) - queryAndCheck(queryString, []Sample{ - {Point{T: ts, V: 3}, labels.EmptyLabels()}, - }) - }) + idx0++ + } } }