Skip to content

Commit

Permalink
Merge pull request #7326 from pedro-stanaka/fix/exemplars-store-multi…
Browse files Browse the repository at this point in the history
…tsdb

Query: fix exemplar proxying for receivers with multiple tenants
  • Loading branch information
fpetkovski authored May 6, 2024
2 parents 7ce3f35 + 167c32f commit 07838b8
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 24 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ We use *breaking :warning:* to mark changes that are not backward compatible (re
### Fixed

- [#7323](https://github.com/thanos-io/thanos/pull/7323) Sidecar: wait for prometheus on startup
- [#7326](https://github.com/thanos-io/thanos/pull/7326) Query: fixing exemplars proxy when querying stores with multiple tenants.

### Added

Expand Down
41 changes: 22 additions & 19 deletions pkg/exemplars/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"google.golang.org/grpc/status"

"github.com/thanos-io/thanos/pkg/exemplars/exemplarspb"
"github.com/thanos-io/thanos/pkg/store"
"github.com/thanos-io/thanos/pkg/store/storepb"
"github.com/thanos-io/thanos/pkg/tracing"
)
Expand Down Expand Up @@ -85,28 +86,21 @@ func (s *Proxy) Exemplars(req *exemplarspb.ExemplarsRequest, srv exemplarspb.Exe
for _, st := range s.exemplars() {
queryParts = queryParts[:0]

Matchers:
for _, matchers := range selectors {
matcherSet := make(map[string]struct{})
for _, m := range matchers {
for _, ls := range st.LabelSets {
if lv := ls.Get(m.Name); lv != "" {
if !m.Matches(lv) {
continue Matchers
} else {
// If the current matcher matches one external label,
// we don't add it to the current metric selector
// as Prometheus' Exemplars API cannot handle external labels.
continue
}
}
matcherSet[m.String()] = struct{}{}
}
for _, matcherSet := range selectors {
extLbls := st.LabelSets
if !store.LabelSetsMatch(matcherSet, extLbls...) {
continue
}

labelMatchers = labelMatchers[:0]
for m := range matcherSet {
labelMatchers = append(labelMatchers, m)
for _, m := range matcherSet {
if containsLabelName(m.Name, extLbls) {
// If the current matcher matches one external label,
// we don't add it to the current metric selector
// as Prometheus' Exemplars API cannot handle external labels.
continue
}
labelMatchers = append(labelMatchers, m.String())
}

queryParts = append(queryParts, "{"+strings.Join(labelMatchers, ", ")+"}")
Expand Down Expand Up @@ -164,6 +158,15 @@ func (s *Proxy) Exemplars(req *exemplarspb.ExemplarsRequest, srv exemplarspb.Exe
return nil
}

func containsLabelName(name string, sets []labels.Labels) bool {
for _, ls := range sets {
if ls.Get(name) != "" {
return true
}
}
return false
}

func (stream *exemplarsStream) receive(ctx context.Context) error {
exemplars, err := stream.client.Exemplars(ctx, stream.request)
if err != nil {
Expand Down
29 changes: 28 additions & 1 deletion pkg/exemplars/proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"google.golang.org/grpc/status"

"github.com/efficientgo/core/testutil"

"github.com/thanos-io/thanos/pkg/exemplars/exemplarspb"
"github.com/thanos-io/thanos/pkg/store/labelpb"
"github.com/thanos-io/thanos/pkg/store/storepb"
Expand Down Expand Up @@ -243,6 +244,31 @@ func TestProxy(t *testing.T) {
}),
},
},
{
name: "one external label matches one of the selector labels",
request: &exemplarspb.ExemplarsRequest{
Query: `http_request_duration_bucket{cluster="A"}`,
PartialResponseStrategy: storepb.PartialResponseStrategy_WARN,
},
clients: []*exemplarspb.ExemplarStore{
{
ExemplarsClient: &testExemplarClient{
response: exemplarspb.NewExemplarsResponse(&exemplarspb.ExemplarData{
SeriesLabels: labelpb.ZLabelSet{Labels: labelpb.ZLabelsFromPromLabels(labels.FromMap(map[string]string{"__name__": "http_request_duration_bucket"}))},
Exemplars: []*exemplarspb.Exemplar{{Value: 1}},
}),
},
LabelSets: []labels.Labels{labels.FromMap(map[string]string{"cluster": "non-matching"}), labels.FromMap(map[string]string{"cluster": "A"})},
},
},
server: &testExemplarServer{},
wantResponses: []*exemplarspb.ExemplarsResponse{
exemplarspb.NewExemplarsResponse(&exemplarspb.ExemplarData{
SeriesLabels: labelpb.ZLabelSet{Labels: labelpb.ZLabelsFromPromLabels(labels.FromMap(map[string]string{"__name__": "http_request_duration_bucket"}))},
Exemplars: []*exemplarspb.Exemplar{{Value: 1}},
}),
},
},
{
name: "external label selects stores",
request: &exemplarspb.ExemplarsRequest{
Expand Down Expand Up @@ -341,12 +367,13 @@ func TestProxy(t *testing.T) {
// for matched response for simplicity.
Outer:
for _, exp := range tc.wantResponses {
var lres *exemplarspb.ExemplarsResponse
for _, res := range tc.server.responses {
if reflect.DeepEqual(exp, res) {
continue Outer
}
}
t.Errorf("miss expected response %v", exp)
t.Errorf("miss expected response %v. got: %v", exp, lres)
}
})
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/store/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ func storeMatches(ctx context.Context, s Client, debugLogging bool, mint, maxt i
}

extLset := s.LabelSets()
if !labelSetsMatch(matchers, extLset...) {
if !LabelSetsMatch(matchers, extLset...) {
if debugLogging {
reason = fmt.Sprintf("external labels %v does not match request label matchers: %v", extLset, matchers)
}
Expand All @@ -449,16 +449,16 @@ func storeMatchDebugMetadata(s Client, storeDebugMatchers [][]*labels.Matcher) (

match := false
for _, sm := range storeDebugMatchers {
match = match || labelSetsMatch(sm, labels.FromStrings("__address__", addr))
match = match || LabelSetsMatch(sm, labels.FromStrings("__address__", addr))
}
if !match {
return false, fmt.Sprintf("__address__ %v does not match debug store metadata matchers: %v", addr, storeDebugMatchers)
}
return true, ""
}

// labelSetsMatch returns false if all label-set do not match the matchers (aka: OR is between all label-sets).
func labelSetsMatch(matchers []*labels.Matcher, lset ...labels.Labels) bool {
// LabelSetsMatch returns false if all label-set do not match the matchers (aka: OR is between all label-sets).
func LabelSetsMatch(matchers []*labels.Matcher, lset ...labels.Labels) bool {
if len(lset) == 0 {
return true
}
Expand Down

0 comments on commit 07838b8

Please sign in to comment.