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 }