Skip to content

Commit 778b660

Browse files
authored
Cherrypick commits to release 1.16 for new RC (#5657)
* Fix param_query omitted in query frontend query stats log (#5655) * fix param_query not logged in query stats log Signed-off-by: Ben Ye <[email protected]> * fix lint Signed-off-by: Ben Ye <[email protected]> * fix unit test of user agent Signed-off-by: Ben Ye <[email protected]> --------- Signed-off-by: Ben Ye <[email protected]> * Add querier.max-subquery-steps to make subquery step size check optional (#5656) * add querier.max-subquery-steps to make subquery step size check optional Signed-off-by: Ben Ye <[email protected]> * update Signed-off-by: Ben Ye <[email protected]> * disable subquery step size check by default, make it optional Signed-off-by: Ben Ye <[email protected]> * fix integ test and add changelog Signed-off-by: Ben Ye <[email protected]> --------- Signed-off-by: Ben Ye <[email protected]> * bump RC version to 1.16.0-rc.1 Signed-off-by: Ben Ye <[email protected]> --------- Signed-off-by: Ben Ye <[email protected]>
1 parent e700ebb commit 778b660

File tree

12 files changed

+121
-32
lines changed

12 files changed

+121
-32
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* [CHANGE] DDBKV: Change metric name from `dynamodb_kv_read_capacity_total` to `dynamodb_kv_consumed_capacity_total` and include Delete, Put, Batch dimension. #5487
2121
* [CHANGE] Compactor: Adding the userId on the compact dir path. #5524
2222
* [CHANGE] Ingester: Remove deprecated ingester metrics. #5472
23+
* [CHANGE] Query Frontend: Expose `-querier.max-subquery-steps` to configure subquery max steps check. By default, the limit is set to 0, which is disabled. #5656
2324
* [FEATURE] Store Gateway: Implementing multi level index cache. #5451
2425
* [FEATURE] Ruler: Add support for disabling rule groups. #5521
2526
* [FEATURE] Support object storage backends for runtime configuration file. #5292
@@ -102,6 +103,7 @@
102103
* [BUGFIX] DDBKV: When no change detected in ring, retry the CAS until there is change. #5502
103104
* [BUGFIX] Fix bug on objstore when configured to use S3 fips endpoints. #5540
104105
* [BUGFIX] Ruler: Fix bug on ruler where a failure to load a single RuleGroup would prevent rulers to sync all RuleGroup. #5563
106+
* [BUGFIX] Query Frontend: Fix query string being omitted in query stats log. #5655
105107

106108
## 1.15.3 2023-06-22
107109

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.16.0-rc.0
1+
1.16.0-rc.1

docs/blocks-storage/querier.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,11 @@ querier:
157157
# CLI flag: -querier.default-evaluation-interval
158158
[default_evaluation_interval: <duration> | default = 1m]
159159

160+
# Max number of steps allowed for every subquery expression in query. Number
161+
# of steps is calculated using subquery range / step. A value > 0 enables it.
162+
# CLI flag: -querier.max-subquery-steps
163+
[max_subquery_steps: <int> | default = 0]
164+
160165
# Active query tracker monitors active queries, and writes them to the file in
161166
# given directory. If Cortex discovers any queries in this log during startup,
162167
# it will log them to the log file. Setting to empty value disables active

docs/configuration/config-file-reference.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3420,6 +3420,11 @@ The `querier_config` configures the Cortex querier.
34203420
# CLI flag: -querier.default-evaluation-interval
34213421
[default_evaluation_interval: <duration> | default = 1m]
34223422
3423+
# Max number of steps allowed for every subquery expression in query. Number of
3424+
# steps is calculated using subquery range / step. A value > 0 enables it.
3425+
# CLI flag: -querier.max-subquery-steps
3426+
[max_subquery_steps: <int> | default = 0]
3427+
34233428
# Active query tracker monitors active queries, and writes them to the file in
34243429
# given directory. If Cortex discovers any queries in this log during startup,
34253430
# it will log them to the log file. Setting to empty value disables active query

integration/query_frontend_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,11 @@ func TestQueryFrontendSubQueryStepSize(t *testing.T) {
220220

221221
minio := e2edb.NewMinio(9000, BlocksStorageFlags()["-blocks-storage.s3.bucket-name"])
222222
require.NoError(t, s.StartAndWaitReady(minio))
223+
224+
// Enable subquery step size check.
225+
flags = mergeFlags(e2e.EmptyFlags(), map[string]string{
226+
"-querier.max-subquery-steps": "11000",
227+
})
223228
return cortexConfigFile, flags
224229
},
225230
})

pkg/cortex/modules.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,7 @@ func (t *Cortex) initQueryFrontendTripperware() (serv services.Service, err erro
486486
t.Overrides,
487487
queryAnalyzer,
488488
t.Cfg.Querier.DefaultEvaluationInterval,
489+
t.Cfg.Querier.MaxSubQuerySteps,
489490
)
490491

491492
return services.NewIdleService(nil, func(_ error) error {

pkg/frontend/transport/handler.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ type Handler struct {
9898
}
9999

100100
// NewHandler creates a new frontend handler.
101-
func NewHandler(cfg HandlerConfig, roundTripper http.RoundTripper, log log.Logger, reg prometheus.Registerer) http.Handler {
101+
func NewHandler(cfg HandlerConfig, roundTripper http.RoundTripper, log log.Logger, reg prometheus.Registerer) *Handler {
102102
h := &Handler{
103103
cfg: cfg,
104104
log: log,
@@ -407,17 +407,17 @@ func (f *Handler) parseRequestQueryString(r *http.Request, bodyBuf bytes.Buffer)
407407
}
408408

409409
func formatQueryString(queryString url.Values) (fields []interface{}) {
410-
var queryFields []string
410+
var queryFields []interface{}
411411
for k, v := range queryString {
412412
// If `query` or `match[]` field exists, we always put it as the last field.
413413
if k == "query" || k == "match[]" {
414-
queryFields = []string{fmt.Sprintf("param_%s", k), strings.Join(v, ",")}
414+
queryFields = []interface{}{fmt.Sprintf("param_%s", k), strings.Join(v, ",")}
415415
continue
416416
}
417417
fields = append(fields, fmt.Sprintf("param_%s", k), strings.Join(v, ","))
418418
}
419419
if len(queryFields) > 0 {
420-
fields = append(fields, queryFields)
420+
fields = append(fields, queryFields...)
421421
}
422422
return fields
423423
}

pkg/frontend/transport/handler_test.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package transport
22

33
import (
4+
"bytes"
45
"context"
56
"io"
67
"net/http"
78
"net/http/httptest"
9+
"net/url"
810
"strings"
911
"testing"
12+
"time"
1013

1114
"github.com/go-kit/log"
1215
"github.com/pkg/errors"
@@ -16,6 +19,8 @@ import (
1619
"github.com/stretchr/testify/require"
1720
"github.com/weaveworks/common/httpgrpc"
1821
"github.com/weaveworks/common/user"
22+
23+
querier_stats "github.com/cortexproject/cortex/pkg/querier/stats"
1924
)
2025

2126
type roundTripperFunc func(*http.Request) (*http.Response, error)
@@ -274,8 +279,44 @@ func TestHandler_ServeHTTP(t *testing.T) {
274279
assert.Equal(t, tt.expectedMetrics, count)
275280

276281
if tt.additionalMetricsCheckFunc != nil {
277-
tt.additionalMetricsCheckFunc(handler.(*Handler))
282+
tt.additionalMetricsCheckFunc(handler)
278283
}
279284
})
280285
}
281286
}
287+
288+
func TestReportQueryStatsFormat(t *testing.T) {
289+
outputBuf := bytes.NewBuffer(nil)
290+
logger := log.NewSyncLogger(log.NewLogfmtLogger(outputBuf))
291+
handler := NewHandler(HandlerConfig{QueryStatsEnabled: true}, http.DefaultTransport, logger, nil)
292+
293+
userID := "fake"
294+
queryString := url.Values(map[string][]string{"query": {"up"}})
295+
req, err := http.NewRequest(http.MethodGet, "http://localhost:8080/prometheus/api/v1/query", nil)
296+
require.NoError(t, err)
297+
req.Header = http.Header{
298+
"User-Agent": []string{"Grafana"},
299+
}
300+
resp := &http.Response{
301+
ContentLength: 1000,
302+
}
303+
stats := &querier_stats.QueryStats{
304+
Stats: querier_stats.Stats{
305+
WallTime: 3 * time.Second,
306+
FetchedSeriesCount: 100,
307+
FetchedChunksCount: 200,
308+
FetchedSamplesCount: 300,
309+
FetchedChunkBytes: 1024,
310+
FetchedDataBytes: 2048,
311+
},
312+
}
313+
responseErr := errors.New("foo_err")
314+
handler.reportQueryStats(req, userID, queryString, time.Second, stats, responseErr, http.StatusOK, resp)
315+
316+
data, err := io.ReadAll(outputBuf)
317+
require.NoError(t, err)
318+
319+
expectedLog := `level=error msg="query stats" component=query-frontend method=GET path=/prometheus/api/v1/query response_time=1s query_wall_time_seconds=3 fetched_series_count=100 fetched_chunks_count=200 fetched_samples_count=300 fetched_chunks_bytes=1024 fetched_data_bytes=2048 status_code=200 response_size=1000 query_length=2 user_agent=Grafana error=foo_err param_query=up
320+
`
321+
require.Equal(t, expectedLog, string(data))
322+
}

pkg/querier/querier.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ type Config struct {
6464
// step if not specified.
6565
DefaultEvaluationInterval time.Duration `yaml:"default_evaluation_interval"`
6666

67+
// Limit of number of steps allowed for every subquery expression in a query.
68+
MaxSubQuerySteps int64 `yaml:"max_subquery_steps"`
69+
6770
// Directory for ActiveQueryTracker. If empty, ActiveQueryTracker will be disabled and MaxConcurrent will not be applied (!).
6871
// ActiveQueryTracker logs queries that were active during the last crash, but logs them on the next startup.
6972
// However, we need to use active query tracker, otherwise we cannot limit Max Concurrent queries in the PromQL
@@ -114,6 +117,7 @@ func (cfg *Config) RegisterFlags(f *flag.FlagSet) {
114117
f.DurationVar(&cfg.LookbackDelta, "querier.lookback-delta", 5*time.Minute, "Time since the last sample after which a time series is considered stale and ignored by expression evaluations.")
115118
f.DurationVar(&cfg.ShuffleShardingIngestersLookbackPeriod, "querier.shuffle-sharding-ingesters-lookback-period", 0, "When distributor's sharding strategy is shuffle-sharding and this setting is > 0, queriers fetch in-memory series from the minimum set of required ingesters, selecting only ingesters which may have received series since 'now - lookback period'. The lookback period should be greater or equal than the configured 'query store after' and 'query ingesters within'. If this setting is 0, queriers always query all ingesters (ingesters shuffle sharding on read path is disabled).")
116119
f.BoolVar(&cfg.ThanosEngine, "querier.thanos-engine", false, "Experimental. Use Thanos promql engine https://github.com/thanos-io/promql-engine rather than the Prometheus promql engine.")
120+
f.Int64Var(&cfg.MaxSubQuerySteps, "querier.max-subquery-steps", 0, "Max number of steps allowed for every subquery expression in query. Number of steps is calculated using subquery range / step. A value > 0 enables it.")
117121
}
118122

119123
// Validate the config

pkg/querier/tripperware/queryrange/query_range_middlewares_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ func TestRoundTrip(t *testing.T) {
7474
nil,
7575
qa,
7676
time.Minute,
77+
0,
7778
)
7879

7980
for i, tc := range []struct {

pkg/querier/tripperware/roundtrip.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ func NewQueryTripperware(
103103
limits Limits,
104104
queryAnalyzer querysharding.Analyzer,
105105
defaultSubQueryInterval time.Duration,
106+
maxSubQuerySteps int64,
106107
) Tripperware {
107108
// Per tenant query metrics.
108109
queriesPerTenant := promauto.With(registerer).NewCounterVec(prometheus.CounterOpts{
@@ -145,10 +146,10 @@ func NewQueryTripperware(
145146
activeUsers.UpdateUserTimestamp(userStr, time.Now())
146147
queriesPerTenant.WithLabelValues(op, userStr).Inc()
147148

148-
if isQuery || isQueryRange {
149+
if maxSubQuerySteps > 0 && (isQuery || isQueryRange) {
149150
query := r.FormValue("query")
150151
// Check subquery step size.
151-
if err := SubQueryStepSizeCheck(query, defaultSubQueryInterval, MaxStep); err != nil {
152+
if err := SubQueryStepSizeCheck(query, defaultSubQueryInterval, maxSubQuerySteps); err != nil {
152153
return nil, err
153154
}
154155
}

pkg/querier/tripperware/roundtrip_test.go

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -109,46 +109,69 @@ func TestRoundTrip(t *testing.T) {
109109
path, expectedBody string
110110
expectedErr error
111111
limits Limits
112+
maxSubQuerySteps int64
112113
}{
113114
{
114-
path: "/foo",
115-
expectedBody: "bar",
116-
limits: defaultOverrides,
115+
path: "/foo",
116+
expectedBody: "bar",
117+
limits: defaultOverrides,
118+
maxSubQuerySteps: 11000,
117119
},
118120
{
119-
path: queryExemplar,
120-
expectedBody: "bar",
121-
limits: defaultOverrides,
121+
path: queryExemplar,
122+
expectedBody: "bar",
123+
limits: defaultOverrides,
124+
maxSubQuerySteps: 11000,
122125
},
123126
{
124-
path: queryRange,
125-
expectedBody: responseBody,
126-
limits: defaultOverrides,
127+
path: queryRange,
128+
expectedBody: responseBody,
129+
limits: defaultOverrides,
130+
maxSubQuerySteps: 11000,
127131
},
128132
{
129-
path: query,
130-
expectedBody: "bar",
131-
limits: defaultOverrides,
133+
path: query,
134+
expectedBody: "bar",
135+
limits: defaultOverrides,
136+
maxSubQuerySteps: 11000,
132137
},
133138
{
134-
path: queryNonShardable,
135-
expectedBody: "bar",
136-
limits: defaultOverrides,
139+
path: queryNonShardable,
140+
expectedBody: "bar",
141+
limits: defaultOverrides,
142+
maxSubQuerySteps: 11000,
137143
},
138144
{
139-
path: queryNonShardable,
140-
expectedBody: "bar",
141-
limits: shardingOverrides,
145+
path: queryNonShardable,
146+
expectedBody: "bar",
147+
limits: shardingOverrides,
148+
maxSubQuerySteps: 11000,
142149
},
143150
{
144-
path: query,
145-
expectedBody: responseBody,
146-
limits: shardingOverrides,
151+
path: query,
152+
expectedBody: responseBody,
153+
limits: shardingOverrides,
154+
maxSubQuerySteps: 11000,
147155
},
156+
// Shouldn't hit subquery step limit because max steps is set to 0 so this check is disabled.
148157
{
149-
path: querySubqueryStepSizeTooSmall,
150-
expectedErr: httpgrpc.Errorf(http.StatusBadRequest, ErrSubQueryStepTooSmall, 11000),
151-
limits: defaultOverrides,
158+
path: querySubqueryStepSizeTooSmall,
159+
expectedBody: "bar",
160+
limits: defaultOverrides,
161+
maxSubQuerySteps: 0,
162+
},
163+
// Shouldn't hit subquery step limit because max steps is higher, which is 100K.
164+
{
165+
path: querySubqueryStepSizeTooSmall,
166+
expectedBody: "bar",
167+
limits: defaultOverrides,
168+
maxSubQuerySteps: 100000,
169+
},
170+
{
171+
path: querySubqueryStepSizeTooSmall,
172+
expectedErr: httpgrpc.Errorf(http.StatusBadRequest, ErrSubQueryStepTooSmall, 11000),
173+
limits: defaultOverrides,
174+
maxSubQuerySteps: 11000,
152175
},
153176
} {
154177
t.Run(tc.path, func(t *testing.T) {
@@ -177,6 +200,7 @@ func TestRoundTrip(t *testing.T) {
177200
tc.limits,
178201
querysharding.NewQueryAnalyzer(),
179202
time.Minute,
203+
tc.maxSubQuerySteps,
180204
)
181205
resp, err := tw(downstream).RoundTrip(req)
182206
if tc.expectedErr == nil {

0 commit comments

Comments
 (0)