github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/querier/tenantfederation/merge_queryable.go (about)

     1  package tenantfederation
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sort"
     7  	"strings"
     8  
     9  	"github.com/grafana/dskit/concurrency"
    10  	"github.com/pkg/errors"
    11  	"github.com/prometheus/prometheus/pkg/labels"
    12  	"github.com/prometheus/prometheus/storage"
    13  	"github.com/prometheus/prometheus/tsdb/chunkenc"
    14  	tsdb_errors "github.com/prometheus/prometheus/tsdb/errors"
    15  	"github.com/weaveworks/common/user"
    16  
    17  	"github.com/cortexproject/cortex/pkg/tenant"
    18  	"github.com/cortexproject/cortex/pkg/util/spanlogger"
    19  )
    20  
    21  const (
    22  	defaultTenantLabel   = "__tenant_id__"
    23  	retainExistingPrefix = "original_"
    24  	maxConcurrency       = 16
    25  )
    26  
    27  // NewQueryable returns a queryable that iterates through all the tenant IDs
    28  // that are part of the request and aggregates the results from each tenant's
    29  // Querier by sending of subsequent requests.
    30  // By setting byPassWithSingleQuerier to true the mergeQuerier gets by-passed
    31  // and results for request with a single querier will not contain the
    32  // "__tenant_id__" label. This allows a smoother transition, when enabling
    33  // tenant federation in a cluster.
    34  // The result contains a label "__tenant_id__" to identify the tenant ID that
    35  // it originally resulted from.
    36  // If the label "__tenant_id__" is already existing, its value is overwritten
    37  // by the tenant ID and the previous value is exposed through a new label
    38  // prefixed with "original_". This behaviour is not implemented recursively.
    39  func NewQueryable(upstream storage.Queryable, byPassWithSingleQuerier bool) storage.Queryable {
    40  	return NewMergeQueryable(defaultTenantLabel, tenantQuerierCallback(upstream), byPassWithSingleQuerier)
    41  }
    42  
    43  func tenantQuerierCallback(queryable storage.Queryable) MergeQuerierCallback {
    44  	return func(ctx context.Context, mint int64, maxt int64) ([]string, []storage.Querier, error) {
    45  		tenantIDs, err := tenant.TenantIDs(ctx)
    46  		if err != nil {
    47  			return nil, nil, err
    48  		}
    49  
    50  		var queriers = make([]storage.Querier, len(tenantIDs))
    51  		for pos, tenantID := range tenantIDs {
    52  			q, err := queryable.Querier(
    53  				user.InjectOrgID(ctx, tenantID),
    54  				mint,
    55  				maxt,
    56  			)
    57  			if err != nil {
    58  				return nil, nil, err
    59  			}
    60  			queriers[pos] = q
    61  		}
    62  
    63  		return tenantIDs, queriers, nil
    64  	}
    65  }
    66  
    67  // MergeQuerierCallback returns the underlying queriers and their IDs relevant
    68  // for the query.
    69  type MergeQuerierCallback func(ctx context.Context, mint int64, maxt int64) (ids []string, queriers []storage.Querier, err error)
    70  
    71  // NewMergeQueryable returns a queryable that merges results from multiple
    72  // underlying Queryables. The underlying queryables and its label values to be
    73  // considered are returned by a MergeQuerierCallback.
    74  // By setting byPassWithSingleQuerier to true the mergeQuerier gets by-passed
    75  // and results for request with a single querier will not contain the id label.
    76  // This allows a smoother transition, when enabling tenant federation in a
    77  // cluster.
    78  // Results contain a label `idLabelName` to identify the underlying queryable
    79  // that it originally resulted from.
    80  // If the label `idLabelName` is already existing, its value is overwritten and
    81  // the previous value is exposed through a new label prefixed with "original_".
    82  // This behaviour is not implemented recursively.
    83  func NewMergeQueryable(idLabelName string, callback MergeQuerierCallback, byPassWithSingleQuerier bool) storage.Queryable {
    84  	return &mergeQueryable{
    85  		idLabelName:             idLabelName,
    86  		callback:                callback,
    87  		byPassWithSingleQuerier: byPassWithSingleQuerier,
    88  	}
    89  }
    90  
    91  type mergeQueryable struct {
    92  	idLabelName             string
    93  	byPassWithSingleQuerier bool
    94  	callback                MergeQuerierCallback
    95  }
    96  
    97  // Querier returns a new mergeQuerier, which aggregates results from multiple
    98  // underlying queriers into a single result.
    99  func (m *mergeQueryable) Querier(ctx context.Context, mint int64, maxt int64) (storage.Querier, error) {
   100  	// TODO: it's necessary to think how to override context inside querier
   101  	//  to mark spans created inside querier as child of a span created inside
   102  	//  methods of merged querier.
   103  	ids, queriers, err := m.callback(ctx, mint, maxt)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	// by pass when only single querier is returned
   109  	if m.byPassWithSingleQuerier && len(queriers) == 1 {
   110  		return queriers[0], nil
   111  	}
   112  
   113  	return &mergeQuerier{
   114  		ctx:         ctx,
   115  		idLabelName: m.idLabelName,
   116  		queriers:    queriers,
   117  		ids:         ids,
   118  	}, nil
   119  }
   120  
   121  // mergeQuerier aggregates the results from underlying queriers and adds a
   122  // label `idLabelName` to identify the queryable that the metric resulted
   123  // from.
   124  // If the label `idLabelName` is already existing, its value is overwritten and
   125  // the previous value is exposed through a new label prefixed with "original_".
   126  // This behaviour is not implemented recursively
   127  type mergeQuerier struct {
   128  	ctx         context.Context
   129  	queriers    []storage.Querier
   130  	idLabelName string
   131  	ids         []string
   132  }
   133  
   134  // LabelValues returns all potential values for a label name.  It is not safe
   135  // to use the strings beyond the lifefime of the querier.
   136  // For the label `idLabelName` it will return all the underlying ids available.
   137  // For the label "original_" + `idLabelName it will return all the values
   138  // of the underlying queriers for `idLabelName`.
   139  func (m *mergeQuerier) LabelValues(name string, matchers ...*labels.Matcher) ([]string, storage.Warnings, error) {
   140  	log, _ := spanlogger.New(m.ctx, "mergeQuerier.LabelValues")
   141  	defer log.Span.Finish()
   142  
   143  	matchedTenants, filteredMatchers := filterValuesByMatchers(m.idLabelName, m.ids, matchers...)
   144  
   145  	if name == m.idLabelName {
   146  		var labelValues = make([]string, 0, len(matchedTenants))
   147  		for _, id := range m.ids {
   148  			if _, matched := matchedTenants[id]; matched {
   149  				labelValues = append(labelValues, id)
   150  			}
   151  		}
   152  		return labelValues, nil, nil
   153  	}
   154  
   155  	// ensure the name of a retained label gets handled under the original
   156  	// label name
   157  	if name == retainExistingPrefix+m.idLabelName {
   158  		name = m.idLabelName
   159  	}
   160  
   161  	return m.mergeDistinctStringSliceWithTenants(func(ctx context.Context, q storage.Querier) ([]string, storage.Warnings, error) {
   162  		return q.LabelValues(name, filteredMatchers...)
   163  	}, matchedTenants)
   164  }
   165  
   166  // LabelNames returns all the unique label names present in the underlying
   167  // queriers. It also adds the `idLabelName` and if present in the original
   168  // results the original `idLabelName`.
   169  func (m *mergeQuerier) LabelNames(matchers ...*labels.Matcher) ([]string, storage.Warnings, error) {
   170  	log, _ := spanlogger.New(m.ctx, "mergeQuerier.LabelNames")
   171  	defer log.Span.Finish()
   172  
   173  	matchedTenants, filteredMatchers := filterValuesByMatchers(m.idLabelName, m.ids, matchers...)
   174  
   175  	labelNames, warnings, err := m.mergeDistinctStringSliceWithTenants(func(ctx context.Context, q storage.Querier) ([]string, storage.Warnings, error) {
   176  		return q.LabelNames(filteredMatchers...)
   177  	}, matchedTenants)
   178  	if err != nil {
   179  		return nil, nil, err
   180  	}
   181  
   182  	// check if the `idLabelName` exists in the original result
   183  	var idLabelNameExists bool
   184  	labelPos := sort.SearchStrings(labelNames, m.idLabelName)
   185  	if labelPos < len(labelNames) && labelNames[labelPos] == m.idLabelName {
   186  		idLabelNameExists = true
   187  	}
   188  
   189  	labelToAdd := m.idLabelName
   190  
   191  	// if `idLabelName` already exists, we need to add the name prefix with
   192  	// retainExistingPrefix.
   193  	if idLabelNameExists {
   194  		labelToAdd = retainExistingPrefix + m.idLabelName
   195  		labelPos = sort.SearchStrings(labelNames, labelToAdd)
   196  	}
   197  
   198  	// insert label at the correct position
   199  	labelNames = append(labelNames, "")
   200  	copy(labelNames[labelPos+1:], labelNames[labelPos:])
   201  	labelNames[labelPos] = labelToAdd
   202  
   203  	return labelNames, warnings, nil
   204  }
   205  
   206  type stringSliceFunc func(context.Context, storage.Querier) ([]string, storage.Warnings, error)
   207  
   208  type stringSliceFuncJob struct {
   209  	querier  storage.Querier
   210  	id       string
   211  	result   []string
   212  	warnings storage.Warnings
   213  }
   214  
   215  // mergeDistinctStringSliceWithTenants aggregates stringSliceFunc call
   216  // results from queriers whose tenant ids match the tenants map. If a nil map is
   217  // provided, all queriers are used. It removes duplicates and sorts the result.
   218  // It doesn't require the output of the stringSliceFunc to be sorted, as results
   219  // of LabelValues are not sorted.
   220  func (m *mergeQuerier) mergeDistinctStringSliceWithTenants(f stringSliceFunc, tenants map[string]struct{}) ([]string, storage.Warnings, error) {
   221  	var jobs []interface{}
   222  
   223  	for pos, id := range m.ids {
   224  		if tenants != nil {
   225  			if _, matched := tenants[id]; !matched {
   226  				continue
   227  			}
   228  		}
   229  
   230  		jobs = append(jobs, &stringSliceFuncJob{
   231  			querier: m.queriers[pos],
   232  			id:      m.ids[pos],
   233  		})
   234  	}
   235  
   236  	run := func(ctx context.Context, jobIntf interface{}) error {
   237  		job, ok := jobIntf.(*stringSliceFuncJob)
   238  		if !ok {
   239  			return fmt.Errorf("unexpected type %T", jobIntf)
   240  		}
   241  
   242  		var err error
   243  		job.result, job.warnings, err = f(ctx, job.querier)
   244  		if err != nil {
   245  			return errors.Wrapf(err, "error querying %s %s", rewriteLabelName(m.idLabelName), job.id)
   246  		}
   247  
   248  		return nil
   249  	}
   250  
   251  	err := concurrency.ForEach(m.ctx, jobs, maxConcurrency, run)
   252  	if err != nil {
   253  		return nil, nil, err
   254  	}
   255  
   256  	// aggregate warnings and deduplicate string results
   257  	var warnings storage.Warnings
   258  	resultMap := make(map[string]struct{})
   259  	for _, jobIntf := range jobs {
   260  		job, ok := jobIntf.(*stringSliceFuncJob)
   261  		if !ok {
   262  			return nil, nil, fmt.Errorf("unexpected type %T", jobIntf)
   263  		}
   264  
   265  		for _, e := range job.result {
   266  			resultMap[e] = struct{}{}
   267  		}
   268  
   269  		for _, w := range job.warnings {
   270  			warnings = append(warnings, errors.Wrapf(w, "warning querying %s %s", rewriteLabelName(m.idLabelName), job.id))
   271  		}
   272  	}
   273  
   274  	var result = make([]string, 0, len(resultMap))
   275  	for e := range resultMap {
   276  		result = append(result, e)
   277  	}
   278  	sort.Strings(result)
   279  	return result, warnings, nil
   280  }
   281  
   282  // Close releases the resources of the Querier.
   283  func (m *mergeQuerier) Close() error {
   284  	errs := tsdb_errors.NewMulti()
   285  	for pos, id := range m.ids {
   286  		errs.Add(errors.Wrapf(m.queriers[pos].Close(), "failed to close querier for %s %s", rewriteLabelName(m.idLabelName), id))
   287  	}
   288  	return errs.Err()
   289  }
   290  
   291  type selectJob struct {
   292  	pos     int
   293  	querier storage.Querier
   294  	id      string
   295  }
   296  
   297  // Select returns a set of series that matches the given label matchers. If the
   298  // `idLabelName` is matched on, it only considers those queriers
   299  // matching. The forwarded labelSelector is not containing those that operate
   300  // on `idLabelName`.
   301  func (m *mergeQuerier) Select(sortSeries bool, hints *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet {
   302  	log, ctx := spanlogger.New(m.ctx, "mergeQuerier.Select")
   303  	defer log.Span.Finish()
   304  	matchedValues, filteredMatchers := filterValuesByMatchers(m.idLabelName, m.ids, matchers...)
   305  	var jobs = make([]interface{}, len(matchedValues))
   306  	var seriesSets = make([]storage.SeriesSet, len(matchedValues))
   307  	var jobPos int
   308  	for labelPos := range m.ids {
   309  		if _, matched := matchedValues[m.ids[labelPos]]; !matched {
   310  			continue
   311  		}
   312  		jobs[jobPos] = &selectJob{
   313  			pos:     jobPos,
   314  			querier: m.queriers[labelPos],
   315  			id:      m.ids[labelPos],
   316  		}
   317  		jobPos++
   318  	}
   319  
   320  	run := func(ctx context.Context, jobIntf interface{}) error {
   321  		job, ok := jobIntf.(*selectJob)
   322  		if !ok {
   323  			return fmt.Errorf("unexpected type %T", jobIntf)
   324  		}
   325  		seriesSets[job.pos] = &addLabelsSeriesSet{
   326  			upstream: job.querier.Select(sortSeries, hints, filteredMatchers...),
   327  			labels: labels.Labels{
   328  				{
   329  					Name:  m.idLabelName,
   330  					Value: job.id,
   331  				},
   332  			},
   333  		}
   334  		return nil
   335  	}
   336  
   337  	err := concurrency.ForEach(ctx, jobs, maxConcurrency, run)
   338  	if err != nil {
   339  		return storage.ErrSeriesSet(err)
   340  	}
   341  
   342  	return storage.NewMergeSeriesSet(seriesSets, storage.ChainedSeriesMerge)
   343  }
   344  
   345  // filterValuesByMatchers applies matchers to inputed `idLabelName` and
   346  // `ids`. A map of matched values is returned and also all label matchers not
   347  // matching the `idLabelName`.
   348  // In case a label matcher is set on a label conflicting with `idLabelName`, we
   349  // need to rename this labelMatcher's name to its original name. This is used
   350  // to as part of Select in the mergeQueryable, to ensure only relevant queries
   351  // are considered and the forwarded matchers do not contain matchers on the
   352  // `idLabelName`.
   353  func filterValuesByMatchers(idLabelName string, ids []string, matchers ...*labels.Matcher) (matchedIDs map[string]struct{}, unrelatedMatchers []*labels.Matcher) {
   354  	// this contains the matchers which are not related to idLabelName
   355  	unrelatedMatchers = make([]*labels.Matcher, 0, len(matchers))
   356  
   357  	// build map of values to consider for the matchers
   358  	matchedIDs = make(map[string]struct{}, len(ids))
   359  	for _, value := range ids {
   360  		matchedIDs[value] = struct{}{}
   361  	}
   362  
   363  	for _, m := range matchers {
   364  		switch m.Name {
   365  		// matcher has idLabelName to target a specific tenant(s)
   366  		case idLabelName:
   367  			for value := range matchedIDs {
   368  				if !m.Matches(value) {
   369  					delete(matchedIDs, value)
   370  				}
   371  			}
   372  
   373  		// check if has the retained label name
   374  		case retainExistingPrefix + idLabelName:
   375  			// rewrite label to the original name, by copying matcher and
   376  			// replacing the label name
   377  			rewrittenM := *m
   378  			rewrittenM.Name = idLabelName
   379  			unrelatedMatchers = append(unrelatedMatchers, &rewrittenM)
   380  
   381  		default:
   382  			unrelatedMatchers = append(unrelatedMatchers, m)
   383  		}
   384  	}
   385  
   386  	return matchedIDs, unrelatedMatchers
   387  }
   388  
   389  type addLabelsSeriesSet struct {
   390  	upstream   storage.SeriesSet
   391  	labels     labels.Labels
   392  	currSeries storage.Series
   393  }
   394  
   395  func (m *addLabelsSeriesSet) Next() bool {
   396  	m.currSeries = nil
   397  	return m.upstream.Next()
   398  }
   399  
   400  // At returns full series. Returned series should be iteratable even after Next is called.
   401  func (m *addLabelsSeriesSet) At() storage.Series {
   402  	if m.currSeries == nil {
   403  		upstream := m.upstream.At()
   404  		m.currSeries = &addLabelsSeries{
   405  			upstream: upstream,
   406  			labels:   setLabelsRetainExisting(upstream.Labels(), m.labels...),
   407  		}
   408  	}
   409  	return m.currSeries
   410  }
   411  
   412  // The error that iteration as failed with.
   413  // When an error occurs, set cannot continue to iterate.
   414  func (m *addLabelsSeriesSet) Err() error {
   415  	return errors.Wrapf(m.upstream.Err(), "error querying %s", labelsToString(m.labels))
   416  }
   417  
   418  // A collection of warnings for the whole set.
   419  // Warnings could be return even iteration has not failed with error.
   420  func (m *addLabelsSeriesSet) Warnings() storage.Warnings {
   421  	upstream := m.upstream.Warnings()
   422  	warnings := make(storage.Warnings, len(upstream))
   423  	for pos := range upstream {
   424  		warnings[pos] = errors.Wrapf(upstream[pos], "warning querying %s", labelsToString(m.labels))
   425  	}
   426  	return warnings
   427  }
   428  
   429  // rewrite label name to be more readable in error output
   430  func rewriteLabelName(s string) string {
   431  	return strings.TrimRight(strings.TrimLeft(s, "_"), "_")
   432  }
   433  
   434  // this outputs a more readable error format
   435  func labelsToString(labels labels.Labels) string {
   436  	parts := make([]string, len(labels))
   437  	for pos, l := range labels {
   438  		parts[pos] = rewriteLabelName(l.Name) + " " + l.Value
   439  	}
   440  	return strings.Join(parts, ", ")
   441  }
   442  
   443  type addLabelsSeries struct {
   444  	upstream storage.Series
   445  	labels   labels.Labels
   446  }
   447  
   448  // Labels returns the complete set of labels. For series it means all labels identifying the series.
   449  func (a *addLabelsSeries) Labels() labels.Labels {
   450  	return a.labels
   451  }
   452  
   453  // Iterator returns a new, independent iterator of the data of the series.
   454  func (a *addLabelsSeries) Iterator() chunkenc.Iterator {
   455  	return a.upstream.Iterator()
   456  }
   457  
   458  // this sets a label and preserves an existing value a new label prefixed with
   459  // original_. It doesn't do this recursively.
   460  func setLabelsRetainExisting(src labels.Labels, additionalLabels ...labels.Label) labels.Labels {
   461  	lb := labels.NewBuilder(src)
   462  
   463  	for _, additionalL := range additionalLabels {
   464  		if oldValue := src.Get(additionalL.Name); oldValue != "" {
   465  			lb.Set(
   466  				retainExistingPrefix+additionalL.Name,
   467  				oldValue,
   468  			)
   469  		}
   470  		lb.Set(additionalL.Name, additionalL.Value)
   471  	}
   472  
   473  	return lb.Labels()
   474  }