Like Prometheus, but for logs.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
loki/pkg/storage/chunk/schema_util_test.go

149 lines
4.3 KiB

package chunk
import (
"bytes"
"encoding/binary"
"encoding/hex"
"encoding/json"
"math"
"math/rand"
"testing"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestLabelSeriesID(t *testing.T) {
for _, c := range []struct {
lbls labels.Labels
expected string
}{
{
labels.Labels{{Name: model.MetricNameLabel, Value: "foo"}},
"LCa0a2j/xo/5m0U8HTBBNBNCLXBkg7+g+YpeiGJm564",
},
{
labels.Labels{
{Name: model.MetricNameLabel, Value: "foo"},
{Name: "bar", Value: "baz"},
{Name: "flip", Value: "flop"},
{Name: "toms", Value: "code"},
},
"KrbXMezYneba+o7wfEdtzOdAWhbfWcDrlVfs1uOCX3M",
},
{
labels.Labels{},
"RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o",
},
} {
seriesID := string(labelsSeriesID(c.lbls))
assert.Equal(t, c.expected, seriesID, labelsString(c.lbls))
}
}
func TestSchemaTimeEncoding(t *testing.T) {
assert.Equal(t, uint32(0), decodeTime(encodeTime(0)), "0")
assert.Equal(t, uint32(math.MaxUint32), decodeTime(encodeTime(math.MaxUint32)), "MaxUint32")
for i := 0; i < 100; i++ {
a, b := uint32(rand.Int31()), uint32(rand.Int31())
assert.Equal(t, a, decodeTime(encodeTime(a)), "a")
assert.Equal(t, b, decodeTime(encodeTime(b)), "b")
if a < b {
assert.Equal(t, -1, bytes.Compare(encodeTime(a), encodeTime(b)), "lt")
} else if a > b {
assert.Equal(t, 1, bytes.Compare(encodeTime(a), encodeTime(b)), "gt")
} else {
assert.Equal(t, 1, bytes.Compare(encodeTime(a), encodeTime(b)), "eq")
}
}
}
func TestParseChunkTimeRangeValue(t *testing.T) {
// Test we can decode legacy range values
for _, c := range []struct {
encoded []byte
value, chunkID string
}{
{[]byte("1\x002\x003\x00"), "2", "3"},
// version 1 range keys (v3 Schema) base64-encodes the label value
{[]byte("toms\x00Y29kZQ\x002:1484661279394:1484664879394\x001\x00"),
"code", "2:1484661279394:1484664879394"},
// version 1 range keys (v4 Schema) doesn't have the label name in the range key
{[]byte("\x00Y29kZQ\x002:1484661279394:1484664879394\x001\x00"),
"code", "2:1484661279394:1484664879394"},
// version 2 range keys (also v4 Schema) don't have the label name or value in the range key
{[]byte("\x00\x002:1484661279394:1484664879394\x002\x00"),
"", "2:1484661279394:1484664879394"},
// version 3 range keys (v5 Schema) have timestamp in first 'dimension'
{[]byte("a1b2c3d4\x00\x002:1484661279394:1484664879394\x003\x00"),
"", "2:1484661279394:1484664879394"},
// version 4 range keys (also v5 Schema) have timestamp in first 'dimension',
// base64 value in second
{[]byte("a1b2c3d4\x00Y29kZQ\x002:1484661279394:1484664879394\x004\x00"),
"code", "2:1484661279394:1484664879394"},
} {
chunkID, labelValue, err := parseChunkTimeRangeValue(c.encoded, nil)
require.NoError(t, err)
assert.Equal(t, model.LabelValue(c.value), labelValue)
assert.Equal(t, c.chunkID, chunkID)
}
}
func TestParseMetricNameRangeValue(t *testing.T) {
for _, c := range []struct {
encoded []byte
value string
expMetricName string
}{
// version 1 (id 6) metric name range keys (used in v7 Schema) have
// metric name hash in first 'dimension', however just returns the value
{[]byte("a1b2c3d4\x00\x00\x006\x00"), "foo", "foo"},
{encodeRangeKey(metricNameRangeKeyV1, []byte("bar"), nil, nil), "bar", "bar"},
} {
metricName, err := parseMetricNameRangeValue(c.encoded, []byte(c.value))
require.NoError(t, err)
assert.Equal(t, model.LabelValue(c.expMetricName), metricName)
}
}
func TestParseSeriesRangeValue(t *testing.T) {
metric := model.Metric{
model.MetricNameLabel: "foo",
"bar": "bary",
"baz": "bazy",
}
fingerprintBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(fingerprintBytes, uint64(metric.Fingerprint()))
metricBytes, err := json.Marshal(metric)
require.NoError(t, err)
for _, c := range []struct {
encoded []byte
value []byte
expMetric model.Metric
}{
{encodeRangeKey(seriesRangeKeyV1, fingerprintBytes, nil, nil), metricBytes, metric},
} {
metric, err := parseSeriesRangeValue(c.encoded, c.value)
require.NoError(t, err)
assert.Equal(t, c.expMetric, metric)
}
}
func decodeTime(bs []byte) uint32 {
buf := make([]byte, 4)
_, _ = hex.Decode(buf, bs)
return binary.BigEndian.Uint32(buf)
}