github.com/thanos-io/thanos@v0.32.5/pkg/exemplars/tsdb.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package exemplars
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/gogo/status"
    10  	"github.com/pkg/errors"
    11  	"github.com/prometheus/prometheus/model/labels"
    12  	"github.com/prometheus/prometheus/storage"
    13  	"google.golang.org/grpc/codes"
    14  
    15  	"github.com/thanos-io/thanos/pkg/exemplars/exemplarspb"
    16  	"github.com/thanos-io/thanos/pkg/store/labelpb"
    17  )
    18  
    19  // TSDB allows fetching exemplars from a TSDB instance.
    20  type TSDB struct {
    21  	db storage.ExemplarQueryable
    22  
    23  	extLabels labels.Labels
    24  	mtx       sync.RWMutex
    25  }
    26  
    27  // NewTSDB creates new exemplars.TSDB.
    28  func NewTSDB(db storage.ExemplarQueryable, extLabels labels.Labels) *TSDB {
    29  	return &TSDB{
    30  		db:        db,
    31  		extLabels: extLabels,
    32  	}
    33  }
    34  
    35  func (t *TSDB) SetExtLabels(extLabels labels.Labels) {
    36  	t.mtx.Lock()
    37  	defer t.mtx.Unlock()
    38  
    39  	t.extLabels = extLabels
    40  }
    41  
    42  func (t *TSDB) getExtLabels() labels.Labels {
    43  	t.mtx.RLock()
    44  	defer t.mtx.RUnlock()
    45  
    46  	return t.extLabels
    47  }
    48  
    49  // Exemplars returns all specified exemplars from a TSDB instance.
    50  func (t *TSDB) Exemplars(matchers [][]*labels.Matcher, start, end int64, s exemplarspb.Exemplars_ExemplarsServer) error {
    51  	match, selectors := selectorsMatchesExternalLabels(matchers, t.getExtLabels())
    52  
    53  	if !match {
    54  		return nil
    55  	}
    56  
    57  	if len(selectors) == 0 {
    58  		return status.Error(codes.InvalidArgument, errors.New("no matchers specified (excluding external labels)").Error())
    59  	}
    60  
    61  	eq, err := t.db.ExemplarQuerier(s.Context())
    62  	if err != nil {
    63  		return status.Error(codes.Internal, err.Error())
    64  	}
    65  
    66  	exemplars, err := eq.Select(start, end, selectors...)
    67  	if err != nil {
    68  		return status.Error(codes.Internal, err.Error())
    69  	}
    70  
    71  	for _, e := range exemplars {
    72  		exd := exemplarspb.ExemplarData{
    73  			SeriesLabels: labelpb.ZLabelSet{
    74  				Labels: labelpb.ZLabelsFromPromLabels(labelpb.ExtendSortedLabels(e.SeriesLabels, t.getExtLabels())),
    75  			},
    76  			Exemplars: exemplarspb.ExemplarsFromPromExemplars(e.Exemplars),
    77  		}
    78  		if err := s.Send(exemplarspb.NewExemplarsResponse(&exd)); err != nil {
    79  			return status.Error(codes.Aborted, err.Error())
    80  		}
    81  	}
    82  	return nil
    83  }
    84  
    85  // selectorsMatchesExternalLabels returns false if none of the selectors matches the external labels.
    86  // If true, it also returns an array of non-empty Prometheus matchers.
    87  func selectorsMatchesExternalLabels(selectors [][]*labels.Matcher, externalLabels labels.Labels) (bool, [][]*labels.Matcher) {
    88  	matchedOnce := false
    89  
    90  	var newSelectors [][]*labels.Matcher
    91  	for _, m := range selectors {
    92  		match, m := matchesExternalLabels(m, externalLabels)
    93  
    94  		matchedOnce = matchedOnce || match
    95  		if match && len(m) > 0 {
    96  			newSelectors = append(newSelectors, m)
    97  		}
    98  	}
    99  
   100  	return matchedOnce, newSelectors
   101  }