Skip to content

Commit c3c5ba0

Browse files
committed
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]>
1 parent a8aab0d commit c3c5ba0

File tree

9 files changed

+73
-26
lines changed

9 files changed

+73
-26
lines changed

CHANGELOG.md

Lines changed: 1 addition & 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

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/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)