-
Notifications
You must be signed in to change notification settings - Fork 177
Optimize queries using regex matchers for set lookups #602
Changes from all commits
d249d1d
32080de
3f60bf8
02dfa44
842e2a4
1e3cf63
4a66e34
87c3186
017871f
b81e86b
0f88eed
81cb028
954df56
c929d7a
959158e
591ae7c
2749e56
d30f3a2
6049a19
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ import ( | |
| "fmt" | ||
| "sort" | ||
| "strings" | ||
| "unicode/utf8" | ||
|
|
||
| "github.com/pkg/errors" | ||
| "github.com/prometheus/tsdb/chunkenc" | ||
|
|
@@ -266,6 +267,62 @@ func (q *blockQuerier) Close() error { | |
| return merr.Err() | ||
| } | ||
|
|
||
| // Bitmap used by func isRegexMetaCharacter to check whether a character needs to be escaped. | ||
| var regexMetaCharacterBytes [16]byte | ||
|
|
||
| // isRegexMetaCharacter reports whether byte b needs to be escaped. | ||
| func isRegexMetaCharacter(b byte) bool { | ||
| return b < utf8.RuneSelf && regexMetaCharacterBytes[b%16]&(1<<(b/16)) != 0 | ||
gouthamve marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| func init() { | ||
| for _, b := range []byte(`.+*?()|[]{}^$`) { | ||
| regexMetaCharacterBytes[b%16] |= 1 << (b / 16) | ||
| } | ||
| } | ||
|
|
||
| func findSetMatches(pattern string) []string { | ||
| // Return empty matches if the wrapper from Prometheus is missing. | ||
| if len(pattern) < 6 || pattern[:4] != "^(?:" || pattern[len(pattern)-2:] != ")$" { | ||
| return nil | ||
| } | ||
| escaped := false | ||
| sets := []*strings.Builder{&strings.Builder{}} | ||
| for i := 4; i < len(pattern)-2; i++ { | ||
| if escaped { | ||
| switch { | ||
| case isRegexMetaCharacter(pattern[i]): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this check necessary?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @brian-brazil Should I include the cases above?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, though check what Go does for escaping characters that don't need to be escaped.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm sorry, I was thinking about the wrong language.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have rechecked, the special characters you meant like |
||
| sets[len(sets)-1].WriteByte(pattern[i]) | ||
| case pattern[i] == '\\': | ||
| sets[len(sets)-1].WriteByte('\\') | ||
| default: | ||
| return nil | ||
| } | ||
| escaped = false | ||
| } else { | ||
| switch { | ||
| case isRegexMetaCharacter(pattern[i]): | ||
| if pattern[i] == '|' { | ||
| sets = append(sets, &strings.Builder{}) | ||
| } else { | ||
| return nil | ||
| } | ||
| case pattern[i] == '\\': | ||
| escaped = true | ||
| default: | ||
| sets[len(sets)-1].WriteByte(pattern[i]) | ||
| } | ||
naivewong marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| matches := make([]string, 0, len(sets)) | ||
| for _, s := range sets { | ||
| if s.Len() > 0 { | ||
| matches = append(matches, s.String()) | ||
| } | ||
| } | ||
| return matches | ||
| } | ||
|
|
||
| // PostingsForMatchers assembles a single postings iterator against the index reader | ||
| // based on the given matchers. | ||
| func PostingsForMatchers(ix IndexReader, ms ...labels.Matcher) (index.Postings, error) { | ||
|
|
@@ -346,6 +403,14 @@ func postingsForMatcher(ix IndexReader, m labels.Matcher) (index.Postings, error | |
| return ix.Postings(em.Name(), em.Value()) | ||
| } | ||
|
|
||
| // Fast-path for set matching. | ||
| if em, ok := m.(*labels.RegexpMatcher); ok { | ||
| setMatches := findSetMatches(em.Value()) | ||
| if len(setMatches) > 0 { | ||
| return postingsForSetMatcher(ix, em.Name(), setMatches) | ||
| } | ||
| } | ||
|
|
||
| tpls, err := ix.LabelValues(m.Name()) | ||
| if err != nil { | ||
| return nil, err | ||
|
|
@@ -411,6 +476,18 @@ func inversePostingsForMatcher(ix IndexReader, m labels.Matcher) (index.Postings | |
| return index.Merge(rit...), nil | ||
| } | ||
|
|
||
| func postingsForSetMatcher(ix IndexReader, name string, matches []string) (index.Postings, error) { | ||
| var its []index.Postings | ||
| for _, match := range matches { | ||
| if it, err := ix.Postings(name, match); err == nil { | ||
naivewong marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| its = append(its, it) | ||
| } else { | ||
| return nil, err | ||
| } | ||
| } | ||
| return index.Merge(its...), nil | ||
| } | ||
|
|
||
| func mergeStrings(a, b []string) []string { | ||
| maxl := len(a) | ||
| if len(b) > len(a) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.