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

     1  package querier
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/go-kit/log"
     7  	"github.com/prometheus/prometheus/model/labels"
     8  	"github.com/weaveworks/common/user"
     9  
    10  	"github.com/grafana/dskit/tenant"
    11  
    12  	"github.com/grafana/loki/pkg/iter"
    13  	"github.com/grafana/loki/pkg/loghttp"
    14  	"github.com/grafana/loki/pkg/logproto"
    15  	"github.com/grafana/loki/pkg/logql"
    16  	"github.com/grafana/loki/pkg/logql/syntax"
    17  	"github.com/grafana/loki/pkg/storage/stores/index/stats"
    18  )
    19  
    20  const (
    21  	defaultTenantLabel   = "__tenant_id__"
    22  	retainExistingPrefix = "original_"
    23  )
    24  
    25  // MultiTenantQuerier is able to query across different tenants.
    26  type MultiTenantQuerier struct {
    27  	Querier
    28  }
    29  
    30  // NewMultiTenantQuerier returns a new querier able to query across different tenants.
    31  func NewMultiTenantQuerier(querier Querier, logger log.Logger) *MultiTenantQuerier {
    32  	return &MultiTenantQuerier{
    33  		Querier: querier,
    34  	}
    35  }
    36  
    37  func (q *MultiTenantQuerier) SelectLogs(ctx context.Context, params logql.SelectLogParams) (iter.EntryIterator, error) {
    38  	tenantIDs, err := tenant.TenantIDs(ctx)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	if len(tenantIDs) == 1 {
    44  		return q.Querier.SelectLogs(ctx, params)
    45  	}
    46  
    47  	selector, err := params.LogSelector()
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	matchedTenants, filteredMatchers := filterValuesByMatchers(defaultTenantLabel, tenantIDs, selector.Matchers()...)
    52  	params.Selector = replaceMatchers(selector, filteredMatchers).String()
    53  
    54  	iters := make([]iter.EntryIterator, len(matchedTenants))
    55  	i := 0
    56  	for id := range matchedTenants {
    57  		singleContext := user.InjectOrgID(ctx, id)
    58  		iter, err := q.Querier.SelectLogs(singleContext, params)
    59  		if err != nil {
    60  			return nil, err
    61  		}
    62  
    63  		iters[i] = NewTenantEntryIterator(iter, id)
    64  		i++
    65  	}
    66  	return iter.NewSortEntryIterator(iters, params.Direction), nil
    67  }
    68  
    69  func (q *MultiTenantQuerier) SelectSamples(ctx context.Context, params logql.SelectSampleParams) (iter.SampleIterator, error) {
    70  	tenantIDs, err := tenant.TenantIDs(ctx)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	if len(tenantIDs) == 1 {
    76  		return q.Querier.SelectSamples(ctx, params)
    77  	}
    78  
    79  	matchedTenants, updatedSelector, err := removeTenantSelector(params, tenantIDs)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	params.Selector = updatedSelector.String()
    84  
    85  	iters := make([]iter.SampleIterator, len(matchedTenants))
    86  	i := 0
    87  	for id := range matchedTenants {
    88  		singleContext := user.InjectOrgID(ctx, id)
    89  		iter, err := q.Querier.SelectSamples(singleContext, params)
    90  		if err != nil {
    91  			return nil, err
    92  		}
    93  
    94  		iters[i] = NewTenantSampleIterator(iter, id)
    95  		i++
    96  	}
    97  	return iter.NewSortSampleIterator(iters), nil
    98  }
    99  
   100  func (q *MultiTenantQuerier) Label(ctx context.Context, req *logproto.LabelRequest) (*logproto.LabelResponse, error) {
   101  	tenantIDs, err := tenant.TenantIDs(ctx)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	if req.Values && req.Name == defaultTenantLabel {
   107  		return &logproto.LabelResponse{Values: tenantIDs}, nil
   108  	}
   109  
   110  	if len(tenantIDs) == 1 {
   111  		return q.Querier.Label(ctx, req)
   112  	}
   113  
   114  	responses := make([]*logproto.LabelResponse, len(tenantIDs))
   115  	for i, id := range tenantIDs {
   116  		singleContext := user.InjectOrgID(ctx, id)
   117  		resp, err := q.Querier.Label(singleContext, req)
   118  		if err != nil {
   119  			return nil, err
   120  		}
   121  
   122  		responses[i] = resp
   123  	}
   124  
   125  	// Append tenant ID label name if label names are requested.
   126  	if !req.Values {
   127  		responses = append(responses, &logproto.LabelResponse{Values: []string{defaultTenantLabel}})
   128  	}
   129  
   130  	return logproto.MergeLabelResponses(responses)
   131  }
   132  
   133  func (q *MultiTenantQuerier) Series(ctx context.Context, req *logproto.SeriesRequest) (*logproto.SeriesResponse, error) {
   134  	tenantIDs, err := tenant.TenantIDs(ctx)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	if len(tenantIDs) == 1 {
   140  		return q.Querier.Series(ctx, req)
   141  	}
   142  
   143  	responses := make([]*logproto.SeriesResponse, len(tenantIDs))
   144  	for i, id := range tenantIDs {
   145  		singleContext := user.InjectOrgID(ctx, id)
   146  		resp, err := q.Querier.Series(singleContext, req)
   147  		if err != nil {
   148  			return nil, err
   149  		}
   150  
   151  		for _, s := range resp.GetSeries() {
   152  			if _, ok := s.Labels[defaultTenantLabel]; !ok {
   153  				s.Labels[defaultTenantLabel] = id
   154  			}
   155  		}
   156  
   157  		responses[i] = resp
   158  	}
   159  
   160  	return logproto.MergeSeriesResponses(responses)
   161  }
   162  
   163  func (q *MultiTenantQuerier) IndexStats(ctx context.Context, req *loghttp.RangeQuery) (*stats.Stats, error) {
   164  	tenantIDs, err := tenant.TenantIDs(ctx)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	if len(tenantIDs) == 1 {
   170  		return q.Querier.IndexStats(ctx, req)
   171  	}
   172  
   173  	responses := make([]*stats.Stats, len(tenantIDs))
   174  	for i, id := range tenantIDs {
   175  		singleContext := user.InjectOrgID(ctx, id)
   176  		resp, err := q.Querier.IndexStats(singleContext, req)
   177  		if err != nil {
   178  			return nil, err
   179  		}
   180  
   181  		responses[i] = resp
   182  	}
   183  
   184  	merged := stats.MergeStats(responses...)
   185  
   186  	return &merged, nil
   187  }
   188  
   189  // removeTenantSelector filters the given tenant IDs based on any tenant ID filter the in passed selector.
   190  func removeTenantSelector(params logql.SelectSampleParams, tenantIDs []string) (map[string]struct{}, syntax.Expr, error) {
   191  	expr, err := params.Expr()
   192  	if err != nil {
   193  		return nil, nil, err
   194  	}
   195  	matchedTenants, filteredMatchers := filterValuesByMatchers(defaultTenantLabel, tenantIDs, expr.Selector().Matchers()...)
   196  	updatedExpr := replaceMatchers(expr, filteredMatchers)
   197  	return matchedTenants, updatedExpr, nil
   198  }
   199  
   200  // replaceMatchers traverses the passed expression and replaces all matchers.
   201  func replaceMatchers(expr syntax.Expr, matchers []*labels.Matcher) syntax.Expr {
   202  	expr, _ = syntax.Clone(expr)
   203  	expr.Walk(func(e interface{}) {
   204  		switch concrete := e.(type) {
   205  		case *syntax.MatchersExpr:
   206  			concrete.Mts = matchers
   207  		}
   208  	})
   209  	return expr
   210  }
   211  
   212  // See https://github.com/grafana/mimir/blob/114ab88b50638a2047e2ca2a60640f6ca6fe8c17/pkg/querier/tenantfederation/tenant_federation.go#L29-L69
   213  // filterValuesByMatchers applies matchers to inputed `idLabelName` and
   214  // `ids`. A set of matched IDs is returned and also all label matchers not
   215  // targeting the `idLabelName` label.
   216  //
   217  // In case a label matcher is set on a label conflicting with `idLabelName`, we
   218  // need to rename this labelMatcher's name to its original name. This is used
   219  // to as part of Select in the mergeQueryable, to ensure only relevant queries
   220  // are considered and the forwarded matchers do not contain matchers on the
   221  // `idLabelName`.
   222  func filterValuesByMatchers(idLabelName string, ids []string, matchers ...*labels.Matcher) (matchedIDs map[string]struct{}, unrelatedMatchers []*labels.Matcher) {
   223  	// this contains the matchers which are not related to idLabelName
   224  	unrelatedMatchers = make([]*labels.Matcher, 0, len(matchers))
   225  
   226  	// build map of values to consider for the matchers
   227  	matchedIDs = sliceToSet(ids)
   228  
   229  	for _, m := range matchers {
   230  		switch m.Name {
   231  		// matcher has idLabelName to target a specific tenant(s)
   232  		case idLabelName:
   233  			for value := range matchedIDs {
   234  				if !m.Matches(value) {
   235  					delete(matchedIDs, value)
   236  				}
   237  			}
   238  
   239  		// check if has the retained label name
   240  		case retainExistingPrefix + idLabelName:
   241  			// rewrite label to the original name, by copying matcher and
   242  			// replacing the label name
   243  			rewrittenM := *m
   244  			rewrittenM.Name = idLabelName
   245  			unrelatedMatchers = append(unrelatedMatchers, &rewrittenM)
   246  
   247  		default:
   248  			unrelatedMatchers = append(unrelatedMatchers, m)
   249  		}
   250  	}
   251  
   252  	return matchedIDs, unrelatedMatchers
   253  }
   254  
   255  func sliceToSet(values []string) map[string]struct{} {
   256  	out := make(map[string]struct{}, len(values))
   257  	for _, v := range values {
   258  		out[v] = struct{}{}
   259  	}
   260  	return out
   261  }
   262  
   263  type relabel struct {
   264  	tenantID string
   265  	cache    map[string]labels.Labels
   266  }
   267  
   268  func (r relabel) relabel(original string) string {
   269  	lbls, ok := r.cache[original]
   270  	if ok {
   271  		return lbls.String()
   272  	}
   273  
   274  	lbls, _ = syntax.ParseLabels(original)
   275  	builder := labels.NewBuilder(lbls).Del(defaultTenantLabel)
   276  
   277  	// Prefix label if it conflicts with the tenant label.
   278  	if lbls.Has(defaultTenantLabel) {
   279  		builder.Set(retainExistingPrefix+defaultTenantLabel, lbls.Get(defaultTenantLabel))
   280  	}
   281  	builder.Set(defaultTenantLabel, r.tenantID)
   282  
   283  	lbls = builder.Labels()
   284  	r.cache[original] = lbls
   285  	return lbls.String()
   286  }
   287  
   288  // TenantEntry Iterator wraps an entry iterator and adds the tenant label.
   289  type TenantEntryIterator struct {
   290  	iter.EntryIterator
   291  	relabel
   292  }
   293  
   294  func NewTenantEntryIterator(iter iter.EntryIterator, id string) *TenantEntryIterator {
   295  	return &TenantEntryIterator{
   296  		EntryIterator: iter,
   297  		relabel: relabel{
   298  			tenantID: id,
   299  			cache:    map[string]labels.Labels{},
   300  		},
   301  	}
   302  }
   303  
   304  func (i *TenantEntryIterator) Labels() string {
   305  	return i.relabel.relabel(i.EntryIterator.Labels())
   306  }
   307  
   308  // TenantEntry Iterator wraps a sample iterator and adds the tenant label.
   309  type TenantSampleIterator struct {
   310  	iter.SampleIterator
   311  	relabel
   312  }
   313  
   314  func NewTenantSampleIterator(iter iter.SampleIterator, id string) *TenantSampleIterator {
   315  	return &TenantSampleIterator{
   316  		SampleIterator: iter,
   317  		relabel: relabel{
   318  			tenantID: id,
   319  			cache:    map[string]labels.Labels{},
   320  		},
   321  	}
   322  
   323  }
   324  
   325  func (i *TenantSampleIterator) Labels() string {
   326  	return i.relabel.relabel(i.SampleIterator.Labels())
   327  }