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

     1  package index
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"math"
     9  	"math/rand"
    10  	"testing"
    11  
    12  	"github.com/prometheus/prometheus/model/labels"
    13  
    14  	"github.com/prometheus/common/model"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func TestLabelSeriesID(t *testing.T) {
    20  	for _, c := range []struct {
    21  		lbls     labels.Labels
    22  		expected string
    23  	}{
    24  		{
    25  			labels.Labels{{Name: model.MetricNameLabel, Value: "foo"}},
    26  			"LCa0a2j/xo/5m0U8HTBBNBNCLXBkg7+g+YpeiGJm564",
    27  		},
    28  		{
    29  			labels.Labels{
    30  				{Name: model.MetricNameLabel, Value: "foo"},
    31  				{Name: "bar", Value: "baz"},
    32  				{Name: "flip", Value: "flop"},
    33  				{Name: "toms", Value: "code"},
    34  			},
    35  			"KrbXMezYneba+o7wfEdtzOdAWhbfWcDrlVfs1uOCX3M",
    36  		},
    37  		{
    38  			labels.Labels{},
    39  			"RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o",
    40  		},
    41  	} {
    42  		seriesID := string(labelsSeriesID(c.lbls))
    43  		assert.Equal(t, c.expected, seriesID, labelsString(c.lbls))
    44  	}
    45  }
    46  
    47  func TestSchemaTimeEncoding(t *testing.T) {
    48  	assert.Equal(t, uint32(0), decodeTime(encodeTime(0)), "0")
    49  	assert.Equal(t, uint32(math.MaxUint32), decodeTime(encodeTime(math.MaxUint32)), "MaxUint32")
    50  
    51  	for i := 0; i < 100; i++ {
    52  		a, b := uint32(rand.Int31()), uint32(rand.Int31())
    53  
    54  		assert.Equal(t, a, decodeTime(encodeTime(a)), "a")
    55  		assert.Equal(t, b, decodeTime(encodeTime(b)), "b")
    56  
    57  		if a < b {
    58  			assert.Equal(t, -1, bytes.Compare(encodeTime(a), encodeTime(b)), "lt")
    59  		} else if a > b {
    60  			assert.Equal(t, 1, bytes.Compare(encodeTime(a), encodeTime(b)), "gt")
    61  		} else {
    62  			assert.Equal(t, 1, bytes.Compare(encodeTime(a), encodeTime(b)), "eq")
    63  		}
    64  	}
    65  }
    66  
    67  func TestParseChunkTimeRangeValue(t *testing.T) {
    68  	// Test we can decode legacy range values
    69  	for _, c := range []struct {
    70  		encoded        []byte
    71  		value, chunkID string
    72  	}{
    73  		{[]byte("1\x002\x003\x00"), "2", "3"},
    74  
    75  		// version 1 range keys (v3 Schema) base64-encodes the label value
    76  		{
    77  			[]byte("toms\x00Y29kZQ\x002:1484661279394:1484664879394\x001\x00"),
    78  			"code", "2:1484661279394:1484664879394",
    79  		},
    80  
    81  		// version 1 range keys (v4 Schema) doesn't have the label name in the range key
    82  		{
    83  			[]byte("\x00Y29kZQ\x002:1484661279394:1484664879394\x001\x00"),
    84  			"code", "2:1484661279394:1484664879394",
    85  		},
    86  
    87  		// version 2 range keys (also v4 Schema) don't have the label name or value in the range key
    88  		{
    89  			[]byte("\x00\x002:1484661279394:1484664879394\x002\x00"),
    90  			"", "2:1484661279394:1484664879394",
    91  		},
    92  
    93  		// version 3 range keys (v5 Schema) have timestamp in first 'dimension'
    94  		{
    95  			[]byte("a1b2c3d4\x00\x002:1484661279394:1484664879394\x003\x00"),
    96  			"", "2:1484661279394:1484664879394",
    97  		},
    98  
    99  		// version 4 range keys (also v5 Schema) have timestamp in first 'dimension',
   100  		// base64 value in second
   101  		{
   102  			[]byte("a1b2c3d4\x00Y29kZQ\x002:1484661279394:1484664879394\x004\x00"),
   103  			"code", "2:1484661279394:1484664879394",
   104  		},
   105  	} {
   106  		chunkID, labelValue, err := ParseChunkTimeRangeValue(c.encoded, nil)
   107  		require.NoError(t, err)
   108  		assert.Equal(t, model.LabelValue(c.value), labelValue)
   109  		assert.Equal(t, c.chunkID, chunkID)
   110  	}
   111  }
   112  
   113  func TestParseMetricNameRangeValue(t *testing.T) {
   114  	for _, c := range []struct {
   115  		encoded       []byte
   116  		value         string
   117  		expMetricName string
   118  	}{
   119  		// version 1 (id 6) metric name range keys (used in v7 Schema) have
   120  		// metric name hash in first 'dimension', however just returns the value
   121  		{[]byte("a1b2c3d4\x00\x00\x006\x00"), "foo", "foo"},
   122  		{encodeRangeKey(metricNameRangeKeyV1, []byte("bar"), nil, nil), "bar", "bar"},
   123  	} {
   124  		metricName, err := parseMetricNameRangeValue(c.encoded, []byte(c.value))
   125  		require.NoError(t, err)
   126  		assert.Equal(t, model.LabelValue(c.expMetricName), metricName)
   127  	}
   128  }
   129  
   130  func TestParseSeriesRangeValue(t *testing.T) {
   131  	metric := model.Metric{
   132  		model.MetricNameLabel: "foo",
   133  		"bar":                 "bary",
   134  		"baz":                 "bazy",
   135  	}
   136  
   137  	fingerprintBytes := make([]byte, 8)
   138  	binary.LittleEndian.PutUint64(fingerprintBytes, uint64(metric.Fingerprint()))
   139  	metricBytes, err := json.Marshal(metric)
   140  	require.NoError(t, err)
   141  
   142  	for _, c := range []struct {
   143  		encoded   []byte
   144  		value     []byte
   145  		expMetric model.Metric
   146  	}{
   147  		{encodeRangeKey(seriesRangeKeyV1, fingerprintBytes, nil, nil), metricBytes, metric},
   148  	} {
   149  		metric, err := parseSeriesRangeValue(c.encoded, c.value)
   150  		require.NoError(t, err)
   151  		assert.Equal(t, c.expMetric, metric)
   152  	}
   153  }
   154  
   155  func decodeTime(bs []byte) uint32 {
   156  	buf := make([]byte, 4)
   157  	_, _ = hex.Decode(buf, bs)
   158  	return binary.BigEndian.Uint32(buf)
   159  }