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 }