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  }