Skip to content

Commit e47ad33

Browse files
authored
Enforce /api/v1/series API query length check at Querier (#6018)
* enforce /api/v1/series API query length check at Querier Signed-off-by: Ben Ye <[email protected]> * changelog Signed-off-by: Ben Ye <[email protected]> --------- Signed-off-by: Ben Ye <[email protected]>
1 parent b9cf452 commit e47ad33

File tree

3 files changed

+43
-5
lines changed

3 files changed

+43
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* [ENHANCEMENT] Upgrade go to 1.21.11 #6014
1919
* [BUGFIX] Configsdb: Fix endline issue in db password. #5920
2020
* [BUGFIX] Ingester: Fix `user` and `type` labels for the `cortex_ingester_tsdb_head_samples_appended_total` TSDB metric. #5952
21+
* [BUGFIX] Querier: Enforce max query length check for `/api/v1/series` API even though `ignoreMaxQueryLength` is set to true. #6018
2122

2223
## 1.17.1 2024-05-20
2324

pkg/querier/querier.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -375,20 +375,21 @@ func (q querier) Select(ctx context.Context, sortSeries bool, sp *storage.Select
375375
// so we make sure changes are reflected back to hints.
376376
sp.Start = startMs
377377
sp.End = endMs
378+
getSeries := sp.Func == "series"
378379

379380
// For series queries without specifying the start time, we prefer to
380381
// only query ingesters and not to query maxQueryLength to avoid OOM kill.
381-
if sp.Func == "series" && startMs == 0 {
382+
if getSeries && startMs == 0 {
382383
return metadataQuerier.Select(ctx, true, sp, matchers...)
383384
}
384385

385386
startTime := model.Time(startMs)
386387
endTime := model.Time(endMs)
387388

388-
// Validate query time range. This validation should be done only for instant / range queries and
389-
// NOT for metadata queries (series, labels) because the query-frontend doesn't support splitting
390-
// of such queries.
391-
if !q.ignoreMaxQueryLength {
389+
// Validate query time range. This validation for instant / range queries can be done either at Query Frontend
390+
// or here at Querier. When the check is done at Query Frontend, we still want to enforce the max query length
391+
// check for /api/v1/series request since there is no specific tripperware for series.
392+
if !q.ignoreMaxQueryLength || getSeries {
392393
if maxQueryLength := q.limits.MaxQueryLength(userID); maxQueryLength > 0 && endTime.Sub(startTime) > maxQueryLength {
393394
limitErr := validation.LimitError(fmt.Sprintf(validation.ErrQueryTooLong, endTime.Sub(startTime), maxQueryLength))
394395
return storage.ErrSeriesSet(limitErr)

pkg/querier/querier_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,42 @@ func TestQuerier_ValidateQueryTimeRange_MaxQueryLength(t *testing.T) {
892892
}
893893
}
894894

895+
func TestQuerier_ValidateQueryTimeRange_MaxQueryLength_Series(t *testing.T) {
896+
t.Parallel()
897+
const maxQueryLength = 30 * 24 * time.Hour
898+
899+
//parallel testing causes data race
900+
var cfg Config
901+
flagext.DefaultValues(&cfg)
902+
// Disable active query tracker to avoid mmap error.
903+
cfg.ActiveQueryTrackerDir = ""
904+
// Ignore max query length check at Querier but it still enforces it for Series.
905+
cfg.IgnoreMaxQueryLength = true
906+
907+
limits := DefaultLimitsConfig()
908+
limits.MaxQueryLength = model.Duration(maxQueryLength)
909+
overrides, err := validation.NewOverrides(limits, nil)
910+
require.NoError(t, err)
911+
912+
chunkStore := &emptyChunkStore{}
913+
distributor := &emptyDistributor{}
914+
915+
queryables := []QueryableWithFilter{UseAlwaysQueryable(NewMockStoreQueryable(cfg, chunkStore))}
916+
queryable, _, _ := New(cfg, overrides, distributor, queryables, nil, log.NewNopLogger())
917+
918+
ctx := user.InjectOrgID(context.Background(), "test")
919+
now := time.Now()
920+
end := now.Add(-time.Minute)
921+
start := end.Add(-maxQueryLength - time.Hour)
922+
minT := util.TimeToMillis(start)
923+
maxT := util.TimeToMillis(end)
924+
q, err := queryable.Querier(minT, maxT)
925+
require.NoError(t, err)
926+
ss := q.Select(ctx, false, &storage.SelectHints{Func: "series", Start: minT, End: maxT})
927+
require.False(t, ss.Next())
928+
require.True(t, strings.Contains(ss.Err().Error(), "the query time range exceeds the limit (query length: 721h0m0s, limit: 720h0m0s)"))
929+
}
930+
895931
func TestQuerier_ValidateQueryTimeRange_MaxQueryLookback(t *testing.T) {
896932
t.Parallel()
897933
const (

0 commit comments

Comments
 (0)