Skip to content
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

Track number of optimised regexp label matchers #4813

1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
* [ENHANCEMENT] Add native histogram support for `cortex_request_duration_seconds` metric family. #4987
* [ENHANCEMENT] Ruler: do not list rule groups in the object storage for disabled tenants. #5004
* [ENHANCEMENT] Query-frontend and querier: add HTTP API endpoint `<prometheus-http-prefix>/api/v1/format_query` to format a PromQL query. #4373
* [ENHANCEMENT] Query-frontend: Add `cortex_query_frontend_regexp_matcher_count` and `cortex_query_frontend_regexp_matcher_optimized_count` metrics to track optimization of regular expression label matchers. #4813
* [ENHANCEMENT] Alertmanager: Add configuration option to enable or disable the deletion of alertmanager state from object storage. This is useful when migrating alertmanager tenants from one cluster to another, because it avoids a condition where the state object is copied but then deleted before the configuration object is copied. #4989
* [ENHANCEMENT] Querier: only use the minimum set of chunks from ingesters when querying, and cancel unnecessary requests to ingesters sooner if we know their results won't be used. #5016
* [ENHANCEMENT] Add `-enable-go-runtime-metrics` flag to expose all go runtime metrics as Prometheus metrics. #5009
Expand Down
37 changes: 33 additions & 4 deletions pkg/frontend/querymiddleware/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,37 @@ import (

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/promql/parser"
)

type queryStatsMiddleware struct {
nonAlignedQueries prometheus.Counter
next Handler
nonAlignedQueries prometheus.Counter
regexpMatcherCount prometheus.Counter
regexpMatcherOptimizedCount prometheus.Counter
next Handler
}

func newQueryStatsMiddleware(reg prometheus.Registerer) Middleware {
nonAlignedQueries := promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "cortex_query_frontend_non_step_aligned_queries_total",
Help: "Total queries sent that are not step aligned.",
})
regexpMatcherCount := promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "cortex_query_frontend_regexp_matcher_count",
Help: "Total number of regexp matchers",
})
regexpMatcherOptimizedCount := promauto.With(reg).NewCounter(prometheus.CounterOpts{
Name: "cortex_query_frontend_regexp_matcher_optimized_count",
Help: "Total number of optimized regexp matchers",
})

return MiddlewareFunc(func(next Handler) Handler {
return &queryStatsMiddleware{
nonAlignedQueries: nonAlignedQueries,
next: next,
nonAlignedQueries: nonAlignedQueries,
regexpMatcherCount: regexpMatcherCount,
regexpMatcherOptimizedCount: regexpMatcherOptimizedCount,
next: next,
}
})
}
Expand All @@ -33,5 +47,20 @@ func (s queryStatsMiddleware) Do(ctx context.Context, req Request) (Response, er
s.nonAlignedQueries.Inc()
}

if expr, err := parser.ParseExpr(req.GetQuery()); err == nil {
for _, selectors := range parser.ExtractSelectors(expr) {
for _, matcher := range selectors {
if matcher.Type != labels.MatchRegexp && matcher.Type != labels.MatchNotRegexp {
continue
}

s.regexpMatcherCount.Inc()
if matcher.IsRegexOptimized() {
s.regexpMatcherOptimizedCount.Inc()
}
}
}
}

return s.next.Do(ctx, req)
}
84 changes: 84 additions & 0 deletions pkg/frontend/querymiddleware/stats_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: AGPL-3.0-only

package querymiddleware

import (
"context"
"strings"
"testing"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/weaveworks/common/user"

"github.com/grafana/mimir/pkg/util"
)

func Test_queryStatsMiddleware_Do(t *testing.T) {
type args struct {
req Request
}
tests := []struct {
name string
args args
expectedMetrics *strings.Reader
}{
{
name: "happy path",
args: args{
req: &PrometheusRangeQueryRequest{
Path: "/query_range",
Start: util.TimeToMillis(start),
End: util.TimeToMillis(end),
Step: step.Milliseconds(),
Query: `sum(sum_over_time(metric{app="test",namespace=~"short"}[5m]))`,
},
},
expectedMetrics: strings.NewReader(`
# HELP cortex_query_frontend_non_step_aligned_queries_total Total queries sent that are not step aligned.
# TYPE cortex_query_frontend_non_step_aligned_queries_total counter
cortex_query_frontend_non_step_aligned_queries_total 1
# HELP cortex_query_frontend_regexp_matcher_count Total number of regexp matchers
# TYPE cortex_query_frontend_regexp_matcher_count counter
cortex_query_frontend_regexp_matcher_count 1
# HELP cortex_query_frontend_regexp_matcher_optimized_count Total number of optimized regexp matchers
# TYPE cortex_query_frontend_regexp_matcher_optimized_count counter
cortex_query_frontend_regexp_matcher_optimized_count 1
`),
},
{
name: "parseExpr failed",
args: args{
req: &PrometheusRangeQueryRequest{
Path: "/query_range",
Start: util.TimeToMillis(start),
End: util.TimeToMillis(end),
Step: step.Milliseconds(),
Query: `?`,
},
},
expectedMetrics: strings.NewReader(`
# HELP cortex_query_frontend_non_step_aligned_queries_total Total queries sent that are not step aligned.
# TYPE cortex_query_frontend_non_step_aligned_queries_total counter
cortex_query_frontend_non_step_aligned_queries_total 1
# HELP cortex_query_frontend_regexp_matcher_count Total number of regexp matchers
# TYPE cortex_query_frontend_regexp_matcher_count counter
cortex_query_frontend_regexp_matcher_count 0
# HELP cortex_query_frontend_regexp_matcher_optimized_count Total number of optimized regexp matchers
# TYPE cortex_query_frontend_regexp_matcher_optimized_count counter
cortex_query_frontend_regexp_matcher_optimized_count 0
`),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reg := prometheus.NewPedanticRegistry()
mw := newQueryStatsMiddleware(reg)
_, err := mw.Wrap(mockHandlerWith(nil, nil)).Do(user.InjectOrgID(context.Background(), "test"), tt.args.req)
require.NoError(t, err)
assert.NoError(t, testutil.GatherAndCompare(reg, tt.expectedMetrics))
})
}
}