github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/stores/tsdb/index_client.go (about)

     1  package tsdb
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/prometheus/common/model"
     7  	"github.com/prometheus/prometheus/model/labels"
     8  
     9  	"github.com/grafana/loki/pkg/logproto"
    10  	"github.com/grafana/loki/pkg/logql/syntax"
    11  	"github.com/grafana/loki/pkg/querier/astmapper"
    12  	"github.com/grafana/loki/pkg/storage/chunk"
    13  	"github.com/grafana/loki/pkg/storage/stores/index/stats"
    14  	"github.com/grafana/loki/pkg/storage/stores/tsdb/index"
    15  	"github.com/grafana/loki/pkg/util/spanlogger"
    16  )
    17  
    18  // implements stores.Index
    19  type IndexClient struct {
    20  	idx Index
    21  }
    22  
    23  func NewIndexClient(idx Index) *IndexClient {
    24  	return &IndexClient{
    25  		idx: idx,
    26  	}
    27  }
    28  
    29  // TODO(owen-d): This is a hack for compatibility with how the current query-mapping works.
    30  // Historically, Loki will read the index shard factor and the query planner will inject shard
    31  // labels accordingly.
    32  // In the future, we should use dynamic sharding in TSDB to determine the shard factors
    33  // and we may no longer wish to send a shard label inside the queries,
    34  // but rather expose it as part of the stores.Index interface
    35  func cleanMatchers(matchers ...*labels.Matcher) ([]*labels.Matcher, *index.ShardAnnotation, error) {
    36  	// first use withoutNameLabel to make a copy with the name label removed
    37  	matchers = withoutNameLabel(matchers)
    38  	s, shardLabelIndex, err := astmapper.ShardFromMatchers(matchers)
    39  	if err != nil {
    40  		return nil, nil, err
    41  	}
    42  
    43  	var shard *index.ShardAnnotation
    44  	if s != nil {
    45  		matchers = append(matchers[:shardLabelIndex], matchers[shardLabelIndex+1:]...)
    46  		shard = &index.ShardAnnotation{
    47  			Shard: uint32(s.Shard),
    48  			Of:    uint32(s.Of),
    49  		}
    50  
    51  		if err := shard.Validate(); err != nil {
    52  			return nil, nil, err
    53  		}
    54  	}
    55  
    56  	if len(matchers) == 0 {
    57  		// hack to query all data
    58  		matchers = append(matchers, labels.MustNewMatcher(labels.MatchEqual, "", ""))
    59  	}
    60  
    61  	return matchers, shard, err
    62  
    63  }
    64  
    65  // TODO(owen-d): synchronize logproto.ChunkRef and tsdb.ChunkRef so we don't have to convert.
    66  // They share almost the same fields, so we can add the missing `KB` field to the proto and then
    67  // use that within the tsdb package.
    68  func (c *IndexClient) GetChunkRefs(ctx context.Context, userID string, from, through model.Time, matchers ...*labels.Matcher) ([]logproto.ChunkRef, error) {
    69  	log, ctx := spanlogger.New(ctx, "IndexClient.GetChunkRefs")
    70  	defer log.Span.Finish()
    71  
    72  	var kvps []interface{}
    73  	defer func() {
    74  		log.Log(kvps...)
    75  	}()
    76  
    77  	matchers, shard, err := cleanMatchers(matchers...)
    78  	kvps = append(kvps,
    79  		"from", from.Time(),
    80  		"through", through.Time(),
    81  		"matchers", syntax.MatchersString(matchers),
    82  		"shard", shard,
    83  		"cleanMatcherErr", err,
    84  	)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	// TODO(owen-d): use a pool to reduce allocs here
    90  	chks, err := c.idx.GetChunkRefs(ctx, userID, from, through, nil, shard, matchers...)
    91  	kvps = append(kvps,
    92  		"chunks", len(chks),
    93  		"indexErr", err,
    94  	)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	refs := make([]logproto.ChunkRef, 0, len(chks))
   100  	for _, chk := range chks {
   101  		refs = append(refs, logproto.ChunkRef{
   102  			Fingerprint: uint64(chk.Fingerprint),
   103  			UserID:      chk.User,
   104  			From:        chk.Start,
   105  			Through:     chk.End,
   106  			Checksum:    chk.Checksum,
   107  		})
   108  	}
   109  
   110  	return refs, err
   111  }
   112  
   113  func (c *IndexClient) GetSeries(ctx context.Context, userID string, from, through model.Time, matchers ...*labels.Matcher) ([]labels.Labels, error) {
   114  	matchers, shard, err := cleanMatchers(matchers...)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	xs, err := c.idx.Series(ctx, userID, from, through, nil, shard, matchers...)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	res := make([]labels.Labels, 0, len(xs))
   125  	for _, x := range xs {
   126  		res = append(res, x.Labels)
   127  	}
   128  	return res, nil
   129  }
   130  
   131  // tsdb no longer uses the __metric_name__="logs" hack, so we can ignore metric names!
   132  func (c *IndexClient) LabelValuesForMetricName(ctx context.Context, userID string, from, through model.Time, _ string, labelName string, matchers ...*labels.Matcher) ([]string, error) {
   133  	matchers, _, err := cleanMatchers(matchers...)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	return c.idx.LabelValues(ctx, userID, from, through, labelName, matchers...)
   138  }
   139  
   140  // tsdb no longer uses the __metric_name__="logs" hack, so we can ignore metric names!
   141  func (c *IndexClient) LabelNamesForMetricName(ctx context.Context, userID string, from, through model.Time, _ string) ([]string, error) {
   142  	return c.idx.LabelNames(ctx, userID, from, through)
   143  }
   144  
   145  func (c *IndexClient) Stats(ctx context.Context, userID string, from, through model.Time, matchers ...*labels.Matcher) (*stats.Stats, error) {
   146  	matchers, shard, err := cleanMatchers(matchers...)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	blooms := stats.BloomPool.Get()
   152  	defer stats.BloomPool.Put(blooms)
   153  	blooms, err = c.idx.Stats(ctx, userID, from, through, blooms, shard, matchers...)
   154  
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	res := blooms.Stats()
   159  
   160  	return &res, nil
   161  }
   162  
   163  // SetChunkFilterer sets a chunk filter to be used when retrieving chunks.
   164  // This is only used for GetSeries implementation.
   165  // Todo we might want to pass it as a parameter to GetSeries instead.
   166  func (c *IndexClient) SetChunkFilterer(chunkFilter chunk.RequestChunkFilterer) {
   167  	c.idx.SetChunkFilterer(chunkFilter)
   168  }
   169  
   170  // TODO(owen-d): in the future, handle this by preventing passing the __name__="logs" label
   171  // to TSDB indices at all.
   172  func withoutNameLabel(matchers []*labels.Matcher) []*labels.Matcher {
   173  	if len(matchers) == 0 {
   174  		return nil
   175  	}
   176  
   177  	dst := make([]*labels.Matcher, 0, len(matchers)-1)
   178  	for _, m := range matchers {
   179  		if m.Name == labels.MetricName {
   180  			continue
   181  		}
   182  		dst = append(dst, m)
   183  	}
   184  
   185  	return dst
   186  }