github.com/thanos-io/thanos@v0.32.5/pkg/store/storepb/custom.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package storepb
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/binary"
     9  	"fmt"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/gogo/protobuf/types"
    15  	"github.com/pkg/errors"
    16  	"github.com/prometheus/prometheus/model/labels"
    17  	"google.golang.org/grpc/codes"
    18  
    19  	"github.com/thanos-io/thanos/pkg/store/labelpb"
    20  )
    21  
    22  var PartialResponseStrategyValues = func() []string {
    23  	var s []string
    24  	for k := range PartialResponseStrategy_value {
    25  		s = append(s, k)
    26  	}
    27  	sort.Strings(s)
    28  	return s
    29  }()
    30  
    31  func NewWarnSeriesResponse(err error) *SeriesResponse {
    32  	return &SeriesResponse{
    33  		Result: &SeriesResponse_Warning{
    34  			Warning: err.Error(),
    35  		},
    36  	}
    37  }
    38  
    39  func NewSeriesResponse(series *Series) *SeriesResponse {
    40  	return &SeriesResponse{
    41  		Result: &SeriesResponse_Series{
    42  			Series: series,
    43  		},
    44  	}
    45  }
    46  
    47  func NewHintsSeriesResponse(hints *types.Any) *SeriesResponse {
    48  	return &SeriesResponse{
    49  		Result: &SeriesResponse_Hints{
    50  			Hints: hints,
    51  		},
    52  	}
    53  }
    54  
    55  func GRPCCodeFromWarn(warn string) codes.Code {
    56  	if strings.Contains(warn, "rpc error: code = ResourceExhausted") {
    57  		return codes.ResourceExhausted
    58  	}
    59  	if strings.Contains(warn, "rpc error: code = Code(422)") {
    60  		return 422
    61  	}
    62  	return codes.Unknown
    63  }
    64  
    65  type emptySeriesSet struct{}
    66  
    67  func (emptySeriesSet) Next() bool                       { return false }
    68  func (emptySeriesSet) At() (labels.Labels, []AggrChunk) { return nil, nil }
    69  func (emptySeriesSet) Err() error                       { return nil }
    70  
    71  // EmptySeriesSet returns a new series set that contains no series.
    72  func EmptySeriesSet() SeriesSet {
    73  	return emptySeriesSet{}
    74  }
    75  
    76  // MergeSeriesSets takes all series sets and returns as a union single series set.
    77  // It assumes series are sorted by labels within single SeriesSet, similar to remote read guarantees.
    78  // However, they can be partial: in such case, if the single SeriesSet returns the same series within many iterations,
    79  // MergeSeriesSets will merge those into one.
    80  //
    81  // It also assumes in a "best effort" way that chunks are sorted by min time. It's done as an optimization only, so if input
    82  // series' chunks are NOT sorted, the only consequence is that the duplicates might be not correctly removed. This is double checked
    83  // which on just-before PromQL level as well, so the only consequence is increased network bandwidth.
    84  // If all chunks were sorted, MergeSeriesSet ALSO returns sorted chunks by min time.
    85  //
    86  // Chunks within the same series can also overlap (within all SeriesSet
    87  // as well as single SeriesSet alone). If the chunk ranges overlap, the *exact* chunk duplicates will be removed
    88  // (except one), and any other overlaps will be appended into on chunks slice.
    89  func MergeSeriesSets(all ...SeriesSet) SeriesSet {
    90  	switch len(all) {
    91  	case 0:
    92  		return emptySeriesSet{}
    93  	case 1:
    94  		return newUniqueSeriesSet(all[0])
    95  	}
    96  	h := len(all) / 2
    97  
    98  	return newMergedSeriesSet(
    99  		MergeSeriesSets(all[:h]...),
   100  		MergeSeriesSets(all[h:]...),
   101  	)
   102  }
   103  
   104  // SeriesSet is a set of series and their corresponding chunks.
   105  // The set is sorted by the label sets. Chunks may be overlapping or expected of order.
   106  type SeriesSet interface {
   107  	Next() bool
   108  	At() (labels.Labels, []AggrChunk)
   109  	Err() error
   110  }
   111  
   112  // mergedSeriesSet takes two series sets as a single series set.
   113  type mergedSeriesSet struct {
   114  	a, b SeriesSet
   115  
   116  	lset         labels.Labels
   117  	chunks       []AggrChunk
   118  	adone, bdone bool
   119  }
   120  
   121  func newMergedSeriesSet(a, b SeriesSet) *mergedSeriesSet {
   122  	s := &mergedSeriesSet{a: a, b: b}
   123  	// Initialize first elements of both sets as Next() needs
   124  	// one element look-ahead.
   125  	s.adone = !s.a.Next()
   126  	s.bdone = !s.b.Next()
   127  
   128  	return s
   129  }
   130  
   131  func (s *mergedSeriesSet) At() (labels.Labels, []AggrChunk) {
   132  	return s.lset, s.chunks
   133  }
   134  
   135  func (s *mergedSeriesSet) Err() error {
   136  	if s.a.Err() != nil {
   137  		return s.a.Err()
   138  	}
   139  	return s.b.Err()
   140  }
   141  
   142  func (s *mergedSeriesSet) compare() int {
   143  	if s.adone {
   144  		return 1
   145  	}
   146  	if s.bdone {
   147  		return -1
   148  	}
   149  	lsetA, _ := s.a.At()
   150  	lsetB, _ := s.b.At()
   151  	return labels.Compare(lsetA, lsetB)
   152  }
   153  
   154  func (s *mergedSeriesSet) Next() bool {
   155  	if s.adone && s.bdone || s.Err() != nil {
   156  		return false
   157  	}
   158  
   159  	d := s.compare()
   160  	if d > 0 {
   161  		s.lset, s.chunks = s.b.At()
   162  		s.bdone = !s.b.Next()
   163  		return true
   164  	}
   165  	if d < 0 {
   166  		s.lset, s.chunks = s.a.At()
   167  		s.adone = !s.a.Next()
   168  		return true
   169  	}
   170  
   171  	// Both a and b contains the same series. Go through all chunks, remove duplicates and concatenate chunks from both
   172  	// series sets. We best effortly assume chunks are sorted by min time. If not, we will not detect all deduplicate which will
   173  	// be account on select layer anyway. We do it still for early optimization.
   174  	lset, chksA := s.a.At()
   175  	_, chksB := s.b.At()
   176  	s.lset = lset
   177  
   178  	// Slice reuse is not generally safe with nested merge iterators.
   179  	// We err on the safe side an create a new slice.
   180  	s.chunks = make([]AggrChunk, 0, len(chksA)+len(chksB))
   181  
   182  	b := 0
   183  Outer:
   184  	for a := range chksA {
   185  		for {
   186  			if b >= len(chksB) {
   187  				// No more b chunks.
   188  				s.chunks = append(s.chunks, chksA[a:]...)
   189  				break Outer
   190  			}
   191  
   192  			cmp := chksA[a].Compare(chksB[b])
   193  			if cmp > 0 {
   194  				s.chunks = append(s.chunks, chksA[a])
   195  				break
   196  			}
   197  			if cmp < 0 {
   198  				s.chunks = append(s.chunks, chksB[b])
   199  				b++
   200  				continue
   201  			}
   202  
   203  			// Exact duplicated chunks, discard one from b.
   204  			b++
   205  		}
   206  	}
   207  
   208  	if b < len(chksB) {
   209  		s.chunks = append(s.chunks, chksB[b:]...)
   210  	}
   211  
   212  	s.adone = !s.a.Next()
   213  	s.bdone = !s.b.Next()
   214  	return true
   215  }
   216  
   217  // uniqueSeriesSet takes one series set and ensures each iteration contains single, full series.
   218  type uniqueSeriesSet struct {
   219  	SeriesSet
   220  	done bool
   221  
   222  	peek *Series
   223  
   224  	lset   labels.Labels
   225  	chunks []AggrChunk
   226  }
   227  
   228  func newUniqueSeriesSet(wrapped SeriesSet) *uniqueSeriesSet {
   229  	return &uniqueSeriesSet{SeriesSet: wrapped}
   230  }
   231  
   232  func (s *uniqueSeriesSet) At() (labels.Labels, []AggrChunk) {
   233  	return s.lset, s.chunks
   234  }
   235  
   236  func (s *uniqueSeriesSet) Next() bool {
   237  	if s.Err() != nil {
   238  		return false
   239  	}
   240  
   241  	for !s.done {
   242  		if s.done = !s.SeriesSet.Next(); s.done {
   243  			break
   244  		}
   245  		lset, chks := s.SeriesSet.At()
   246  		if s.peek == nil {
   247  			s.peek = &Series{Labels: labelpb.ZLabelsFromPromLabels(lset), Chunks: chks}
   248  			continue
   249  		}
   250  
   251  		if labels.Compare(lset, s.peek.PromLabels()) != 0 {
   252  			s.lset, s.chunks = s.peek.PromLabels(), s.peek.Chunks
   253  			s.peek = &Series{Labels: labelpb.ZLabelsFromPromLabels(lset), Chunks: chks}
   254  			return true
   255  		}
   256  
   257  		// We assume non-overlapping, sorted chunks. This is best effort only, if it's otherwise it
   258  		// will just be duplicated, but well handled by StoreAPI consumers.
   259  		s.peek.Chunks = append(s.peek.Chunks, chks...)
   260  	}
   261  
   262  	if s.peek == nil {
   263  		return false
   264  	}
   265  
   266  	s.lset, s.chunks = s.peek.PromLabels(), s.peek.Chunks
   267  	s.peek = nil
   268  	return true
   269  }
   270  
   271  // Compare returns positive 1 if chunk is smaller -1 if larger than b by min time, then max time.
   272  // It returns 0 if chunks are exactly the same.
   273  func (m AggrChunk) Compare(b AggrChunk) int {
   274  	if m.MinTime < b.MinTime {
   275  		return 1
   276  	}
   277  	if m.MinTime > b.MinTime {
   278  		return -1
   279  	}
   280  
   281  	// Same min time.
   282  	if m.MaxTime < b.MaxTime {
   283  		return 1
   284  	}
   285  	if m.MaxTime > b.MaxTime {
   286  		return -1
   287  	}
   288  
   289  	// We could use proto.Equal, but we need ordering as well.
   290  	for _, cmp := range []func() int{
   291  		func() int { return m.Raw.Compare(b.Raw) },
   292  		func() int { return m.Count.Compare(b.Count) },
   293  		func() int { return m.Sum.Compare(b.Sum) },
   294  		func() int { return m.Min.Compare(b.Min) },
   295  		func() int { return m.Max.Compare(b.Max) },
   296  		func() int { return m.Counter.Compare(b.Counter) },
   297  	} {
   298  		if c := cmp(); c == 0 {
   299  			continue
   300  		} else {
   301  			return c
   302  		}
   303  	}
   304  	return 0
   305  }
   306  
   307  // Compare returns positive 1 if chunk is smaller -1 if larger.
   308  // It returns 0 if chunks are exactly the same.
   309  func (m *Chunk) Compare(b *Chunk) int {
   310  	if m == nil && b == nil {
   311  		return 0
   312  	}
   313  	if b == nil {
   314  		return 1
   315  	}
   316  	if m == nil {
   317  		return -1
   318  	}
   319  
   320  	if m.Type < b.Type {
   321  		return 1
   322  	}
   323  	if m.Type > b.Type {
   324  		return -1
   325  	}
   326  	return bytes.Compare(m.Data, b.Data)
   327  }
   328  
   329  func (x *PartialResponseStrategy) UnmarshalJSON(entry []byte) error {
   330  	fieldStr, err := strconv.Unquote(string(entry))
   331  	if err != nil {
   332  		return errors.Wrapf(err, fmt.Sprintf("failed to unqote %v, in order to unmarshal as 'partial_response_strategy'. Possible values are %s", string(entry), strings.Join(PartialResponseStrategyValues, ",")))
   333  	}
   334  
   335  	if fieldStr == "" {
   336  		// NOTE: For Rule default is abort as this is recommended for alerting.
   337  		*x = PartialResponseStrategy_ABORT
   338  		return nil
   339  	}
   340  
   341  	strategy, ok := PartialResponseStrategy_value[strings.ToUpper(fieldStr)]
   342  	if !ok {
   343  		return errors.Errorf(fmt.Sprintf("failed to unmarshal %v as 'partial_response_strategy'. Possible values are %s", string(entry), strings.Join(PartialResponseStrategyValues, ",")))
   344  	}
   345  	*x = PartialResponseStrategy(strategy)
   346  	return nil
   347  }
   348  
   349  func (x *PartialResponseStrategy) MarshalJSON() ([]byte, error) {
   350  	return []byte(strconv.Quote(x.String())), nil
   351  }
   352  
   353  // PromMatchersToMatchers returns proto matchers from Prometheus matchers.
   354  // NOTE: It allocates memory.
   355  func PromMatchersToMatchers(ms ...*labels.Matcher) ([]LabelMatcher, error) {
   356  	res := make([]LabelMatcher, 0, len(ms))
   357  	for _, m := range ms {
   358  		var t LabelMatcher_Type
   359  
   360  		switch m.Type {
   361  		case labels.MatchEqual:
   362  			t = LabelMatcher_EQ
   363  		case labels.MatchNotEqual:
   364  			t = LabelMatcher_NEQ
   365  		case labels.MatchRegexp:
   366  			t = LabelMatcher_RE
   367  		case labels.MatchNotRegexp:
   368  			t = LabelMatcher_NRE
   369  		default:
   370  			return nil, errors.Errorf("unrecognized matcher type %d", m.Type)
   371  		}
   372  		res = append(res, LabelMatcher{Type: t, Name: m.Name, Value: m.Value})
   373  	}
   374  	return res, nil
   375  }
   376  
   377  // MatchersToPromMatchers returns Prometheus matchers from proto matchers.
   378  // NOTE: It allocates memory.
   379  func MatchersToPromMatchers(ms ...LabelMatcher) ([]*labels.Matcher, error) {
   380  	res := make([]*labels.Matcher, 0, len(ms))
   381  	for _, m := range ms {
   382  		var t labels.MatchType
   383  
   384  		switch m.Type {
   385  		case LabelMatcher_EQ:
   386  			t = labels.MatchEqual
   387  		case LabelMatcher_NEQ:
   388  			t = labels.MatchNotEqual
   389  		case LabelMatcher_RE:
   390  			t = labels.MatchRegexp
   391  		case LabelMatcher_NRE:
   392  			t = labels.MatchNotRegexp
   393  		default:
   394  			return nil, errors.Errorf("unrecognized label matcher type %d", m.Type)
   395  		}
   396  		m, err := labels.NewMatcher(t, m.Name, m.Value)
   397  		if err != nil {
   398  			return nil, err
   399  		}
   400  		res = append(res, m)
   401  	}
   402  	return res, nil
   403  }
   404  
   405  // MatchersToString converts label matchers to string format.
   406  // String should be parsable as a valid PromQL query metric selector.
   407  func MatchersToString(ms ...LabelMatcher) string {
   408  	var res string
   409  	for i, m := range ms {
   410  		res += m.PromString()
   411  		if i < len(ms)-1 {
   412  			res += ", "
   413  		}
   414  	}
   415  	return "{" + res + "}"
   416  }
   417  
   418  // PromMatchersToString converts prometheus label matchers to string format.
   419  // String should be parsable as a valid PromQL query metric selector.
   420  func PromMatchersToString(ms ...*labels.Matcher) string {
   421  	var res string
   422  	for i, m := range ms {
   423  		res += m.String()
   424  		if i < len(ms)-1 {
   425  			res += ", "
   426  		}
   427  	}
   428  	return "{" + res + "}"
   429  }
   430  
   431  func (m *LabelMatcher) PromString() string {
   432  	return fmt.Sprintf("%s%s%q", m.Name, m.Type.PromString(), m.Value)
   433  }
   434  
   435  func (x LabelMatcher_Type) PromString() string {
   436  	typeToStr := map[LabelMatcher_Type]string{
   437  		LabelMatcher_EQ:  "=",
   438  		LabelMatcher_NEQ: "!=",
   439  		LabelMatcher_RE:  "=~",
   440  		LabelMatcher_NRE: "!~",
   441  	}
   442  	if str, ok := typeToStr[x]; ok {
   443  		return str
   444  	}
   445  	panic("unknown match type")
   446  }
   447  
   448  // PromLabels return Prometheus labels.Labels without extra allocation.
   449  func (m *Series) PromLabels() labels.Labels {
   450  	return labelpb.ZLabelsToPromLabels(m.Labels)
   451  }
   452  
   453  // Deprecated.
   454  // TODO(bwplotka): Remove this once Cortex dep will stop using it.
   455  type Label = labelpb.ZLabel
   456  
   457  // Deprecated.
   458  // TODO(bwplotka): Remove this in next PR. Done to reduce diff only.
   459  type LabelSet = labelpb.ZLabelSet
   460  
   461  // Deprecated.
   462  // TODO(bwplotka): Remove this once Cortex dep will stop using it.
   463  func CompareLabels(a, b []Label) int {
   464  	return labels.Compare(labelpb.ZLabelsToPromLabels(a), labelpb.ZLabelsToPromLabels(b))
   465  }
   466  
   467  // Deprecated.
   468  // TODO(bwplotka): Remove this once Cortex dep will stop using it.
   469  func LabelsToPromLabelsUnsafe(lset []Label) labels.Labels {
   470  	return labelpb.ZLabelsToPromLabels(lset)
   471  }
   472  
   473  // XORNumSamples return number of samples. Returns 0 if it's not XOR chunk.
   474  func (m *Chunk) XORNumSamples() int {
   475  	if m.Type == Chunk_XOR {
   476  		return int(binary.BigEndian.Uint16(m.Data))
   477  	}
   478  	return 0
   479  }
   480  
   481  type SeriesStatsCounter struct {
   482  	lastSeriesHash uint64
   483  
   484  	Series  int
   485  	Chunks  int
   486  	Samples int
   487  }
   488  
   489  func (c *SeriesStatsCounter) CountSeries(seriesLabels []labelpb.ZLabel) {
   490  	seriesHash := labelpb.HashWithPrefix("", seriesLabels)
   491  	if c.lastSeriesHash != 0 || seriesHash != c.lastSeriesHash {
   492  		c.lastSeriesHash = seriesHash
   493  		c.Series++
   494  	}
   495  }
   496  
   497  func (c *SeriesStatsCounter) Count(series *Series) {
   498  	c.CountSeries(series.Labels)
   499  	for _, chk := range series.Chunks {
   500  		if chk.Raw != nil {
   501  			c.Chunks++
   502  			c.Samples += chk.Raw.XORNumSamples()
   503  		}
   504  
   505  		if chk.Count != nil {
   506  			c.Chunks++
   507  			c.Samples += chk.Count.XORNumSamples()
   508  		}
   509  
   510  		if chk.Counter != nil {
   511  			c.Chunks++
   512  			c.Samples += chk.Counter.XORNumSamples()
   513  		}
   514  
   515  		if chk.Max != nil {
   516  			c.Chunks++
   517  			c.Samples += chk.Max.XORNumSamples()
   518  		}
   519  
   520  		if chk.Min != nil {
   521  			c.Chunks++
   522  			c.Samples += chk.Min.XORNumSamples()
   523  		}
   524  
   525  		if chk.Sum != nil {
   526  			c.Chunks++
   527  			c.Samples += chk.Sum.XORNumSamples()
   528  		}
   529  	}
   530  }
   531  
   532  func (m *SeriesRequest) ToPromQL() string {
   533  	return m.QueryHints.toPromQL(m.Matchers)
   534  }
   535  
   536  // IsSafeToExecute returns true if the function or aggregation from the query hint
   537  // can be safely executed by the underlying Prometheus instance without affecting the
   538  // result of the query.
   539  func (m *QueryHints) IsSafeToExecute() bool {
   540  	distributiveOperations := []string{
   541  		"max",
   542  		"max_over_time",
   543  		"min",
   544  		"min_over_time",
   545  		"group",
   546  	}
   547  	for _, op := range distributiveOperations {
   548  		if m.Func.Name == op {
   549  			return true
   550  		}
   551  	}
   552  
   553  	return false
   554  }