github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/stores/tsdb/multitenant.go (about) 1 package tsdb 2 3 import ( 4 "context" 5 "sort" 6 7 "github.com/prometheus/common/model" 8 "github.com/prometheus/prometheus/model/labels" 9 10 "github.com/grafana/loki/pkg/storage/chunk" 11 "github.com/grafana/loki/pkg/storage/stores/index/stats" 12 "github.com/grafana/loki/pkg/storage/stores/tsdb/index" 13 ) 14 15 // TenantLabel is part of the reserved label namespace (__ prefix) 16 // It's used to create multi-tenant TSDBs (which do not have a tenancy concept) 17 // These labels are stripped out during compaction to single-tenant TSDBs 18 const TenantLabel = "__loki_tenant__" 19 20 // MultiTenantIndex will inject a tenant label to it's queries 21 // This works with pre-compacted TSDBs which aren't yet per tenant. 22 type MultiTenantIndex struct { 23 idx Index 24 } 25 26 func NewMultiTenantIndex(idx Index) *MultiTenantIndex { 27 return &MultiTenantIndex{idx: idx} 28 } 29 30 func withTenantLabelMatcher(userID string, matchers []*labels.Matcher) []*labels.Matcher { 31 cpy := make([]*labels.Matcher, len(matchers)+1) 32 cpy[0] = labels.MustNewMatcher(labels.MatchEqual, TenantLabel, userID) 33 copy(cpy[1:], matchers) 34 return cpy 35 } 36 37 func withoutTenantLabel(ls labels.Labels) labels.Labels { 38 for i, l := range ls { 39 if l.Name == TenantLabel { 40 ls = append(ls[:i], ls[i+1:]...) 41 break 42 } 43 } 44 return ls 45 } 46 47 func (m *MultiTenantIndex) Bounds() (model.Time, model.Time) { return m.idx.Bounds() } 48 49 func (m *MultiTenantIndex) SetChunkFilterer(chunkFilter chunk.RequestChunkFilterer) { 50 m.idx.SetChunkFilterer(chunkFilter) 51 } 52 53 func (m *MultiTenantIndex) Close() error { return m.idx.Close() } 54 55 func (m *MultiTenantIndex) GetChunkRefs(ctx context.Context, userID string, from, through model.Time, res []ChunkRef, shard *index.ShardAnnotation, matchers ...*labels.Matcher) ([]ChunkRef, error) { 56 return m.idx.GetChunkRefs(ctx, userID, from, through, res, shard, withTenantLabelMatcher(userID, matchers)...) 57 } 58 59 func (m *MultiTenantIndex) Series(ctx context.Context, userID string, from, through model.Time, res []Series, shard *index.ShardAnnotation, matchers ...*labels.Matcher) ([]Series, error) { 60 xs, err := m.idx.Series(ctx, userID, from, through, res, shard, withTenantLabelMatcher(userID, matchers)...) 61 if err != nil { 62 return nil, err 63 } 64 for i := range xs { 65 xs[i].Labels = withoutTenantLabel(xs[i].Labels) 66 } 67 return xs, nil 68 } 69 70 func (m *MultiTenantIndex) LabelNames(ctx context.Context, userID string, from, through model.Time, matchers ...*labels.Matcher) ([]string, error) { 71 res, err := m.idx.LabelNames(ctx, userID, from, through, withTenantLabelMatcher(userID, matchers)...) 72 if err != nil { 73 return nil, err 74 } 75 76 // Strip out the tenant label in response. 77 i := sort.SearchStrings(res, TenantLabel) 78 if i == len(res) || res[i] != TenantLabel { 79 return res, nil 80 } 81 82 return append(res[:i], res[i+1:]...), nil 83 } 84 85 func (m *MultiTenantIndex) LabelValues(ctx context.Context, userID string, from, through model.Time, name string, matchers ...*labels.Matcher) ([]string, error) { 86 // Prevent queries for the internal tenant label 87 if name == TenantLabel { 88 return nil, nil 89 } 90 return m.idx.LabelValues(ctx, userID, from, through, name, withTenantLabelMatcher(userID, matchers)...) 91 } 92 93 func (m *MultiTenantIndex) Stats(ctx context.Context, userID string, from, through model.Time, blooms *stats.Blooms, shard *index.ShardAnnotation, matchers ...*labels.Matcher) (*stats.Blooms, error) { 94 return m.idx.Stats(ctx, userID, from, through, blooms, shard, withTenantLabelMatcher(userID, matchers)...) 95 }