Skip to content

Commit 6e1af69

Browse files
GiedriusSbwplotka
authored andcommitted
Make it clear, explicit, and fail early on query with just external labels. (#1310) (#1321)
Signed-off-by: Bartek Plotka <bwplotka@gmail.com>
1 parent 40829e6 commit 6e1af69

File tree

5 files changed

+81
-45
lines changed

5 files changed

+81
-45
lines changed

pkg/store/prometheus.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,21 @@ func (p *PrometheusStore) putBuffer(b *[]byte) {
125125

126126
// Series returns all series for a requested time range and label matcher.
127127
func (p *PrometheusStore) Series(r *storepb.SeriesRequest, s storepb.Store_SeriesServer) error {
128-
ext := p.externalLabels()
128+
externalLabels := p.externalLabels()
129129

130-
match, newMatchers, err := labelsMatches(ext, r.Matchers)
130+
match, newMatchers, err := matchesExternalLabels(r.Matchers, externalLabels)
131131
if err != nil {
132132
return status.Error(codes.InvalidArgument, err.Error())
133133
}
134+
134135
if !match {
135136
return nil
136137
}
138+
139+
if len(newMatchers) == 0 {
140+
return status.Error(codes.InvalidArgument, errors.New("no matchers specified (excluding external labels)").Error())
141+
}
142+
137143
q := prompb.Query{StartTimestampMs: r.MinTime, EndTimestampMs: r.MaxTime}
138144

139145
// TODO(fabxc): import common definitions from prompb once we have a stable gRPC
@@ -166,7 +172,7 @@ func (p *PrometheusStore) Series(r *storepb.SeriesRequest, s storepb.Store_Serie
166172
span.SetTag("series_count", len(resp.Results[0].Timeseries))
167173

168174
for _, e := range resp.Results[0].Timeseries {
169-
lset := p.translateAndExtendLabels(e.Labels, ext)
175+
lset := p.translateAndExtendLabels(e.Labels, externalLabels)
170176

171177
if len(e.Samples) == 0 {
172178
// As found in https://github.com/improbable-eng/thanos/issues/381
@@ -286,8 +292,10 @@ func (p *PrometheusStore) promSeries(ctx context.Context, q prompb.Query) (*prom
286292
return &data, nil
287293
}
288294

289-
func labelsMatches(lset labels.Labels, ms []storepb.LabelMatcher) (bool, []storepb.LabelMatcher, error) {
290-
if len(lset) == 0 {
295+
// matchesExternalLabels filters out external labels matching from matcher if exsits as the local storage does not have them.
296+
// It also returns false if given matchers are not matching external labels.
297+
func matchesExternalLabels(ms []storepb.LabelMatcher, externalLabels labels.Labels) (bool, []storepb.LabelMatcher, error) {
298+
if len(externalLabels) == 0 {
291299
return true, ms, nil
292300
}
293301

@@ -299,7 +307,7 @@ func labelsMatches(lset labels.Labels, ms []storepb.LabelMatcher) (bool, []store
299307
return false, nil, err
300308
}
301309

302-
extValue := lset.Get(m.Name)
310+
extValue := externalLabels.Get(m.Name)
303311
if extValue == "" {
304312
// Agnostic to external labels.
305313
newMatcher = append(newMatcher, m)
@@ -312,6 +320,7 @@ func labelsMatches(lset labels.Labels, ms []storepb.LabelMatcher) (bool, []store
312320
return false, nil, nil
313321
}
314322
}
323+
315324
return true, newMatcher, nil
316325
}
317326

pkg/store/prometheus_test.go

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -61,36 +61,53 @@ func testPrometheusStoreSeriesE2e(t *testing.T, prefix string) {
6161
}, nil)
6262
testutil.Ok(t, err)
6363

64-
// Query all three samples except for the first one. Since we round up queried data
65-
// to seconds, we can test whether the extra sample gets stripped properly.
66-
srv := newStoreSeriesServer(ctx)
64+
{
65+
// Query all three samples except for the first one. Since we round up queried data
66+
// to seconds, we can test whether the extra sample gets stripped properly.
67+
srv := newStoreSeriesServer(ctx)
68+
69+
err = proxy.Series(&storepb.SeriesRequest{
70+
MinTime: baseT + 101,
71+
MaxTime: baseT + 300,
72+
Matchers: []storepb.LabelMatcher{
73+
{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "b"},
74+
},
75+
}, srv)
76+
testutil.Ok(t, err)
6777

68-
err = proxy.Series(&storepb.SeriesRequest{
69-
MinTime: baseT + 101,
70-
MaxTime: baseT + 300,
71-
Matchers: []storepb.LabelMatcher{
72-
{Type: storepb.LabelMatcher_EQ, Name: "a", Value: "b"},
73-
},
74-
}, srv)
75-
testutil.Ok(t, err)
78+
testutil.Equals(t, 1, len(srv.SeriesSet))
7679

77-
testutil.Equals(t, 1, len(srv.SeriesSet))
80+
testutil.Equals(t, []storepb.Label{
81+
{Name: "a", Value: "b"},
82+
{Name: "region", Value: "eu-west"},
83+
}, srv.SeriesSet[0].Labels)
7884

79-
testutil.Equals(t, []storepb.Label{
80-
{Name: "a", Value: "b"},
81-
{Name: "region", Value: "eu-west"},
82-
}, srv.SeriesSet[0].Labels)
85+
testutil.Equals(t, 1, len(srv.SeriesSet[0].Chunks))
8386

84-
testutil.Equals(t, 1, len(srv.SeriesSet[0].Chunks))
87+
c := srv.SeriesSet[0].Chunks[0]
88+
testutil.Equals(t, storepb.Chunk_XOR, c.Raw.Type)
8589

86-
c := srv.SeriesSet[0].Chunks[0]
87-
testutil.Equals(t, storepb.Chunk_XOR, c.Raw.Type)
90+
chk, err := chunkenc.FromData(chunkenc.EncXOR, c.Raw.Data)
91+
testutil.Ok(t, err)
8892

89-
chk, err := chunkenc.FromData(chunkenc.EncXOR, c.Raw.Data)
90-
testutil.Ok(t, err)
93+
samples := expandChunk(chk.Iterator())
94+
testutil.Equals(t, []sample{{baseT + 200, 2}, {baseT + 300, 3}}, samples)
9195

92-
samples := expandChunk(chk.Iterator())
93-
testutil.Equals(t, []sample{{baseT + 200, 2}, {baseT + 300, 3}}, samples)
96+
}
97+
// Querying by external labels only.
98+
{
99+
srv := newStoreSeriesServer(ctx)
100+
101+
err = proxy.Series(&storepb.SeriesRequest{
102+
MinTime: baseT + 101,
103+
MaxTime: baseT + 300,
104+
Matchers: []storepb.LabelMatcher{
105+
{Type: storepb.LabelMatcher_EQ, Name: "region", Value: "eu-west"},
106+
},
107+
}, srv)
108+
testutil.NotOk(t, err)
109+
testutil.Equals(t, "rpc error: code = InvalidArgument desc = no matchers specified (excluding external labels)", err.Error())
110+
}
94111
}
95112

96113
type sample struct {

pkg/store/proxy.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,14 +181,18 @@ func (s ctxRespSender) send(r *storepb.SeriesResponse) {
181181
// Series returns all series for a requested time range and label matcher. Requested series are taken from other
182182
// stores and proxied to RPC client. NOTE: Resulted data are not trimmed exactly to min and max time range.
183183
func (s *ProxyStore) Series(r *storepb.SeriesRequest, srv storepb.Store_SeriesServer) error {
184-
match, newMatchers, err := labelsMatches(s.selectorLabels, r.Matchers)
184+
match, newMatchers, err := matchesExternalLabels(r.Matchers, s.selectorLabels)
185185
if err != nil {
186186
return status.Error(codes.InvalidArgument, err.Error())
187187
}
188188
if !match {
189189
return nil
190190
}
191191

192+
if len(newMatchers) == 0 {
193+
return status.Error(codes.InvalidArgument, errors.New("no matchers specified (excluding external labels)").Error())
194+
}
195+
192196
var (
193197
g, gctx = errgroup.WithContext(srv.Context())
194198

@@ -220,7 +224,7 @@ func (s *ProxyStore) Series(r *storepb.SeriesRequest, srv storepb.Store_SeriesSe
220224
for _, st := range s.stores() {
221225
// We might be able to skip the store if its meta information indicates
222226
// it cannot have series matching our query.
223-
// NOTE: all matchers are validated in labelsMatches method so we explicitly ignore error.
227+
// NOTE: all matchers are validated in matchesExternalLabels method so we explicitly ignore error.
224228
spanStoreMathes, gctx := tracing.StartSpan(gctx, "store_matches")
225229
ok, _ := storeMatches(st, r.MinTime, r.MaxTime, r.Matchers...)
226230
spanStoreMathes.Finish()

pkg/store/proxy_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ func TestProxyStore_Series_RegressionFillResponseChannel(t *testing.T) {
644644
&storepb.SeriesRequest{
645645
MinTime: 1,
646646
MaxTime: 300,
647-
Matchers: []storepb.LabelMatcher{{Name: "fed", Value: "a", Type: storepb.LabelMatcher_EQ}},
647+
Matchers: []storepb.LabelMatcher{{Name: "any", Value: ".*", Type: storepb.LabelMatcher_RE}},
648648
}, s,
649649
))
650650
testutil.Equals(t, 0, len(s.SeriesSet))

pkg/store/tsdb.go

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,37 +22,37 @@ import (
2222
// It attaches the provided external labels to all results. It only responds with raw data
2323
// and does not support downsampling.
2424
type TSDBStore struct {
25-
logger log.Logger
26-
db *tsdb.DB
27-
component component.SourceStoreAPI
28-
labels labels.Labels
25+
logger log.Logger
26+
db *tsdb.DB
27+
component component.SourceStoreAPI
28+
externalLabels labels.Labels
2929
}
3030

3131
// NewTSDBStore creates a new TSDBStore.
32-
func NewTSDBStore(logger log.Logger, reg prometheus.Registerer, db *tsdb.DB, component component.SourceStoreAPI, externalLabels labels.Labels) *TSDBStore {
32+
func NewTSDBStore(logger log.Logger, _ prometheus.Registerer, db *tsdb.DB, component component.SourceStoreAPI, externalLabels labels.Labels) *TSDBStore {
3333
if logger == nil {
3434
logger = log.NewNopLogger()
3535
}
3636
return &TSDBStore{
37-
logger: logger,
38-
db: db,
39-
component: component,
40-
labels: externalLabels,
37+
logger: logger,
38+
db: db,
39+
component: component,
40+
externalLabels: externalLabels,
4141
}
4242
}
4343

4444
// Info returns store information about the Prometheus instance.
4545
func (s *TSDBStore) Info(ctx context.Context, r *storepb.InfoRequest) (*storepb.InfoResponse, error) {
4646
res := &storepb.InfoResponse{
47-
Labels: make([]storepb.Label, 0, len(s.labels)),
47+
Labels: make([]storepb.Label, 0, len(s.externalLabels)),
4848
StoreType: s.component.ToProto(),
4949
MinTime: 0,
5050
MaxTime: math.MaxInt64,
5151
}
5252
if blocks := s.db.Blocks(); len(blocks) > 0 {
5353
res.MinTime = blocks[0].Meta().MinTime
5454
}
55-
for _, l := range s.labels {
55+
for _, l := range s.externalLabels {
5656
res.Labels = append(res.Labels, storepb.Label{
5757
Name: l.Name,
5858
Value: l.Value,
@@ -73,13 +73,19 @@ func (s *TSDBStore) Info(ctx context.Context, r *storepb.InfoRequest) (*storepb.
7373
// Series returns all series for a requested time range and label matcher. The returned data may
7474
// exceed the requested time bounds.
7575
func (s *TSDBStore) Series(r *storepb.SeriesRequest, srv storepb.Store_SeriesServer) error {
76-
match, newMatchers, err := labelsMatches(s.labels, r.Matchers)
76+
match, newMatchers, err := matchesExternalLabels(r.Matchers, s.externalLabels)
7777
if err != nil {
7878
return status.Error(codes.InvalidArgument, err.Error())
7979
}
80+
8081
if !match {
8182
return nil
8283
}
84+
85+
if len(newMatchers) == 0 {
86+
return status.Error(codes.InvalidArgument, errors.New("no matchers specified (excluding external labels)").Error())
87+
}
88+
8389
matchers, err := translateMatchers(newMatchers)
8490
if err != nil {
8591
return status.Error(codes.InvalidArgument, err.Error())
@@ -113,7 +119,7 @@ func (s *TSDBStore) Series(r *storepb.SeriesRequest, srv storepb.Store_SeriesSer
113119
return status.Errorf(codes.Internal, "encode chunk: %s", err)
114120
}
115121

116-
respSeries.Labels = s.translateAndExtendLabels(series.Labels(), s.labels)
122+
respSeries.Labels = s.translateAndExtendLabels(series.Labels(), s.externalLabels)
117123
respSeries.Chunks = append(respSeries.Chunks[:0], c...)
118124

119125
if err := srv.Send(storepb.NewSeriesResponse(&respSeries)); err != nil {

0 commit comments

Comments
 (0)