Skip to content

Commit 971c0b9

Browse files
authored
Query /series from ingesters regardless the request start / end time range (#3037)
Signed-off-by: Marco Pracucci <[email protected]>
1 parent 0929895 commit 971c0b9

File tree

7 files changed

+65
-22
lines changed

7 files changed

+65
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## master / unreleased
44

5+
* [BUGFIX] Querier: query /series from ingesters regardless the `-querier.query-ingesters-within` setting. #3035
56

67
## 1.3.0-rc.1 / 2020-08-10
78

development/tsdb-blocks-storage-s3-single-binary/config/cortex.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ ingester:
2828
host: consul:8500
2929
replication_factor: 1
3030

31+
querier:
32+
query_ingesters_within: 3h
33+
3134
store_gateway:
3235
sharding_enabled: true
3336
sharding_ring:

development/tsdb-blocks-storage-s3/config/cortex.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ ingester:
2929
replication_factor: 1
3030

3131
querier:
32+
query_ingesters_within: 3h
33+
3234
# Used when the blocks sharding is disabled.
3335
store_gateway_addresses: store-gateway-1:9008,store-gateway-2:9009
3436

integration/e2ecortex/client.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,18 +144,24 @@ func (c *Client) QueryRaw(query string) (*http.Response, []byte, error) {
144144
return res, body, nil
145145
}
146146

147+
// Series finds series by label matchers.
148+
func (c *Client) Series(matches []string, start, end time.Time) ([]model.LabelSet, error) {
149+
result, _, err := c.querierClient.Series(context.Background(), matches, start, end)
150+
return result, err
151+
}
152+
147153
// LabelValues gets label values
148154
func (c *Client) LabelValues(label string) (model.LabelValues, error) {
149155
// Cortex currently doesn't support start/end time.
150-
value, _, err := c.querierClient.LabelValues(context.Background(), label, time.Time{}, time.Time{})
151-
return value, err
156+
result, _, err := c.querierClient.LabelValues(context.Background(), label, time.Time{}, time.Time{})
157+
return result, err
152158
}
153159

154160
// LabelNames gets label names
155161
func (c *Client) LabelNames() ([]string, error) {
156162
// Cortex currently doesn't support start/end time.
157-
value, _, err := c.querierClient.LabelNames(context.Background(), time.Time{}, time.Time{})
158-
return value, err
163+
result, _, err := c.querierClient.LabelNames(context.Background(), time.Time{}, time.Time{})
164+
return result, err
159165
}
160166

161167
type addOrgIDRoundTripper struct {

integration/query_frontend_test.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"time"
1111

1212
"github.com/prometheus/common/model"
13+
"github.com/prometheus/prometheus/pkg/labels"
1314
"github.com/prometheus/prometheus/prompb"
1415
"github.com/stretchr/testify/assert"
1516
"github.com/stretchr/testify/require"
@@ -124,6 +125,7 @@ func runQueryFrontendTest(t *testing.T, testMissingMetricName bool, setup queryF
124125
flags = mergeFlags(flags, map[string]string{
125126
"-querier.cache-results": "true",
126127
"-querier.split-queries-by-interval": "24h",
128+
"-querier.query-ingesters-within": "12h", // Required by the test on query /series out of ingesters time range
127129
"-frontend.memcached.addresses": "dns+" + memcached.NetworkEndpoint(e2ecache.MemcachedPort),
128130
})
129131

@@ -183,6 +185,18 @@ func runQueryFrontendTest(t *testing.T, testMissingMetricName bool, setup queryF
183185
require.Contains(t, string(body), "query must contain metric name")
184186
}
185187

188+
// In this test we do ensure that the /series start/end time is ignored and Cortex
189+
// always returns series in ingesters memory. No need to repeat it for each user.
190+
if userID == 0 {
191+
start := now.Add(-1000 * time.Hour)
192+
end := now.Add(-999 * time.Hour)
193+
194+
result, err := c.Series([]string{"series_1"}, start, end)
195+
require.NoError(t, err)
196+
require.Len(t, result, 1)
197+
assert.Equal(t, model.LabelSet{labels.MetricName: "series_1"}, result[0])
198+
}
199+
186200
for q := 0; q < numQueriesPerUser; q++ {
187201
go func() {
188202
defer wg.Done()
@@ -197,9 +211,9 @@ func runQueryFrontendTest(t *testing.T, testMissingMetricName bool, setup queryF
197211

198212
wg.Wait()
199213

200-
extra := float64(0)
214+
extra := float64(1)
201215
if testMissingMetricName {
202-
extra = 1
216+
extra++
203217
}
204218
require.NoError(t, queryFrontend.WaitSumMetrics(e2e.Equals(numUsers*numQueriesPerUser+extra), "cortex_query_frontend_queries_total"))
205219

pkg/querier/distributor_queryable.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,21 @@ func (q *distributorQuerier) Select(_ bool, sp *storage.SelectHints, matchers ..
7979
log, ctx := spanlogger.New(q.ctx, "distributorQuerier.Select")
8080
defer log.Span.Finish()
8181

82-
minT, maxT := q.mint, q.maxt
83-
if sp != nil {
84-
minT, maxT = sp.Start, sp.End
82+
// Kludge: Prometheus passes nil SelectParams if it is doing a 'series' operation,
83+
// which needs only metadata. For this specific case we shouldn't apply the queryIngestersWithin
84+
// time range manipulation, otherwise we'll end up returning no series at all for
85+
// older time ranges (while in Cortex we do ignore the start/end and always return
86+
// series in ingesters).
87+
if sp == nil {
88+
ms, err := q.distributor.MetricsForLabelMatchers(ctx, model.Time(q.mint), model.Time(q.maxt), matchers...)
89+
if err != nil {
90+
return storage.ErrSeriesSet(err)
91+
}
92+
return series.MetricsToSeriesSet(ms)
8593
}
8694

95+
minT, maxT := sp.Start, sp.End
96+
8797
// If queryIngestersWithin is enabled, we do manipulate the query mint to query samples up until
8898
// now - queryIngestersWithin, because older time ranges are covered by the storage. This
8999
// optimization is particularly important for the blocks storage where the blocks retention in the
@@ -103,16 +113,6 @@ func (q *distributorQuerier) Select(_ bool, sp *storage.SelectHints, matchers ..
103113
}
104114
}
105115

106-
// Kludge: Prometheus passes nil SelectParams if it is doing a 'series' operation,
107-
// which needs only metadata.
108-
if sp == nil {
109-
ms, err := q.distributor.MetricsForLabelMatchers(ctx, model.Time(minT), model.Time(maxT), matchers...)
110-
if err != nil {
111-
return storage.ErrSeriesSet(err)
112-
}
113-
return series.MetricsToSeriesSet(ms)
114-
}
115-
116116
if q.streaming {
117117
return q.streamingSelect(minT, maxT, matchers)
118118
}

pkg/querier/distributor_queryable_test.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ func TestDistributorQuerier_SelectShouldHonorQueryIngestersWithin(t *testing.T)
6868
now := time.Now()
6969

7070
tests := map[string]struct {
71+
querySeries bool
7172
queryIngestersWithin time.Duration
7273
queryMinT int64
7374
queryMaxT int64
@@ -102,6 +103,14 @@ func TestDistributorQuerier_SelectShouldHonorQueryIngestersWithin(t *testing.T)
102103
expectedMinT: 0,
103104
expectedMaxT: 0,
104105
},
106+
"should not manipulate query time range if queryIngestersWithin is enabled and query max time is older, but the query is for /series": {
107+
querySeries: true,
108+
queryIngestersWithin: time.Hour,
109+
queryMinT: util.TimeToMillis(now.Add(-100 * time.Minute)),
110+
queryMaxT: util.TimeToMillis(now.Add(-90 * time.Minute)),
111+
expectedMinT: util.TimeToMillis(now.Add(-100 * time.Minute)),
112+
expectedMaxT: util.TimeToMillis(now.Add(-90 * time.Minute)),
113+
},
105114
}
106115

107116
for _, streamingEnabled := range []bool{false, true} {
@@ -110,13 +119,20 @@ func TestDistributorQuerier_SelectShouldHonorQueryIngestersWithin(t *testing.T)
110119
distributor := &mockDistributor{}
111120
distributor.On("Query", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(model.Matrix{}, nil)
112121
distributor.On("QueryStream", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&client.QueryStreamResponse{}, nil)
122+
distributor.On("MetricsForLabelMatchers", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]metric.Metric{}, nil)
113123

114124
ctx := user.InjectOrgID(context.Background(), "test")
115125
queryable := newDistributorQueryable(distributor, streamingEnabled, nil, testData.queryIngestersWithin)
116126
querier, err := queryable.Querier(ctx, testData.queryMinT, testData.queryMaxT)
117127
require.NoError(t, err)
118128

119-
seriesSet := querier.Select(true, &storage.SelectHints{Start: testData.queryMinT, End: testData.queryMaxT})
129+
// Select hints are not passed by Prometheus when querying /series.
130+
var hints *storage.SelectHints
131+
if !testData.querySeries {
132+
hints = &storage.SelectHints{Start: testData.queryMinT, End: testData.queryMaxT}
133+
}
134+
135+
seriesSet := querier.Select(true, hints)
120136
require.NoError(t, seriesSet.Err())
121137

122138
if testData.expectedMinT == 0 && testData.expectedMaxT == 0 {
@@ -216,8 +232,9 @@ func (m *mockDistributor) LabelValuesForLabelName(context.Context, model.LabelNa
216232
func (m *mockDistributor) LabelNames(context.Context) ([]string, error) {
217233
return nil, nil
218234
}
219-
func (m *mockDistributor) MetricsForLabelMatchers(ctx context.Context, from, through model.Time, matchers ...*labels.Matcher) ([]metric.Metric, error) {
220-
return nil, nil
235+
func (m *mockDistributor) MetricsForLabelMatchers(ctx context.Context, from, to model.Time, matchers ...*labels.Matcher) ([]metric.Metric, error) {
236+
args := m.Called(ctx, from, to, matchers)
237+
return args.Get(0).([]metric.Metric), args.Error(1)
221238
}
222239

223240
func (m *mockDistributor) MetricsMetadata(ctx context.Context) ([]scrape.MetricMetadata, error) {

0 commit comments

Comments
 (0)