github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/stores/series/index/schema.go (about)

     1  package index
     2  
     3  import (
     4  	"encoding/binary"
     5  	"encoding/hex"
     6  	"errors"
     7  	"fmt"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/go-kit/log/level"
    12  	jsoniter "github.com/json-iterator/go"
    13  	"github.com/prometheus/common/model"
    14  	"github.com/prometheus/prometheus/model/labels"
    15  
    16  	"github.com/grafana/loki/pkg/querier/astmapper"
    17  	util_log "github.com/grafana/loki/pkg/util/log"
    18  )
    19  
    20  const (
    21  	chunkTimeRangeKeyV1a = 1
    22  	chunkTimeRangeKeyV1  = '1'
    23  	chunkTimeRangeKeyV2  = '2'
    24  	chunkTimeRangeKeyV3  = '3'
    25  	chunkTimeRangeKeyV4  = '4'
    26  	chunkTimeRangeKeyV5  = '5'
    27  	metricNameRangeKeyV1 = '6'
    28  
    29  	// For v9 schema
    30  	seriesRangeKeyV1      = '7'
    31  	labelSeriesRangeKeyV1 = '8'
    32  	// For v11 schema
    33  	labelNamesRangeKeyV1 = '9'
    34  )
    35  
    36  var (
    37  	// ErrNotSupported when a schema doesn't support that particular lookup.
    38  	ErrNotSupported           = errors.New("not supported")
    39  	ErrMetricNameLabelMissing = errors.New("metric name label missing")
    40  	empty                     = []byte("-")
    41  )
    42  
    43  type hasChunksForIntervalFunc func(userID, seriesID string, from, through model.Time) (bool, error)
    44  
    45  // Schema interfaces define methods to calculate the hash and range keys needed
    46  // to write or read chunks from the external index.
    47  
    48  // SeriesStoreSchema is a schema used by seriesStore
    49  type SeriesStoreSchema interface {
    50  	// When doing a read, use these methods to return the list of entries you should query
    51  	GetReadQueriesForMetric(from, through model.Time, userID string, metricName string) ([]Query, error)
    52  	GetReadQueriesForMetricLabel(from, through model.Time, userID string, metricName string, labelName string) ([]Query, error)
    53  	GetReadQueriesForMetricLabelValue(from, through model.Time, userID string, metricName string, labelName string, labelValue string) ([]Query, error)
    54  	FilterReadQueries(queries []Query, shard *astmapper.ShardAnnotation) []Query
    55  
    56  	// returns cache key string and []IndexEntry per bucket, matched in order
    57  	GetCacheKeysAndLabelWriteEntries(from, through model.Time, userID string, metricName string, labels labels.Labels, chunkID string) ([]string, [][]Entry, error)
    58  	GetChunkWriteEntries(from, through model.Time, userID string, metricName string, labels labels.Labels, chunkID string) ([]Entry, error)
    59  
    60  	// If the query resulted in series IDs, use this method to find chunks.
    61  	GetChunksForSeries(from, through model.Time, userID string, seriesID []byte) ([]Query, error)
    62  	// Returns queries to retrieve all label names of multiple series by id.
    63  	GetLabelNamesForSeries(from, through model.Time, userID string, seriesID []byte) ([]Query, error)
    64  }
    65  
    66  type schemaBucketsFunc func(from, through model.Time, userID string) []Bucket
    67  
    68  // baseSchema implements BaseSchema given a bucketing function and and set of range key callbacks
    69  type baseSchema struct {
    70  	buckets schemaBucketsFunc
    71  	entries baseEntries
    72  }
    73  
    74  // seriesStoreSchema implements SeriesStoreSchema given a bucketing function and and set of range key callbacks
    75  type seriesStoreSchema struct {
    76  	baseSchema
    77  	entries seriesStoreEntries
    78  }
    79  
    80  func newSeriesStoreSchema(buckets schemaBucketsFunc, entries seriesStoreEntries) seriesStoreSchema {
    81  	return seriesStoreSchema{
    82  		baseSchema: baseSchema{buckets: buckets, entries: entries},
    83  		entries:    entries,
    84  	}
    85  }
    86  
    87  // returns cache key string and []IndexEntry per bucket, matched in order
    88  func (s seriesStoreSchema) GetCacheKeysAndLabelWriteEntries(from, through model.Time, userID string, metricName string, labels labels.Labels, chunkID string) ([]string, [][]Entry, error) {
    89  	var keys []string
    90  	var indexEntries [][]Entry
    91  
    92  	for _, bucket := range s.buckets(from, through, userID) {
    93  		key := strings.Join([]string{
    94  			bucket.tableName,
    95  			bucket.hashKey,
    96  			string(labelsSeriesID(labels)),
    97  		},
    98  			"-",
    99  		)
   100  		// This is just encoding to remove invalid characters so that we can put them in memcache.
   101  		// We're not hashing them as the length of the key is well within memcache bounds. tableName + userid + day + 32Byte(seriesID)
   102  		key = hex.EncodeToString([]byte(key))
   103  		keys = append(keys, key)
   104  
   105  		entries, err := s.entries.GetLabelWriteEntries(bucket, metricName, labels, chunkID)
   106  		if err != nil {
   107  			return nil, nil, err
   108  		}
   109  		indexEntries = append(indexEntries, entries)
   110  	}
   111  	return keys, indexEntries, nil
   112  }
   113  
   114  func (s seriesStoreSchema) GetChunkWriteEntries(from, through model.Time, userID string, metricName string, labels labels.Labels, chunkID string) ([]Entry, error) {
   115  	var result []Entry
   116  
   117  	for _, bucket := range s.buckets(from, through, userID) {
   118  		entries, err := s.entries.GetChunkWriteEntries(bucket, metricName, labels, chunkID)
   119  		if err != nil {
   120  			return nil, err
   121  		}
   122  		result = append(result, entries...)
   123  	}
   124  	return result, nil
   125  }
   126  
   127  func (s baseSchema) GetReadQueriesForMetric(from, through model.Time, userID string, metricName string) ([]Query, error) {
   128  	var result []Query
   129  
   130  	buckets := s.buckets(from, through, userID)
   131  	for _, bucket := range buckets {
   132  		entries, err := s.entries.GetReadMetricQueries(bucket, metricName)
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  		result = append(result, entries...)
   137  	}
   138  	return result, nil
   139  }
   140  
   141  func (s baseSchema) GetReadQueriesForMetricLabel(from, through model.Time, userID string, metricName string, labelName string) ([]Query, error) {
   142  	var result []Query
   143  
   144  	buckets := s.buckets(from, through, userID)
   145  	for _, bucket := range buckets {
   146  		entries, err := s.entries.GetReadMetricLabelQueries(bucket, metricName, labelName)
   147  		if err != nil {
   148  			return nil, err
   149  		}
   150  		result = append(result, entries...)
   151  	}
   152  	return result, nil
   153  }
   154  
   155  func (s baseSchema) GetReadQueriesForMetricLabelValue(from, through model.Time, userID string, metricName string, labelName string, labelValue string) ([]Query, error) {
   156  	var result []Query
   157  
   158  	buckets := s.buckets(from, through, userID)
   159  	for _, bucket := range buckets {
   160  		entries, err := s.entries.GetReadMetricLabelValueQueries(bucket, metricName, labelName, labelValue)
   161  		if err != nil {
   162  			return nil, err
   163  		}
   164  		result = append(result, entries...)
   165  	}
   166  	return result, nil
   167  }
   168  
   169  func (s seriesStoreSchema) GetChunksForSeries(from, through model.Time, userID string, seriesID []byte) ([]Query, error) {
   170  	var result []Query
   171  
   172  	buckets := s.buckets(from, through, userID)
   173  	for _, bucket := range buckets {
   174  		entries, err := s.entries.GetChunksForSeries(bucket, seriesID)
   175  		if err != nil {
   176  			return nil, err
   177  		}
   178  		result = append(result, entries...)
   179  	}
   180  	return result, nil
   181  }
   182  
   183  // GetSeriesDeleteEntries returns IndexEntry's for deleting SeriesIDs from SeriesStore.
   184  // Since SeriesIDs are created per bucket, it makes sure that we don't include series entries which are in use by verifying using hasChunksForIntervalFunc i.e
   185  // It checks first and last buckets covered by the time interval to see if a SeriesID still has chunks in the store,
   186  // if yes then it doesn't include IndexEntry's for that bucket for deletion.
   187  func (s seriesStoreSchema) GetSeriesDeleteEntries(from, through model.Time, userID string, metric labels.Labels, hasChunksForIntervalFunc hasChunksForIntervalFunc) ([]Entry, error) {
   188  	metricName := metric.Get(model.MetricNameLabel)
   189  	if metricName == "" {
   190  		return nil, ErrMetricNameLabelMissing
   191  	}
   192  
   193  	buckets := s.buckets(from, through, userID)
   194  	if len(buckets) == 0 {
   195  		return nil, nil
   196  	}
   197  
   198  	seriesID := string(labelsSeriesID(metric))
   199  
   200  	// Only first and last buckets needs to be checked for in-use series ids.
   201  	// Only partially deleted first/last deleted bucket needs to be checked otherwise
   202  	// not since whole bucket is anyways considered for deletion.
   203  
   204  	// Bucket times are relative to the bucket i.e for a per-day bucket
   205  	// bucket.from would be the number of milliseconds elapsed since the start of that day.
   206  	// If bucket.from is not 0, it means the from param doesn't align with the start of the bucket.
   207  	if buckets[0].from != 0 {
   208  		bucketStartTime := from - model.Time(buckets[0].from)
   209  		hasChunks, err := hasChunksForIntervalFunc(userID, seriesID, bucketStartTime, bucketStartTime+model.Time(buckets[0].bucketSize)-1)
   210  		if err != nil {
   211  			return nil, err
   212  		}
   213  
   214  		if hasChunks {
   215  			buckets = buckets[1:]
   216  			if len(buckets) == 0 {
   217  				return nil, nil
   218  			}
   219  		}
   220  	}
   221  
   222  	lastBucket := buckets[len(buckets)-1]
   223  
   224  	// Similar to bucket.from, bucket.through here is also relative i.e for a per-day bucket
   225  	// through would be the number of milliseconds elapsed since the start of that day
   226  	// If bucket.through is not equal to max size of bucket, it means the through param doesn't align with the end of the bucket.
   227  	if lastBucket.through != lastBucket.bucketSize {
   228  		bucketStartTime := through - model.Time(lastBucket.through)
   229  		hasChunks, err := hasChunksForIntervalFunc(userID, seriesID, bucketStartTime, bucketStartTime+model.Time(lastBucket.bucketSize)-1)
   230  		if err != nil {
   231  			return nil, err
   232  		}
   233  
   234  		if hasChunks {
   235  			buckets = buckets[:len(buckets)-1]
   236  			if len(buckets) == 0 {
   237  				return nil, nil
   238  			}
   239  		}
   240  	}
   241  
   242  	var result []Entry
   243  
   244  	for _, bucket := range buckets {
   245  		entries, err := s.entries.GetLabelWriteEntries(bucket, metricName, metric, "")
   246  		if err != nil {
   247  			return nil, err
   248  		}
   249  		result = append(result, entries...)
   250  	}
   251  
   252  	return result, nil
   253  }
   254  
   255  func (s seriesStoreSchema) GetLabelNamesForSeries(from, through model.Time, userID string, seriesID []byte) ([]Query, error) {
   256  	var result []Query
   257  
   258  	buckets := s.buckets(from, through, userID)
   259  	for _, bucket := range buckets {
   260  		entries, err := s.entries.GetLabelNamesForSeries(bucket, seriesID)
   261  		if err != nil {
   262  			return nil, err
   263  		}
   264  		result = append(result, entries...)
   265  	}
   266  	return result, nil
   267  }
   268  
   269  func (s baseSchema) FilterReadQueries(queries []Query, shard *astmapper.ShardAnnotation) []Query {
   270  	return s.entries.FilterReadQueries(queries, shard)
   271  }
   272  
   273  type baseEntries interface {
   274  	GetReadMetricQueries(bucket Bucket, metricName string) ([]Query, error)
   275  	GetReadMetricLabelQueries(bucket Bucket, metricName string, labelName string) ([]Query, error)
   276  	GetReadMetricLabelValueQueries(bucket Bucket, metricName string, labelName string, labelValue string) ([]Query, error)
   277  	FilterReadQueries(queries []Query, shard *astmapper.ShardAnnotation) []Query
   278  }
   279  
   280  // used by seriesStoreSchema
   281  type seriesStoreEntries interface {
   282  	baseEntries
   283  
   284  	GetLabelWriteEntries(bucket Bucket, metricName string, labels labels.Labels, chunkID string) ([]Entry, error)
   285  	GetChunkWriteEntries(bucket Bucket, metricName string, labels labels.Labels, chunkID string) ([]Entry, error)
   286  
   287  	GetChunksForSeries(bucket Bucket, seriesID []byte) ([]Query, error)
   288  	GetLabelNamesForSeries(bucket Bucket, seriesID []byte) ([]Query, error)
   289  }
   290  
   291  // v9Entries adds a layer of indirection between labels -> series -> chunks.
   292  type v9Entries struct{}
   293  
   294  func (v9Entries) GetLabelWriteEntries(bucket Bucket, metricName string, labels labels.Labels, chunkID string) ([]Entry, error) {
   295  	seriesID := labelsSeriesID(labels)
   296  
   297  	entries := []Entry{
   298  		// Entry for metricName -> seriesID
   299  		{
   300  			TableName:  bucket.tableName,
   301  			HashValue:  bucket.hashKey + ":" + metricName,
   302  			RangeValue: encodeRangeKey(seriesRangeKeyV1, seriesID, nil, nil),
   303  			Value:      empty,
   304  		},
   305  	}
   306  
   307  	// Entries for metricName:labelName -> hash(value):seriesID
   308  	// We use a hash of the value to limit its length.
   309  	for _, v := range labels {
   310  		if v.Name == model.MetricNameLabel {
   311  			continue
   312  		}
   313  		valueHash := sha256bytes(v.Value)
   314  		entries = append(entries, Entry{
   315  			TableName:  bucket.tableName,
   316  			HashValue:  fmt.Sprintf("%s:%s:%s", bucket.hashKey, metricName, v.Name),
   317  			RangeValue: encodeRangeKey(labelSeriesRangeKeyV1, valueHash, seriesID, nil),
   318  			Value:      []byte(v.Value),
   319  		})
   320  	}
   321  
   322  	return entries, nil
   323  }
   324  
   325  func (v9Entries) GetChunkWriteEntries(bucket Bucket, metricName string, labels labels.Labels, chunkID string) ([]Entry, error) {
   326  	seriesID := labelsSeriesID(labels)
   327  	encodedThroughBytes := encodeTime(bucket.through)
   328  
   329  	entries := []Entry{
   330  		// Entry for seriesID -> chunkID
   331  		{
   332  			TableName:  bucket.tableName,
   333  			HashValue:  bucket.hashKey + ":" + string(seriesID),
   334  			RangeValue: encodeRangeKey(chunkTimeRangeKeyV3, encodedThroughBytes, nil, []byte(chunkID)),
   335  		},
   336  	}
   337  
   338  	return entries, nil
   339  }
   340  
   341  func (v9Entries) GetReadMetricQueries(bucket Bucket, metricName string) ([]Query, error) {
   342  	return []Query{
   343  		{
   344  			TableName: bucket.tableName,
   345  			HashValue: bucket.hashKey + ":" + metricName,
   346  		},
   347  	}, nil
   348  }
   349  
   350  func (v9Entries) GetReadMetricLabelQueries(bucket Bucket, metricName string, labelName string) ([]Query, error) {
   351  	return []Query{
   352  		{
   353  			TableName: bucket.tableName,
   354  			HashValue: fmt.Sprintf("%s:%s:%s", bucket.hashKey, metricName, labelName),
   355  		},
   356  	}, nil
   357  }
   358  
   359  func (v9Entries) GetReadMetricLabelValueQueries(bucket Bucket, metricName string, labelName string, labelValue string) ([]Query, error) {
   360  	valueHash := sha256bytes(labelValue)
   361  	return []Query{
   362  		{
   363  			TableName:        bucket.tableName,
   364  			HashValue:        fmt.Sprintf("%s:%s:%s", bucket.hashKey, metricName, labelName),
   365  			RangeValuePrefix: rangeValuePrefix(valueHash),
   366  			ValueEqual:       []byte(labelValue),
   367  		},
   368  	}, nil
   369  }
   370  
   371  func (v9Entries) GetChunksForSeries(bucket Bucket, seriesID []byte) ([]Query, error) {
   372  	encodedFromBytes := encodeTime(bucket.from)
   373  	return []Query{
   374  		{
   375  			TableName:       bucket.tableName,
   376  			HashValue:       bucket.hashKey + ":" + string(seriesID),
   377  			RangeValueStart: rangeValuePrefix(encodedFromBytes),
   378  		},
   379  	}, nil
   380  }
   381  
   382  func (v9Entries) GetLabelNamesForSeries(_ Bucket, _ []byte) ([]Query, error) {
   383  	return nil, ErrNotSupported
   384  }
   385  
   386  func (v9Entries) FilterReadQueries(queries []Query, shard *astmapper.ShardAnnotation) []Query {
   387  	return queries
   388  }
   389  
   390  // v10Entries builds on v9 by sharding index rows to reduce their size.
   391  type v10Entries struct {
   392  	rowShards uint32
   393  }
   394  
   395  func (s v10Entries) GetLabelWriteEntries(bucket Bucket, metricName string, labels labels.Labels, chunkID string) ([]Entry, error) {
   396  	seriesID := labelsSeriesID(labels)
   397  
   398  	// read first 32 bits of the hash and use this to calculate the shard
   399  	shard := binary.BigEndian.Uint32(seriesID) % s.rowShards
   400  
   401  	entries := []Entry{
   402  		// Entry for metricName -> seriesID
   403  		{
   404  			TableName:  bucket.tableName,
   405  			HashValue:  fmt.Sprintf("%02d:%s:%s", shard, bucket.hashKey, metricName),
   406  			RangeValue: encodeRangeKey(seriesRangeKeyV1, seriesID, nil, nil),
   407  			Value:      empty,
   408  		},
   409  	}
   410  
   411  	// Entries for metricName:labelName -> hash(value):seriesID
   412  	// We use a hash of the value to limit its length.
   413  	for _, v := range labels {
   414  		if v.Name == model.MetricNameLabel {
   415  			continue
   416  		}
   417  		valueHash := sha256bytes(v.Value)
   418  		entries = append(entries, Entry{
   419  			TableName:  bucket.tableName,
   420  			HashValue:  fmt.Sprintf("%02d:%s:%s:%s", shard, bucket.hashKey, metricName, v.Name),
   421  			RangeValue: encodeRangeKey(labelSeriesRangeKeyV1, valueHash, seriesID, nil),
   422  			Value:      []byte(v.Value),
   423  		})
   424  	}
   425  
   426  	return entries, nil
   427  }
   428  
   429  func (v10Entries) GetChunkWriteEntries(bucket Bucket, metricName string, labels labels.Labels, chunkID string) ([]Entry, error) {
   430  	seriesID := labelsSeriesID(labels)
   431  	encodedThroughBytes := encodeTime(bucket.through)
   432  
   433  	entries := []Entry{
   434  		// Entry for seriesID -> chunkID
   435  		{
   436  			TableName:  bucket.tableName,
   437  			HashValue:  bucket.hashKey + ":" + string(seriesID),
   438  			RangeValue: encodeRangeKey(chunkTimeRangeKeyV3, encodedThroughBytes, nil, []byte(chunkID)),
   439  			Value:      empty,
   440  		},
   441  	}
   442  
   443  	return entries, nil
   444  }
   445  
   446  func (s v10Entries) GetReadMetricQueries(bucket Bucket, metricName string) ([]Query, error) {
   447  	result := make([]Query, 0, s.rowShards)
   448  	for i := uint32(0); i < s.rowShards; i++ {
   449  		result = append(result, Query{
   450  			TableName: bucket.tableName,
   451  			HashValue: fmt.Sprintf("%02d:%s:%s", i, bucket.hashKey, metricName),
   452  		})
   453  	}
   454  	return result, nil
   455  }
   456  
   457  func (s v10Entries) GetReadMetricLabelQueries(bucket Bucket, metricName string, labelName string) ([]Query, error) {
   458  	result := make([]Query, 0, s.rowShards)
   459  	for i := uint32(0); i < s.rowShards; i++ {
   460  		result = append(result, Query{
   461  			TableName: bucket.tableName,
   462  			HashValue: fmt.Sprintf("%02d:%s:%s:%s", i, bucket.hashKey, metricName, labelName),
   463  		})
   464  	}
   465  	return result, nil
   466  }
   467  
   468  func (s v10Entries) GetReadMetricLabelValueQueries(bucket Bucket, metricName string, labelName string, labelValue string) ([]Query, error) {
   469  	valueHash := sha256bytes(labelValue)
   470  	result := make([]Query, 0, s.rowShards)
   471  	for i := uint32(0); i < s.rowShards; i++ {
   472  		result = append(result, Query{
   473  			TableName:        bucket.tableName,
   474  			HashValue:        fmt.Sprintf("%02d:%s:%s:%s", i, bucket.hashKey, metricName, labelName),
   475  			RangeValuePrefix: rangeValuePrefix(valueHash),
   476  			ValueEqual:       []byte(labelValue),
   477  		})
   478  	}
   479  	return result, nil
   480  }
   481  
   482  func (v10Entries) GetChunksForSeries(bucket Bucket, seriesID []byte) ([]Query, error) {
   483  	encodedFromBytes := encodeTime(bucket.from)
   484  	return []Query{
   485  		{
   486  			TableName:       bucket.tableName,
   487  			HashValue:       bucket.hashKey + ":" + string(seriesID),
   488  			RangeValueStart: rangeValuePrefix(encodedFromBytes),
   489  		},
   490  	}, nil
   491  }
   492  
   493  func (v10Entries) GetLabelNamesForSeries(_ Bucket, _ []byte) ([]Query, error) {
   494  	return nil, ErrNotSupported
   495  }
   496  
   497  // FilterReadQueries will return only queries that match a certain shard
   498  func (v10Entries) FilterReadQueries(queries []Query, shard *astmapper.ShardAnnotation) (matches []Query) {
   499  	if shard == nil {
   500  		return queries
   501  	}
   502  
   503  	for _, query := range queries {
   504  		s := strings.Split(query.HashValue, ":")[0]
   505  		n, err := strconv.Atoi(s)
   506  		if err != nil {
   507  			level.Error(util_log.Logger).Log(
   508  				"msg",
   509  				"Unable to determine shard from IndexQuery",
   510  				"HashValue",
   511  				query.HashValue,
   512  				"schema",
   513  				"v10",
   514  			)
   515  		}
   516  
   517  		if err == nil && n == shard.Shard {
   518  			matches = append(matches, query)
   519  		}
   520  	}
   521  	return matches
   522  }
   523  
   524  // v11Entries builds on v10 but adds index entries for each series to store respective labels.
   525  type v11Entries struct {
   526  	v10Entries
   527  }
   528  
   529  func (s v11Entries) GetLabelWriteEntries(bucket Bucket, metricName string, labels labels.Labels, chunkID string) ([]Entry, error) {
   530  	seriesID := labelsSeriesID(labels)
   531  
   532  	// read first 32 bits of the hash and use this to calculate the shard
   533  	shard := binary.BigEndian.Uint32(seriesID) % s.rowShards
   534  
   535  	labelNames := make([]string, 0, len(labels))
   536  	for _, l := range labels {
   537  		if l.Name == model.MetricNameLabel {
   538  			continue
   539  		}
   540  		labelNames = append(labelNames, l.Name)
   541  	}
   542  	data, err := jsoniter.ConfigFastest.Marshal(labelNames)
   543  	if err != nil {
   544  		return nil, err
   545  	}
   546  	entries := []Entry{
   547  		// Entry for metricName -> seriesID
   548  		{
   549  			TableName:  bucket.tableName,
   550  			HashValue:  fmt.Sprintf("%02d:%s:%s", shard, bucket.hashKey, metricName),
   551  			RangeValue: encodeRangeKey(seriesRangeKeyV1, seriesID, nil, nil),
   552  			Value:      empty,
   553  		},
   554  		// Entry for seriesID -> label names
   555  		{
   556  			TableName:  bucket.tableName,
   557  			HashValue:  string(seriesID),
   558  			RangeValue: encodeRangeKey(labelNamesRangeKeyV1, nil, nil, nil),
   559  			Value:      data,
   560  		},
   561  	}
   562  
   563  	// Entries for metricName:labelName -> hash(value):seriesID
   564  	// We use a hash of the value to limit its length.
   565  	for _, v := range labels {
   566  		if v.Name == model.MetricNameLabel {
   567  			continue
   568  		}
   569  		valueHash := sha256bytes(v.Value)
   570  		entries = append(entries, Entry{
   571  			TableName:  bucket.tableName,
   572  			HashValue:  fmt.Sprintf("%02d:%s:%s:%s", shard, bucket.hashKey, metricName, v.Name),
   573  			RangeValue: encodeRangeKey(labelSeriesRangeKeyV1, valueHash, seriesID, nil),
   574  			Value:      []byte(v.Value),
   575  		})
   576  	}
   577  
   578  	return entries, nil
   579  }
   580  
   581  func (v11Entries) GetLabelNamesForSeries(bucket Bucket, seriesID []byte) ([]Query, error) {
   582  	return []Query{
   583  		{
   584  			TableName: bucket.tableName,
   585  			HashValue: string(seriesID),
   586  		},
   587  	}, nil
   588  }
   589  
   590  type v12Entries struct {
   591  	v11Entries
   592  }