Only do regex lookups when there was no equality match.

For the label matching index-based preselection phase, don't do an OR
between equality and non-equality matchers. Execute only one of the two
(with equality matchers preferred when present).

Fixes https://github.com/prometheus/prometheus/issues/924
This commit is contained in:
Julius Volz 2015-07-23 22:46:13 +02:00
parent a59b7ac7f8
commit 517badc21d
2 changed files with 42 additions and 27 deletions

View File

@ -428,39 +428,40 @@ func (s *memorySeriesStorage) MetricsForLabelMatchers(matchers ...*metric.LabelM
} }
var resFPs map[clientmodel.Fingerprint]struct{} var resFPs map[clientmodel.Fingerprint]struct{}
// If we cannot make a preselection based on equality matchers, expanding the other matchers to labels
// and intersecting their fingerprints is still likely to be the best choice.
if len(equals) > 0 { if len(equals) > 0 {
resFPs = s.fingerprintsForLabelPairs(equals...) resFPs = s.fingerprintsForLabelPairs(equals...)
} } else {
var remaining metric.LabelMatchers // If we cannot make a preselection based on equality matchers, expanding the other matchers to labels
for _, matcher := range filters { // and intersecting their fingerprints is still likely to be the best choice.
// Equal matches are all empty values. var remaining metric.LabelMatchers
if matcher.Match("") { for _, matcher := range filters {
remaining = append(remaining, matcher) // Equal matches are all empty values.
continue if matcher.Match("") {
} remaining = append(remaining, matcher)
intersection := map[clientmodel.Fingerprint]struct{}{} continue
}
intersection := map[clientmodel.Fingerprint]struct{}{}
matches := matcher.Filter(s.LabelValuesForLabelName(matcher.Name)) matches := matcher.Filter(s.LabelValuesForLabelName(matcher.Name))
if len(matches) == 0 { if len(matches) == 0 {
return nil return nil
} }
for _, v := range matches { for _, v := range matches {
fps := s.fingerprintsForLabelPairs(metric.LabelPair{ fps := s.fingerprintsForLabelPairs(metric.LabelPair{
Name: matcher.Name, Name: matcher.Name,
Value: v, Value: v,
}) })
for fp := range fps { for fp := range fps {
if _, ok := resFPs[fp]; ok || resFPs == nil { if _, ok := resFPs[fp]; ok || resFPs == nil {
intersection[fp] = struct{}{} intersection[fp] = struct{}{}
}
} }
} }
resFPs = intersection
} }
resFPs = intersection // The intersected matchers no longer need to be compared against the actual metrics.
filters = remaining
} }
// The intersected matchers no longer need to be compared against the actual metrics.
filters = remaining
result := make(map[clientmodel.Fingerprint]clientmodel.COWMetric, len(resFPs)) result := make(map[clientmodel.Fingerprint]clientmodel.COWMetric, len(resFPs))
for fp := range resFPs { for fp := range resFPs {

View File

@ -70,7 +70,7 @@ func TestMatches(t *testing.T) {
}{ }{
{ {
matchers: metric.LabelMatchers{newMatcher(metric.Equal, "label1", "x")}, matchers: metric.LabelMatchers{newMatcher(metric.Equal, "label1", "x")},
expected: fingerprints[:0], expected: clientmodel.Fingerprints{},
}, },
{ {
matchers: metric.LabelMatchers{newMatcher(metric.Equal, "label1", "test_0")}, matchers: metric.LabelMatchers{newMatcher(metric.Equal, "label1", "test_0")},
@ -161,6 +161,20 @@ func TestMatches(t *testing.T) {
}, },
expected: append(append(clientmodel.Fingerprints{}, fingerprints[30:35]...), fingerprints[45:60]...), expected: append(append(clientmodel.Fingerprints{}, fingerprints[30:35]...), fingerprints[45:60]...),
}, },
{
matchers: metric.LabelMatchers{
newMatcher(metric.Equal, "label1", `nonexistent`),
newMatcher(metric.RegexMatch, "label2", `test`),
},
expected: clientmodel.Fingerprints{},
},
{
matchers: metric.LabelMatchers{
newMatcher(metric.Equal, "label1", `test_0`),
newMatcher(metric.RegexMatch, "label2", `nonexistent`),
},
expected: clientmodel.Fingerprints{},
},
} }
for _, mt := range matcherTests { for _, mt := range matcherTests {