github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/stores/tsdb/multi_file_index.go (about) 1 package tsdb 2 3 import ( 4 "context" 5 "errors" 6 7 "github.com/grafana/dskit/multierror" 8 "github.com/prometheus/common/model" 9 "github.com/prometheus/prometheus/model/labels" 10 "golang.org/x/sync/errgroup" 11 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 ) 16 17 type MultiIndex struct { 18 indices []Index 19 } 20 21 func NewMultiIndex(indices ...Index) (*MultiIndex, error) { 22 if len(indices) == 0 { 23 return nil, errors.New("must supply at least one index") 24 } 25 26 return &MultiIndex{indices: indices}, nil 27 } 28 29 func (i *MultiIndex) Bounds() (model.Time, model.Time) { 30 var lowest, highest model.Time 31 for _, idx := range i.indices { 32 from, through := idx.Bounds() 33 if lowest == 0 || from < lowest { 34 lowest = from 35 } 36 37 if highest == 0 || through > highest { 38 highest = through 39 } 40 } 41 42 return lowest, highest 43 } 44 45 func (i *MultiIndex) SetChunkFilterer(chunkFilter chunk.RequestChunkFilterer) { 46 for _, x := range i.indices { 47 x.SetChunkFilterer(chunkFilter) 48 } 49 } 50 51 func (i *MultiIndex) Close() error { 52 var errs multierror.MultiError 53 for _, idx := range i.indices { 54 if err := idx.Close(); err != nil { 55 errs = append(errs, err) 56 } 57 } 58 return errs.Err() 59 60 } 61 62 func (i *MultiIndex) forIndices(ctx context.Context, from, through model.Time, fn func(context.Context, Index) (interface{}, error)) ([]interface{}, error) { 63 queryBounds := newBounds(from, through) 64 g, ctx := errgroup.WithContext(ctx) 65 66 // ensure we prebuffer the channel by the maximum possible 67 // return length so our goroutines don't block 68 ch := make(chan interface{}, len(i.indices)) 69 70 for _, idx := range i.indices { 71 // ignore indices which can't match this query 72 if Overlap(queryBounds, idx) { 73 // run all queries in linked goroutines (cancel after first err), 74 // bounded by parallelism controls if applicable. 75 76 // must wrap g.Go in anonymous function to capture 77 // idx variable during iteration 78 func(idx Index) { 79 g.Go(func() error { 80 got, err := fn(ctx, idx) 81 if err != nil { 82 return err 83 } 84 ch <- got 85 return nil 86 }) 87 }(idx) 88 89 } 90 } 91 92 // wait for work to finish or error|ctx expiration 93 if err := g.Wait(); err != nil { 94 return nil, err 95 } 96 close(ch) 97 98 results := make([]interface{}, 0, len(i.indices)) 99 for x := range ch { 100 results = append(results, x) 101 } 102 return results, nil 103 } 104 105 func (i *MultiIndex) GetChunkRefs(ctx context.Context, userID string, from, through model.Time, res []ChunkRef, shard *index.ShardAnnotation, matchers ...*labels.Matcher) ([]ChunkRef, error) { 106 if res == nil { 107 res = ChunkRefsPool.Get() 108 } 109 res = res[:0] 110 111 groups, err := i.forIndices(ctx, from, through, func(ctx context.Context, idx Index) (interface{}, error) { 112 return idx.GetChunkRefs(ctx, userID, from, through, nil, shard, matchers...) 113 }) 114 if err != nil { 115 return nil, err 116 } 117 118 // keep track of duplicates 119 seen := make(map[ChunkRef]struct{}) 120 121 // TODO(owen-d): Do this more efficiently, 122 // not all indices overlap each other 123 for _, group := range groups { 124 g := group.([]ChunkRef) 125 for _, ref := range g { 126 _, ok := seen[ref] 127 if ok { 128 continue 129 } 130 seen[ref] = struct{}{} 131 res = append(res, ref) 132 } 133 ChunkRefsPool.Put(g) 134 } 135 136 return res, nil 137 } 138 139 func (i *MultiIndex) Series(ctx context.Context, userID string, from, through model.Time, res []Series, shard *index.ShardAnnotation, matchers ...*labels.Matcher) ([]Series, error) { 140 if res == nil { 141 res = SeriesPool.Get() 142 } 143 res = res[:0] 144 145 groups, err := i.forIndices(ctx, from, through, func(ctx context.Context, idx Index) (interface{}, error) { 146 return idx.Series(ctx, userID, from, through, nil, shard, matchers...) 147 }) 148 if err != nil { 149 return nil, err 150 } 151 152 seen := make(map[model.Fingerprint]struct{}) 153 154 for _, x := range groups { 155 seriesSet := x.([]Series) 156 for _, s := range seriesSet { 157 _, ok := seen[s.Fingerprint] 158 if ok { 159 continue 160 } 161 seen[s.Fingerprint] = struct{}{} 162 res = append(res, s) 163 } 164 SeriesPool.Put(seriesSet) 165 } 166 167 return res, nil 168 } 169 170 func (i *MultiIndex) LabelNames(ctx context.Context, userID string, from, through model.Time, matchers ...*labels.Matcher) ([]string, error) { 171 groups, err := i.forIndices(ctx, from, through, func(ctx context.Context, idx Index) (interface{}, error) { 172 return idx.LabelNames(ctx, userID, from, through, matchers...) 173 }) 174 if err != nil { 175 return nil, err 176 } 177 178 var maxLn int // maximum number of chunk refs, assuming no duplicates 179 xs := make([][]string, 0, len(i.indices)) 180 for _, group := range groups { 181 x := group.([]string) 182 maxLn += len(x) 183 xs = append(xs, x) 184 } 185 186 // optimistically allocate the maximum length slice 187 // to avoid growing incrementally 188 results := make([]string, 0, maxLn) 189 seen := make(map[string]struct{}) 190 191 for _, ls := range xs { 192 for _, l := range ls { 193 _, ok := seen[l] 194 if ok { 195 continue 196 } 197 seen[l] = struct{}{} 198 results = append(results, l) 199 } 200 } 201 202 return results, nil 203 } 204 205 func (i *MultiIndex) LabelValues(ctx context.Context, userID string, from, through model.Time, name string, matchers ...*labels.Matcher) ([]string, error) { 206 groups, err := i.forIndices(ctx, from, through, func(ctx context.Context, idx Index) (interface{}, error) { 207 return idx.LabelValues(ctx, userID, from, through, name, matchers...) 208 }) 209 if err != nil { 210 return nil, err 211 } 212 213 var maxLn int // maximum number of chunk refs, assuming no duplicates 214 xs := make([][]string, 0, len(i.indices)) 215 for _, group := range groups { 216 x := group.([]string) 217 maxLn += len(x) 218 xs = append(xs, x) 219 } 220 221 // optimistically allocate the maximum length slice 222 // to avoid growing incrementally 223 results := make([]string, 0, maxLn) 224 seen := make(map[string]struct{}) 225 226 for _, ls := range xs { 227 for _, l := range ls { 228 _, ok := seen[l] 229 if ok { 230 continue 231 } 232 seen[l] = struct{}{} 233 results = append(results, l) 234 } 235 } 236 237 return results, nil 238 } 239 240 func (i *MultiIndex) Stats(ctx context.Context, userID string, from, through model.Time, blooms *stats.Blooms, shard *index.ShardAnnotation, matchers ...*labels.Matcher) (*stats.Blooms, error) { 241 if blooms == nil { 242 blooms = stats.BloomPool.Get() 243 } 244 245 _, err := i.forIndices(ctx, from, through, func(ctx context.Context, idx Index) (interface{}, error) { 246 return idx.Stats(ctx, userID, from, through, blooms, shard, matchers...) 247 }) 248 return blooms, err 249 }