-
Notifications
You must be signed in to change notification settings - Fork 697
include metric name in annotations from histogram_quantile if delayed… #13905
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
e92c5fc
f941eb3
c3abc4d
2d76138
2d6fe0a
b0a10e2
22cad81
eb62803
cb68b15
73819a8
71b15b9
3bbefd1
fe279ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -247,7 +247,7 @@ func TestOurTestCases(t *testing.T) { | |
| testScript := string(b) | ||
|
|
||
| t.Run("Mimir's engine", func(t *testing.T) { | ||
| if strings.Contains(testFile, "name_label_dropping") { | ||
| if strings.Contains(testFile, "name_label_dropping") || strings.Contains(testFile, "delayed_name_removal_enabled") { | ||
tcp13equals2 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| promqltest.RunTest(t, testScript, mimirEngineWithDelayedNameRemoval) | ||
| return | ||
| } | ||
|
|
@@ -261,7 +261,7 @@ func TestOurTestCases(t *testing.T) { | |
| t.Skip("disabled for Prometheus' engine due to bug in Prometheus' engine") | ||
| } | ||
|
|
||
| if strings.Contains(testFile, "name_label_dropping") { | ||
| if strings.Contains(testFile, "name_label_dropping") || strings.Contains(testFile, "delayed_name_removal_enabled") { | ||
| promqltest.RunTest(t, testScript, prometheusEngineWithDelayedNameRemoval) | ||
| return | ||
| } | ||
|
|
@@ -2201,32 +2201,60 @@ func (t *timeoutTestingQueryTracker) Close() error { | |
| } | ||
|
|
||
| type annotationTestCase struct { | ||
| data string | ||
| expr string | ||
| expectedWarningAnnotations []string | ||
| expectedInfoAnnotations []string | ||
| skipComparisonWithPrometheusReason string | ||
| instantEvaluationTimestamp *time.Time | ||
| data string | ||
| expr string | ||
| expectedWarningAnnotations []string | ||
| // an alternate string for when delayed name removal is enabled. | ||
tcp13equals2 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // if not set the test will fall back to expectedWarningAnnotations | ||
| expectedWarningAnnotationsDelayedNameRemovalEnabled []string | ||
| expectedInfoAnnotations []string | ||
| // an alternate string for when delayed name removal is enabled. | ||
tcp13equals2 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // if not set the test will fall back to expectedInfoAnnotations | ||
| expectedInfoAnnotationsDelayedNameRemovalEnabled []string | ||
| skipComparisonWithPrometheusReason string | ||
| instantEvaluationTimestamp *time.Time | ||
| } | ||
|
|
||
| func (a annotationTestCase) getExpectedInfoAnnotations(delayedNameRemovalEnabled bool) []string { | ||
| if delayedNameRemovalEnabled && a.expectedInfoAnnotationsDelayedNameRemovalEnabled != nil { | ||
| return a.expectedInfoAnnotationsDelayedNameRemovalEnabled | ||
| } | ||
| return a.expectedInfoAnnotations | ||
| } | ||
|
|
||
| func (a annotationTestCase) getExpectedWarningAnnotations(delayedNameRemovalEnabled bool) []string { | ||
| if delayedNameRemovalEnabled && a.expectedWarningAnnotationsDelayedNameRemovalEnabled != nil { | ||
| return a.expectedWarningAnnotationsDelayedNameRemovalEnabled | ||
| } | ||
| return a.expectedWarningAnnotations | ||
| } | ||
|
|
||
| func runAnnotationTests(t *testing.T, testCases map[string]annotationTestCase) { | ||
| startT := timestamp.Time(0).Add(time.Minute) | ||
| step := time.Minute | ||
| endT := startT.Add(2 * step) | ||
|
|
||
| opts := NewTestEngineOpts() | ||
| planner, err := NewQueryPlanner(opts, NewMaximumSupportedVersionQueryPlanVersionProvider()) | ||
| require.NoError(t, err) | ||
| mimirEngine, err := NewEngine(opts, NewStaticQueryLimitsProvider(0), stats.NewQueryMetrics(nil), planner) | ||
| require.NoError(t, err) | ||
| prometheusEngine := promql.NewEngine(opts.CommonOpts) | ||
|
|
||
| const prometheusEngineName = "Prometheus' engine" | ||
| engines := map[string]promql.QueryEngine{ | ||
| "Mimir's engine": mimirEngine, | ||
| const mimirEngineName = "Mimir's engine" | ||
|
|
||
| // create 2 sets of engines - one with EnableDelayedNameRemoval=true and the other with EnableDelayedNameRemoval=false | ||
| // there are some histogram annotation test cases which will emit a different warning/info annotation string depending on the delayed name removal setting | ||
| engines := make(map[bool]map[string]promql.QueryEngine, 2) | ||
tcp13equals2 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| for _, delayedNameRemovalEnabled := range []bool{true, false} { | ||
| opts := NewTestEngineOpts() | ||
| opts.CommonOpts.EnableDelayedNameRemoval = delayedNameRemovalEnabled | ||
|
|
||
| planner, err := NewQueryPlanner(opts, NewMaximumSupportedVersionQueryPlanVersionProvider()) | ||
| require.NoError(t, err) | ||
| mimirEngine, err := NewEngine(opts, NewStaticQueryLimitsProvider(0), stats.NewQueryMetrics(nil), planner) | ||
| require.NoError(t, err) | ||
| prometheusEngine := promql.NewEngine(opts.CommonOpts) | ||
|
|
||
| // Compare against Prometheus' engine to verify our test cases are valid. | ||
| prometheusEngineName: prometheusEngine, | ||
| engines[delayedNameRemovalEnabled] = map[string]promql.QueryEngine{ | ||
| mimirEngineName: mimirEngine, | ||
| prometheusEngineName: prometheusEngine, | ||
| } | ||
| } | ||
|
|
||
| for name, testCase := range testCases { | ||
|
|
@@ -2251,33 +2279,35 @@ func runAnnotationTests(t *testing.T, testCases map[string]annotationTestCase) { | |
|
|
||
| for queryType, generator := range queryTypes { | ||
| t.Run(queryType, func(t *testing.T) { | ||
| results := make([]*promql.Result, 0, 2) | ||
|
|
||
| for engineName, engine := range engines { | ||
| if engineName == prometheusEngineName && testCase.skipComparisonWithPrometheusReason != "" { | ||
| t.Logf("Skipping comparison with Prometheus' engine: %v", testCase.skipComparisonWithPrometheusReason) | ||
| continue | ||
| for _, delayedNameRemovalEnabled := range []bool{true, false} { | ||
| results := make([]*promql.Result, 0, 2) | ||
tcp13equals2 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| for engineName, engine := range engines[delayedNameRemovalEnabled] { | ||
tcp13equals2 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if engineName == prometheusEngineName && testCase.skipComparisonWithPrometheusReason != "" { | ||
| t.Logf("Skipping comparison with Prometheus' engine: %v", testCase.skipComparisonWithPrometheusReason) | ||
| continue | ||
| } | ||
| t.Run(engineName, func(t *testing.T) { | ||
| query, err := generator(engine) | ||
| require.NoError(t, err) | ||
| t.Cleanup(query.Close) | ||
|
|
||
| res := query.Exec(context.Background()) | ||
| require.NoError(t, res.Err) | ||
| results = append(results, res) | ||
|
|
||
| warnings, infos := res.Warnings.AsStrings(testCase.expr, 0, 0) | ||
| require.ElementsMatch(t, testCase.getExpectedWarningAnnotations(delayedNameRemovalEnabled), warnings) | ||
| require.ElementsMatch(t, testCase.getExpectedInfoAnnotations(delayedNameRemovalEnabled), infos) | ||
| }) | ||
| } | ||
| t.Run(engineName, func(t *testing.T) { | ||
| query, err := generator(engine) | ||
| require.NoError(t, err) | ||
| t.Cleanup(query.Close) | ||
|
|
||
| res := query.Exec(context.Background()) | ||
| require.NoError(t, res.Err) | ||
| results = append(results, res) | ||
|
|
||
| warnings, infos := res.Warnings.AsStrings(testCase.expr, 0, 0) | ||
| require.ElementsMatch(t, testCase.expectedWarningAnnotations, warnings) | ||
| require.ElementsMatch(t, testCase.expectedInfoAnnotations, infos) | ||
| }) | ||
| } | ||
|
|
||
| // If both results are available, compare them (sometimes we skip prometheus) | ||
| if len(results) == 2 { | ||
| // We do this extra comparison to ensure that we don't skip a series that may be outputted during a warning | ||
| // or vice-versa where no result may be expected etc. | ||
| testutils.RequireEqualResults(t, testCase.expr, results[0], results[1], false) | ||
| // If both results are available, compare them (sometimes we skip prometheus) | ||
| if len(results) == 2 { | ||
| // We do this extra comparison to ensure that we don't skip a series that may be outputted during a warning | ||
| // or vice-versa where no result may be expected etc. | ||
| testutils.RequireEqualResults(t, testCase.expr, results[0], results[1], false) | ||
| } | ||
| } | ||
| }) | ||
| } | ||
|
|
@@ -3141,6 +3171,7 @@ func TestHistogramAnnotations(t *testing.T) { | |
| data: mixedClassicHistograms, | ||
| expr: `histogram_quantile(0.5, series{host="c"})`, | ||
| expectedWarningAnnotations: []string{`PromQL warning: bucket label "le" is missing or has a malformed value of "abc" (1:25)`}, | ||
| expectedWarningAnnotationsDelayedNameRemovalEnabled: []string{`PromQL warning: bucket label "le" is missing or has a malformed value of "abc" for metric name "series" (1:25)`}, | ||
| }, | ||
| "invalid quantile warning": { | ||
| data: mixedClassicHistograms, | ||
|
|
@@ -3151,11 +3182,13 @@ func TestHistogramAnnotations(t *testing.T) { | |
| data: mixedClassicHistograms, | ||
| expr: `histogram_quantile(0.5, series{host="a"})`, | ||
| expectedWarningAnnotations: []string{`PromQL warning: vector contains a mix of classic and native histograms (1:25)`}, | ||
| expectedWarningAnnotationsDelayedNameRemovalEnabled: []string{`PromQL warning: vector contains a mix of classic and native histograms for metric name "series" (1:25)`}, | ||
| }, | ||
| "forced monotonicity info": { | ||
| data: mixedClassicHistograms, | ||
| expr: `histogram_quantile(0.5, series{host="d"})`, | ||
| expectedInfoAnnotations: []string{`PromQL info: input to histogram_quantile needed to be fixed for monotonicity (see https://prometheus.io/docs/prometheus/latest/querying/functions/#histogram_quantile) (1:25)`}, | ||
| expectedInfoAnnotationsDelayedNameRemovalEnabled: []string{`PromQL info: input to histogram_quantile needed to be fixed for monotonicity (see https://prometheus.io/docs/prometheus/latest/querying/functions/#histogram_quantile) for metric name "series" (1:25)`}, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I realise this isn't something we can control, but I find the expression here hard to follow - the "for metric name ..." bit seems out of place after the parentheses, it fits better before the parentheses to me. Perhaps something to change upstream?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Which ones do you like;
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm OK with tweaking the old structure: |
||
| }, | ||
| "both mixed classic+native histogram and invalid quantile warnings": { | ||
| data: mixedClassicHistograms, | ||
|
|
@@ -3164,6 +3197,10 @@ func TestHistogramAnnotations(t *testing.T) { | |
| `PromQL warning: vector contains a mix of classic and native histograms (1:23)`, | ||
| `PromQL warning: quantile value should be between 0 and 1, got 9 (1:20)`, | ||
| }, | ||
| expectedWarningAnnotationsDelayedNameRemovalEnabled: []string{ | ||
| `PromQL warning: vector contains a mix of classic and native histograms for metric name "series" (1:23)`, | ||
| `PromQL warning: quantile value should be between 0 and 1, got 9 (1:20)`, | ||
| }, | ||
| }, | ||
| "forced monotonicity info is not emitted when quantile is invalid": { | ||
| data: mixedClassicHistograms, | ||
|
|
@@ -3176,6 +3213,7 @@ func TestHistogramAnnotations(t *testing.T) { | |
| `, | ||
| expr: `histogram_quantile(0.5, series{})`, | ||
| expectedWarningAnnotations: []string{`PromQL warning: bucket label "le" is missing or has a malformed value of "" (1:25)`}, | ||
| expectedWarningAnnotationsDelayedNameRemovalEnabled: []string{`PromQL warning: bucket label "le" is missing or has a malformed value of "" for metric name "series" (1:25)`}, | ||
| }, | ||
| "extra entry in series without le label": { | ||
| data: ` | ||
|
|
@@ -3184,6 +3222,7 @@ func TestHistogramAnnotations(t *testing.T) { | |
| `, | ||
| expr: `histogram_quantile(0.5, series{})`, | ||
| expectedWarningAnnotations: []string{`PromQL warning: bucket label "le" is missing or has a malformed value of "" (1:25)`}, | ||
| expectedWarningAnnotationsDelayedNameRemovalEnabled: []string{`PromQL warning: bucket label "le" is missing or has a malformed value of "" for metric name "series" (1:25)`}, | ||
| }, | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| # SPDX-License-Identifier: AGPL-3.0-only | ||
| # Provenance-includes-location: https://github.com/prometheus/prometheus/tree/main/promql/testdata/native_histograms.test | ||
| # Provenance-includes-license: Apache-2.0 | ||
| # Provenance-includes-copyright: The Prometheus Authors | ||
|
|
||
| # Test histogram_quantile annotations. | ||
| load 1m | ||
| nonmonotonic_bucket{le="0.1"} 0+2x10 | ||
| nonmonotonic_bucket{le="1"} 0+1x10 | ||
| nonmonotonic_bucket{le="10"} 0+5x10 | ||
| nonmonotonic_bucket{le="100"} 0+4x10 | ||
| nonmonotonic_bucket{le="1000"} 0+9x10 | ||
| nonmonotonic_bucket{le="+Inf"} 0+8x10 | ||
| myHistogram1{abe="0.1"} 0+2x10 | ||
| myHistogram2{le="Hello World"} 0+2x10 | ||
| mixedHistogram{le="0.1"} 0+2x10 | ||
| mixedHistogram{le="1"} 0+3x10 | ||
| mixedHistogram{} {{schema:0 count:10 sum:50 buckets:[1 2 3]}} | ||
|
|
||
| eval instant at 1m histogram_quantile(0.5, nonmonotonic_bucket) | ||
| expect info msg: PromQL info: input to histogram_quantile needed to be fixed for monotonicity (see https://prometheus.io/docs/prometheus/latest/querying/functions/#histogram_quantile) | ||
| {} 8.5 | ||
|
|
||
| eval instant at 1m histogram_quantile(0.5, myHistogram1) | ||
| expect warn msg: PromQL warning: bucket label "le" is missing or has a malformed value of "" | ||
|
|
||
| eval instant at 1m histogram_quantile(0.5, myHistogram2) | ||
| expect warn msg: PromQL warning: bucket label "le" is missing or has a malformed value of "Hello World" | ||
|
|
||
| eval instant at 1m histogram_quantile(0.5, mixedHistogram) | ||
| expect warn msg: PromQL warning: vector contains a mix of classic and native histograms | ||
|
|
||
| clear | ||
|
|
||
| # Test native histogram quantile and fraction when the native histogram with exponential | ||
| # buckets has NaN observations. | ||
| load 1m | ||
| histogram_nan{case="100% NaNs"} {{schema:0 count:0 sum:0}} {{schema:0 count:3 sum:NaN}} | ||
| histogram_nan{case="20% NaNs"} {{schema:0 count:0 sum:0}} {{schema:0 count:15 sum:NaN buckets:[12]}} | ||
|
|
||
| eval instant at 1m histogram_quantile(1, histogram_nan) | ||
| expect info msg: PromQL info: input to histogram_quantile has NaN observations, result is NaN | ||
| {case="100% NaNs"} NaN | ||
| {case="20% NaNs"} NaN | ||
|
|
||
| eval instant at 1m histogram_quantile(0.81, histogram_nan) | ||
| expect info msg: PromQL info: input to histogram_quantile has NaN observations, result is NaN | ||
| {case="100% NaNs"} NaN | ||
| {case="20% NaNs"} NaN | ||
|
|
||
| eval instant at 1m histogram_quantile(0.8, histogram_nan{case="100% NaNs"}) | ||
| expect info msg: PromQL info: input to histogram_quantile has NaN observations, result is NaN | ||
| {case="100% NaNs"} NaN | ||
|
|
||
| eval instant at 1m histogram_quantile(0.8, histogram_nan{case="20% NaNs"}) | ||
| expect info msg: PromQL info: input to histogram_quantile has NaN observations, result is skewed higher | ||
| {case="20% NaNs"} 1 | ||
|
|
||
| eval instant at 1m histogram_quantile(0.4, histogram_nan{case="100% NaNs"}) | ||
| expect info msg: PromQL info: input to histogram_quantile has NaN observations, result is NaN | ||
| {case="100% NaNs"} NaN | ||
|
|
||
| # histogram_quantile and histogram_fraction equivalence if quantile is not NaN | ||
| eval instant at 1m histogram_quantile(0.4, histogram_nan{case="20% NaNs"}) | ||
| expect info msg: PromQL info: input to histogram_quantile has NaN observations, result is skewed higher | ||
| {case="20% NaNs"} 0.7071067811865475 |
Uh oh!
There was an error while loading. Please reload this page.