github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/stores/tsdb/querier.go (about)

     1  // Copyright 2017 The Prometheus Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package tsdb
    15  
    16  import (
    17  	"sort"
    18  	"strings"
    19  	"unicode/utf8"
    20  
    21  	"github.com/pkg/errors"
    22  	"github.com/prometheus/prometheus/model/labels"
    23  	"github.com/prometheus/prometheus/storage"
    24  
    25  	"github.com/grafana/loki/pkg/storage/stores/tsdb/index"
    26  )
    27  
    28  // Bitmap used by func isRegexMetaCharacter to check whether a character needs to be escaped.
    29  var regexMetaCharacterBytes [16]byte
    30  
    31  // isRegexMetaCharacter reports whether byte b needs to be escaped.
    32  func isRegexMetaCharacter(b byte) bool {
    33  	return b < utf8.RuneSelf && regexMetaCharacterBytes[b%16]&(1<<(b/16)) != 0
    34  }
    35  
    36  func init() {
    37  	for _, b := range []byte(`.+*?()|[]{}^$`) {
    38  		regexMetaCharacterBytes[b%16] |= 1 << (b / 16)
    39  	}
    40  }
    41  
    42  // IndexReader provides reading access of serialized index data.
    43  type IndexReader interface {
    44  	// Bounds returns the earliest and latest samples in the index
    45  	Bounds() (int64, int64)
    46  
    47  	Checksum() uint32
    48  
    49  	// Symbols return an iterator over sorted string symbols that may occur in
    50  	// series' labels and indices. It is not safe to use the returned strings
    51  	// beyond the lifetime of the index reader.
    52  	Symbols() index.StringIter
    53  
    54  	// SortedLabelValues returns sorted possible label values.
    55  	SortedLabelValues(name string, matchers ...*labels.Matcher) ([]string, error)
    56  
    57  	// LabelValues returns possible label values which may not be sorted.
    58  	LabelValues(name string, matchers ...*labels.Matcher) ([]string, error)
    59  
    60  	// Postings returns the postings list iterator for the label pairs.
    61  	// The Postings here contain the offsets to the series inside the index.
    62  	// Found IDs are not strictly required to point to a valid Series, e.g.
    63  	// during background garbage collections. Input values must be sorted.
    64  	Postings(name string, shard *index.ShardAnnotation, values ...string) (index.Postings, error)
    65  
    66  	// Series populates the given labels and chunk metas for the series identified
    67  	// by the reference.
    68  	// Returns storage.ErrNotFound if the ref does not resolve to a known series.
    69  	Series(ref storage.SeriesRef, lset *labels.Labels, chks *[]index.ChunkMeta) (uint64, error)
    70  
    71  	// LabelNames returns all the unique label names present in the index in sorted order.
    72  	LabelNames(matchers ...*labels.Matcher) ([]string, error)
    73  
    74  	// LabelValueFor returns label value for the given label name in the series referred to by ID.
    75  	// If the series couldn't be found or the series doesn't have the requested label a
    76  	// storage.ErrNotFound is returned as error.
    77  	LabelValueFor(id storage.SeriesRef, label string) (string, error)
    78  
    79  	// LabelNamesFor returns all the label names for the series referred to by IDs.
    80  	// The names returned are sorted.
    81  	LabelNamesFor(ids ...storage.SeriesRef) ([]string, error)
    82  
    83  	// Close releases the underlying resources of the reader.
    84  	Close() error
    85  }
    86  
    87  // PostingsForMatchers assembles a single postings iterator against the index reader
    88  // based on the given matchers. The resulting postings are not ordered by series.
    89  func PostingsForMatchers(ix IndexReader, shard *index.ShardAnnotation, ms ...*labels.Matcher) (index.Postings, error) {
    90  	var its, notIts []index.Postings
    91  	// See which label must be non-empty.
    92  	// Optimization for case like {l=~".", l!="1"}.
    93  	labelMustBeSet := make(map[string]bool, len(ms))
    94  	for _, m := range ms {
    95  		if !m.Matches("") {
    96  			labelMustBeSet[m.Name] = true
    97  		}
    98  	}
    99  
   100  	for _, m := range ms {
   101  		if labelMustBeSet[m.Name] {
   102  			// If this matcher must be non-empty, we can be smarter.
   103  			matchesEmpty := m.Matches("")
   104  			isNot := m.Type == labels.MatchNotEqual || m.Type == labels.MatchNotRegexp
   105  			if isNot && matchesEmpty { // l!="foo"
   106  				// If the label can't be empty and is a Not and the inner matcher
   107  				// doesn't match empty, then subtract it out at the end.
   108  				inverse, err := m.Inverse()
   109  				if err != nil {
   110  					return nil, err
   111  				}
   112  
   113  				it, err := postingsForMatcher(ix, shard, inverse)
   114  				if err != nil {
   115  					return nil, err
   116  				}
   117  				notIts = append(notIts, it)
   118  			} else if isNot && !matchesEmpty { // l!=""
   119  				// If the label can't be empty and is a Not, but the inner matcher can
   120  				// be empty we need to use inversePostingsForMatcher.
   121  				inverse, err := m.Inverse()
   122  				if err != nil {
   123  					return nil, err
   124  				}
   125  
   126  				it, err := inversePostingsForMatcher(ix, shard, inverse)
   127  				if err != nil {
   128  					return nil, err
   129  				}
   130  				its = append(its, it)
   131  			} else { // l="a"
   132  				// Non-Not matcher, use normal postingsForMatcher.
   133  				it, err := postingsForMatcher(ix, shard, m)
   134  				if err != nil {
   135  					return nil, err
   136  				}
   137  				its = append(its, it)
   138  			}
   139  		} else { // l=""
   140  			// If the matchers for a labelname selects an empty value, it selects all
   141  			// the series which don't have the label name set too. See:
   142  			// https://github.com/prometheus/prometheus/issues/3575 and
   143  			// https://github.com/prometheus/prometheus/pull/3578#issuecomment-351653555
   144  			it, err := inversePostingsForMatcher(ix, shard, m)
   145  			if err != nil {
   146  				return nil, err
   147  			}
   148  			notIts = append(notIts, it)
   149  		}
   150  	}
   151  
   152  	// If there's nothing to subtract from, add in everything and remove the notIts later.
   153  	if len(its) == 0 && len(notIts) != 0 {
   154  		k, v := index.AllPostingsKey()
   155  		allPostings, err := ix.Postings(k, shard, v)
   156  		if err != nil {
   157  			return nil, err
   158  		}
   159  		its = append(its, allPostings)
   160  	}
   161  
   162  	it := index.Intersect(its...)
   163  
   164  	for _, n := range notIts {
   165  		it = index.Without(it, n)
   166  	}
   167  
   168  	return it, nil
   169  }
   170  
   171  func postingsForMatcher(ix IndexReader, shard *index.ShardAnnotation, m *labels.Matcher) (index.Postings, error) {
   172  	// This method will not return postings for missing labels.
   173  
   174  	// Fast-path for equal matching.
   175  	if m.Type == labels.MatchEqual {
   176  		return ix.Postings(m.Name, shard, m.Value)
   177  	}
   178  
   179  	// Fast-path for set matching.
   180  	if m.Type == labels.MatchRegexp {
   181  		setMatches := findSetMatches(m.GetRegexString())
   182  		if len(setMatches) > 0 {
   183  			sort.Strings(setMatches)
   184  			return ix.Postings(m.Name, shard, setMatches...)
   185  		}
   186  	}
   187  
   188  	vals, err := ix.LabelValues(m.Name)
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  
   193  	var res []string
   194  	lastVal, isSorted := "", true
   195  	for _, val := range vals {
   196  		if m.Matches(val) {
   197  			res = append(res, val)
   198  			if isSorted && val < lastVal {
   199  				isSorted = false
   200  			}
   201  			lastVal = val
   202  		}
   203  	}
   204  
   205  	if len(res) == 0 {
   206  		return index.EmptyPostings(), nil
   207  	}
   208  
   209  	if !isSorted {
   210  		sort.Strings(res)
   211  	}
   212  	return ix.Postings(m.Name, shard, res...)
   213  }
   214  
   215  // inversePostingsForMatcher returns the postings for the series with the label name set but not matching the matcher.
   216  func inversePostingsForMatcher(ix IndexReader, shard *index.ShardAnnotation, m *labels.Matcher) (index.Postings, error) {
   217  	vals, err := ix.LabelValues(m.Name)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  
   222  	var res []string
   223  	lastVal, isSorted := "", true
   224  	for _, val := range vals {
   225  		if !m.Matches(val) {
   226  			res = append(res, val)
   227  			if isSorted && val < lastVal {
   228  				isSorted = false
   229  			}
   230  			lastVal = val
   231  		}
   232  	}
   233  
   234  	if !isSorted {
   235  		sort.Strings(res)
   236  	}
   237  	return ix.Postings(m.Name, shard, res...)
   238  }
   239  
   240  func findSetMatches(pattern string) []string {
   241  	// Return empty matches if the wrapper from Prometheus is missing.
   242  	if len(pattern) < 6 || pattern[:4] != "^(?:" || pattern[len(pattern)-2:] != ")$" {
   243  		return nil
   244  	}
   245  	escaped := false
   246  	sets := []*strings.Builder{{}}
   247  	for i := 4; i < len(pattern)-2; i++ {
   248  		if escaped {
   249  			switch {
   250  			case isRegexMetaCharacter(pattern[i]):
   251  				sets[len(sets)-1].WriteByte(pattern[i])
   252  			case pattern[i] == '\\':
   253  				sets[len(sets)-1].WriteByte('\\')
   254  			default:
   255  				return nil
   256  			}
   257  			escaped = false
   258  		} else {
   259  			switch {
   260  			case isRegexMetaCharacter(pattern[i]):
   261  				if pattern[i] == '|' {
   262  					sets = append(sets, &strings.Builder{})
   263  				} else {
   264  					return nil
   265  				}
   266  			case pattern[i] == '\\':
   267  				escaped = true
   268  			default:
   269  				sets[len(sets)-1].WriteByte(pattern[i])
   270  			}
   271  		}
   272  	}
   273  	matches := make([]string, 0, len(sets))
   274  	for _, s := range sets {
   275  		if s.Len() > 0 {
   276  			matches = append(matches, s.String())
   277  		}
   278  	}
   279  	return matches
   280  }
   281  
   282  func labelValuesWithMatchers(r IndexReader, name string, matchers ...*labels.Matcher) ([]string, error) {
   283  	// We're only interested in metrics which have the label <name>.
   284  	requireLabel, err := labels.NewMatcher(labels.MatchNotEqual, name, "")
   285  	if err != nil {
   286  		return nil, errors.Wrapf(err, "Failed to instantiate label matcher")
   287  	}
   288  
   289  	var p index.Postings
   290  	p, err = PostingsForMatchers(r, nil, append(matchers, requireLabel)...)
   291  	if err != nil {
   292  		return nil, err
   293  	}
   294  
   295  	dedupe := map[string]interface{}{}
   296  	for p.Next() {
   297  		v, err := r.LabelValueFor(p.At(), name)
   298  		if err != nil {
   299  			if err == storage.ErrNotFound {
   300  				continue
   301  			}
   302  
   303  			return nil, err
   304  		}
   305  		dedupe[v] = nil
   306  	}
   307  
   308  	if err = p.Err(); err != nil {
   309  		return nil, err
   310  	}
   311  
   312  	values := make([]string, 0, len(dedupe))
   313  	for value := range dedupe {
   314  		values = append(values, value)
   315  	}
   316  
   317  	return values, nil
   318  }
   319  
   320  func labelNamesWithMatchers(r IndexReader, matchers ...*labels.Matcher) ([]string, error) {
   321  	p, err := PostingsForMatchers(r, nil, matchers...)
   322  	if err != nil {
   323  		return nil, err
   324  	}
   325  
   326  	var postings []storage.SeriesRef
   327  	for p.Next() {
   328  		postings = append(postings, p.At())
   329  	}
   330  	if p.Err() != nil {
   331  		return nil, errors.Wrapf(p.Err(), "postings for label names with matchers")
   332  	}
   333  
   334  	return r.LabelNamesFor(postings...)
   335  }