github.com/thanos-io/thanos@v0.32.5/pkg/rules/queryable.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package rules
     5  
     6  import (
     7  	"context"
     8  	"math/rand"
     9  	"net/url"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/go-kit/log"
    14  	"github.com/go-kit/log/level"
    15  	"github.com/prometheus/client_golang/prometheus"
    16  	"github.com/prometheus/client_golang/prometheus/promauto"
    17  	"github.com/prometheus/common/model"
    18  	"github.com/prometheus/prometheus/model/labels"
    19  	"github.com/prometheus/prometheus/storage"
    20  
    21  	"github.com/thanos-io/thanos/internal/cortex/querier/series"
    22  	"github.com/thanos-io/thanos/pkg/httpconfig"
    23  	"github.com/thanos-io/thanos/pkg/promclient"
    24  	"github.com/thanos-io/thanos/pkg/store/storepb"
    25  )
    26  
    27  type promClientsQueryable struct {
    28  	httpMethod string
    29  	step       time.Duration
    30  
    31  	logger            log.Logger
    32  	promClients       []*promclient.Client
    33  	queryClients      []*httpconfig.Client
    34  	ignoredLabelNames []string
    35  
    36  	duplicatedQuery prometheus.Counter
    37  }
    38  type promClientsQuerier struct {
    39  	ctx        context.Context
    40  	mint, maxt int64
    41  	step       int64
    42  	httpMethod string
    43  
    44  	logger              log.Logger
    45  	promClients         []*promclient.Client
    46  	queryClients        []*httpconfig.Client
    47  	restoreIgnoreLabels []string
    48  
    49  	// We use a dummy counter here because the duplicated
    50  	// addresses are already tracked by rule evaluation part.
    51  	duplicatedQuery prometheus.Counter
    52  }
    53  
    54  // NewPromClientsQueryable creates a queryable that queries queriers from Prometheus clients.
    55  func NewPromClientsQueryable(logger log.Logger, queryClients []*httpconfig.Client, promClients []*promclient.Client,
    56  	httpMethod string, step time.Duration, ignoredLabelNames []string) *promClientsQueryable {
    57  	return &promClientsQueryable{
    58  		logger:            logger,
    59  		queryClients:      queryClients,
    60  		promClients:       promClients,
    61  		duplicatedQuery:   promauto.With(nil).NewCounter(prometheus.CounterOpts{}),
    62  		httpMethod:        httpMethod,
    63  		step:              step,
    64  		ignoredLabelNames: ignoredLabelNames,
    65  	}
    66  }
    67  
    68  // Querier returns a new Querier for the given time range.
    69  func (q *promClientsQueryable) Querier(ctx context.Context, mint, maxt int64) (storage.Querier, error) {
    70  	return &promClientsQuerier{
    71  		ctx:                 ctx,
    72  		mint:                mint,
    73  		maxt:                maxt,
    74  		step:                int64(q.step / time.Second),
    75  		httpMethod:          q.httpMethod,
    76  		logger:              q.logger,
    77  		queryClients:        q.queryClients,
    78  		promClients:         q.promClients,
    79  		restoreIgnoreLabels: q.ignoredLabelNames,
    80  	}, nil
    81  }
    82  
    83  // Select implements storage.Querier interface.
    84  func (q *promClientsQuerier) Select(_ bool, _ *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet {
    85  	query := storepb.PromMatchersToString(matchers...)
    86  
    87  	for _, i := range rand.Perm(len(q.queryClients)) {
    88  		promClient := q.promClients[i]
    89  		endpoints := RemoveDuplicateQueryEndpoints(q.logger, q.duplicatedQuery, q.queryClients[i].Endpoints())
    90  		for _, i := range rand.Perm(len(endpoints)) {
    91  			m, warns, _, err := promClient.QueryRange(q.ctx, endpoints[i], query, q.mint, q.maxt, q.step, promclient.QueryOptions{
    92  				Deduplicate: true,
    93  				Method:      q.httpMethod,
    94  			})
    95  
    96  			if err != nil {
    97  				level.Error(q.logger).Log("err", err, "query", q)
    98  				continue
    99  			}
   100  			if len(warns) > 0 {
   101  				level.Warn(q.logger).Log("warnings", strings.Join(warns, ", "), "query", q)
   102  			}
   103  			matrix := make([]*model.SampleStream, 0, m.Len())
   104  			for _, metric := range m {
   105  				for _, label := range q.restoreIgnoreLabels {
   106  					delete(metric.Metric, model.LabelName(label))
   107  				}
   108  
   109  				matrix = append(matrix, &model.SampleStream{
   110  					Metric: metric.Metric,
   111  					Values: metric.Values,
   112  				})
   113  			}
   114  
   115  			return series.MatrixToSeriesSet(matrix)
   116  		}
   117  	}
   118  	return storage.NoopSeriesSet()
   119  }
   120  
   121  // LabelValues implements storage.LabelQuerier interface.
   122  func (q *promClientsQuerier) LabelValues(name string, matchers ...*labels.Matcher) ([]string, storage.Warnings, error) {
   123  	return nil, nil, nil
   124  }
   125  
   126  // LabelNames implements storage.LabelQuerier interface.
   127  func (q *promClientsQuerier) LabelNames(matchers ...*labels.Matcher) ([]string, storage.Warnings, error) {
   128  	return nil, nil, nil
   129  }
   130  
   131  // Close implements storage.LabelQuerier interface.
   132  func (q *promClientsQuerier) Close() error {
   133  	return nil
   134  }
   135  
   136  // RemoveDuplicateQueryEndpoints removes duplicate endpoints from the list of urls.
   137  func RemoveDuplicateQueryEndpoints(logger log.Logger, duplicatedQueriers prometheus.Counter, urls []*url.URL) []*url.URL {
   138  	set := make(map[string]struct{})
   139  	deduplicated := make([]*url.URL, 0, len(urls))
   140  	for _, u := range urls {
   141  		if _, ok := set[u.String()]; ok {
   142  			level.Warn(logger).Log("msg", "duplicate query address is provided", "addr", u.String())
   143  			duplicatedQueriers.Inc()
   144  			continue
   145  		}
   146  		deduplicated = append(deduplicated, u)
   147  		set[u.String()] = struct{}{}
   148  	}
   149  	return deduplicated
   150  }