github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/ingester/index/bitprefix.go (about)

     1  package index
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  
     7  	"github.com/prometheus/common/model"
     8  	"github.com/prometheus/prometheus/model/labels"
     9  
    10  	"github.com/grafana/loki/pkg/logproto"
    11  	"github.com/grafana/loki/pkg/querier/astmapper"
    12  	"github.com/grafana/loki/pkg/storage/stores/tsdb/index"
    13  )
    14  
    15  // BitPrefixInvertedIndex is another inverted index implementation
    16  // that uses the bit prefix sharding algorithm in tsdb/index/shard.go
    17  // instead of a modulo approach.
    18  // This is the standard for TSDB compatibility because
    19  // the same series must resolve to the same shard (for each period config),
    20  // whether it's resolved on the ingester or via the store.
    21  type BitPrefixInvertedIndex struct {
    22  	totalShards uint32
    23  	shards      []*indexShard
    24  }
    25  
    26  func ValidateBitPrefixShardFactor(factor uint32) error {
    27  	if requiredBits := index.NewShard(0, factor).RequiredBits(); 1<<requiredBits != factor {
    28  		return fmt.Errorf("Incompatible inverted index shard factor on ingester: It must be a power of two, got %d", factor)
    29  	}
    30  	return nil
    31  }
    32  
    33  func NewBitPrefixWithShards(totalShards uint32) (*BitPrefixInvertedIndex, error) {
    34  	if err := ValidateBitPrefixShardFactor(totalShards); err != nil {
    35  		return nil, err
    36  	}
    37  
    38  	shards := make([]*indexShard, totalShards)
    39  	for i := uint32(0); i < totalShards; i++ {
    40  		shards[i] = &indexShard{
    41  			idx:   map[string]indexEntry{},
    42  			shard: i,
    43  		}
    44  	}
    45  	return &BitPrefixInvertedIndex{
    46  		totalShards: totalShards,
    47  		shards:      shards,
    48  	}, nil
    49  }
    50  
    51  func (ii *BitPrefixInvertedIndex) getShards(shard *astmapper.ShardAnnotation) ([]*indexShard, bool) {
    52  	if shard == nil {
    53  		return ii.shards, false
    54  	}
    55  
    56  	// When comparing a higher shard factor to a lower inverted index shard factor
    57  	// we must filter resulting fingerprints as the the lower shard factor in the
    58  	// inverted index is a superset of the requested factor.
    59  	//
    60  	// For instance, the 3_of_4 shard factor maps to the bit prefix 0b11.
    61  	// If the inverted index only has a factor of 2, we'll need to check the 0b1
    62  	// prefixed shard (which contains the 0b10 and 0b11 prefixes).
    63  	// Conversely, if the requested shard is 1_of_2, but the index has a factor of 4,
    64  	// we can _exactly_ match ob1 => (ob10, ob11) and know all fingerprints in those
    65  	// resulting shards have the requested ob1 prefix (don't need to filter).
    66  	var filter bool
    67  	if shard.Of > len(ii.shards) {
    68  		filter = true
    69  	}
    70  
    71  	requestedShard := shard.TSDB()
    72  	minFp, maxFp := requestedShard.Bounds()
    73  
    74  	// Determine how many bits we need to take from
    75  	// the requested shard's min/max fingerprint values
    76  	// in order to calculate the indices for the inverted index's
    77  	// shard factor.
    78  	requiredBits := index.NewShard(0, uint32(len(ii.shards))).RequiredBits()
    79  	lowerIdx := int(minFp >> (64 - requiredBits))
    80  	upperIdx := int(maxFp >> (64 - requiredBits))
    81  
    82  	// If the upper bound's shard doesn't align exactly
    83  	// with the maximum fingerprint, we must also
    84  	// check the subsequent shard.
    85  	// This happens in two cases:
    86  	// 1) When requesting the last shard of any factor.
    87  	// This accounts for zero indexing in our sharding logic
    88  	// to successfully request `shards[start:len(shards)]`
    89  	// 2) When requesting the _first_ shard of a larger factor
    90  	// than the index uses. In this case, the required_bits are not
    91  	// enough and the requested end prefix gets trimmed.
    92  	// If confused, comment out this line and see which tests fail.
    93  	if (upperIdx << (64 - requiredBits)) != int(maxFp) {
    94  		upperIdx++
    95  	}
    96  
    97  	return ii.shards[lowerIdx:upperIdx], filter
    98  }
    99  
   100  func (ii *BitPrefixInvertedIndex) shardForFP(fp model.Fingerprint) int {
   101  	localShard := index.NewShard(0, uint32(len(ii.shards)))
   102  	return int(fp >> (64 - localShard.RequiredBits()))
   103  }
   104  
   105  func (ii *BitPrefixInvertedIndex) validateShard(shard *astmapper.ShardAnnotation) error {
   106  	if shard == nil {
   107  		return nil
   108  	}
   109  
   110  	return shard.TSDB().Validate()
   111  }
   112  
   113  // Add a fingerprint under the specified labels.
   114  // NOTE: memory for `labels` is unsafe; anything retained beyond the
   115  // life of this function must be copied
   116  func (ii *BitPrefixInvertedIndex) Add(labels []logproto.LabelAdapter, fp model.Fingerprint) labels.Labels {
   117  	// add() returns 'interned' values so the original labels are not retained
   118  	return ii.shards[ii.shardForFP(fp)].add(labels, fp)
   119  }
   120  
   121  // Lookup all fingerprints for the provided matchers.
   122  func (ii *BitPrefixInvertedIndex) Lookup(matchers []*labels.Matcher, shard *astmapper.ShardAnnotation) ([]model.Fingerprint, error) {
   123  	if err := ii.validateShard(shard); err != nil {
   124  		return nil, err
   125  	}
   126  
   127  	var result []model.Fingerprint
   128  	shards, filter := ii.getShards(shard)
   129  
   130  	// if no matcher is specified, all fingerprints would be returned
   131  	if len(matchers) == 0 {
   132  		for i := range shards {
   133  			fps := shards[i].allFPs()
   134  			result = append(result, fps...)
   135  		}
   136  	} else {
   137  		for i := range shards {
   138  			fps := shards[i].lookup(matchers)
   139  			result = append(result, fps...)
   140  		}
   141  	}
   142  
   143  	// Because bit prefix order is also ascending order,
   144  	// the merged fingerprints from ascending shards are also in order.
   145  	if filter {
   146  		minFP, maxFP := shard.TSDB().Bounds()
   147  		minIdx := sort.Search(len(result), func(i int) bool {
   148  			return result[i] >= minFP
   149  		})
   150  
   151  		maxIdx := sort.Search(len(result), func(i int) bool {
   152  			return result[i] >= maxFP
   153  		})
   154  
   155  		result = result[minIdx:maxIdx]
   156  	}
   157  
   158  	return result, nil
   159  }
   160  
   161  // LabelNames returns all label names.
   162  func (ii *BitPrefixInvertedIndex) LabelNames(shard *astmapper.ShardAnnotation) ([]string, error) {
   163  	if err := ii.validateShard(shard); err != nil {
   164  		return nil, err
   165  	}
   166  
   167  	var extractor func(unlockIndex) []string
   168  	shards, filter := ii.getShards(shard)
   169  
   170  	// If we need to check shard inclusion, we have to do it the expensive way :(
   171  	// Therefore it's more performant to request shard factors lower or equal to the
   172  	// inverted index factor
   173  	if filter {
   174  		s := shard.TSDB()
   175  
   176  		extractor = func(x unlockIndex) (results []string) {
   177  
   178  		outer:
   179  			for name, entry := range x {
   180  				for _, valEntry := range entry.fps {
   181  					for _, fp := range valEntry.fps {
   182  						if s.Match(fp) {
   183  							results = append(results, name)
   184  							continue outer
   185  						}
   186  					}
   187  				}
   188  			}
   189  
   190  			return results
   191  		}
   192  	}
   193  
   194  	results := make([][]string, 0, len(shards))
   195  	for i := range shards {
   196  		shardResult := shards[i].labelNames(extractor)
   197  		results = append(results, shardResult)
   198  	}
   199  
   200  	return mergeStringSlices(results), nil
   201  }
   202  
   203  // LabelValues returns the values for the given label.
   204  func (ii *BitPrefixInvertedIndex) LabelValues(name string, shard *astmapper.ShardAnnotation) ([]string, error) {
   205  	if err := ii.validateShard(shard); err != nil {
   206  		return nil, err
   207  	}
   208  
   209  	var extractor func(indexEntry) []string
   210  	shards, filter := ii.getShards(shard)
   211  	if filter {
   212  		s := shard.TSDB()
   213  
   214  		extractor = func(x indexEntry) []string {
   215  			results := make([]string, 0, len(x.fps))
   216  
   217  		outer:
   218  			for val, valEntry := range x.fps {
   219  				for _, fp := range valEntry.fps {
   220  					if s.Match(fp) {
   221  						results = append(results, val)
   222  						continue outer
   223  					}
   224  				}
   225  			}
   226  			return results
   227  		}
   228  	}
   229  	results := make([][]string, 0, len(shards))
   230  
   231  	for i := range shards {
   232  		shardResult := shards[i].labelValues(name, extractor)
   233  		results = append(results, shardResult)
   234  	}
   235  
   236  	return mergeStringSlices(results), nil
   237  }
   238  
   239  // Delete a fingerprint with the given label pairs.
   240  func (ii *BitPrefixInvertedIndex) Delete(labels labels.Labels, fp model.Fingerprint) {
   241  	localShard := index.NewShard(0, uint32(len(ii.shards)))
   242  	idx := int(fp >> (64 - localShard.RequiredBits()))
   243  	ii.shards[idx].delete(labels, fp)
   244  }