Skip to content

Commit

Permalink
storepb: construct labels.Labels directly during unmarshal
Browse files Browse the repository at this point in the history
Signed-off-by: Giedrius Statkevičius <giedrius.statkevicius@vinted.com>
  • Loading branch information
GiedriusS committed Sep 23, 2024
1 parent a2113fd commit e63e87d
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 0 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ require (
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
)

require github.com/VictoriaMetrics/easyproto v0.1.4 // indirect

require (
cloud.google.com/go/auth v0.5.1 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,8 @@ github.com/OneOfOne/xxhash v1.2.6 h1:U68crOE3y3MPttCMQGywZOLrTeF5HHJ3/vDBCJn9/bA
github.com/OneOfOne/xxhash v1.2.6/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/VictoriaMetrics/easyproto v0.1.4 h1:r8cNvo8o6sR4QShBXQd1bKw/VVLSQma/V2KhTBPf+Sc=
github.com/VictoriaMetrics/easyproto v0.1.4/go.mod h1:QlGlzaJnDfFd8Lk6Ci/fuLxfTo3/GThPs2KH23mv710=
github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
Expand Down
165 changes: 165 additions & 0 deletions pkg/store/labelpb/stringlabels.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package labelpb

import (
fmt "fmt"
"unsafe"

"github.com/VictoriaMetrics/easyproto"
"github.com/prometheus/prometheus/model/labels"
)

type StringLabelsBuilder struct {
buf []byte
}

func EncodeStringSize(stringLen int) int {
return sizeVarint(uint64(stringLen)) + stringLen
}

func labelSize(m *Label) (n int) {
// strings are encoded as length followed by contents.
l := len(m.Name)
n += l + sizeVarint(uint64(l))
l = len(m.Value)
n += l + sizeVarint(uint64(l))
return n
}

func encodeSize(data []byte, offset, v int) int {
if v < 1<<7 {
offset--
data[offset] = uint8(v)
return offset
}
return encodeVarint(data, offset, uint64(v))
}

func sizeVarint(x uint64) (n int) {
// Most common case first
if x < 1<<7 {
return 1
}
if x >= 1<<56 {
return 9
}
if x >= 1<<28 {
x >>= 28
n = 4
}
if x >= 1<<14 {
x >>= 14
n += 2
}
if x >= 1<<7 {
n++
}
return n + 1
}

func encodeVarint(data []byte, offset int, v uint64) int {
offset -= sizeVarint(v)
base := offset
for v >= 1<<7 {
data[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
data[offset] = uint8(v)
return base
}

func NewStringsLabelsBuilder(size int) *StringLabelsBuilder {
return &StringLabelsBuilder{
buf: make([]byte, size),
}
}

func (b *StringLabelsBuilder) Labels() labels.Labels {
return *(*labels.Labels)(unsafe.Pointer(&struct {
data string
}{
data: unsafe.String(unsafe.SliceData(b.buf), len(b.buf)),
}))
}

// GetLabelsBufferSize returns how many bytes you will need
// for labels.Labels allocation. fieldNumLabels must be
// repeated Labels foo = fieldNumLabels; (in proto file).
func GetLabelsBufferSize(src []byte, fieldNumLabels uint32) (int, error) {
var (
fc easyproto.FieldContext
size int
err error
)

for len(src) > 0 {
src, err = fc.NextField(src)
if err != nil {
return 0, fmt.Errorf("cannot read next field in given byte slice: %w", err)
}

switch fc.FieldNum {
case fieldNumLabels:
data, ok := fc.MessageData()
if !ok {
return 0, fmt.Errorf("cannot read message data")
}
sz, err := getLabelEncodingProtobufSize(data)
if err != nil {
return 0, fmt.Errorf("getting label encoding size: %w", err)
}
size += sz
}
}
return size, nil
}

func getLabelEncodingProtobufSize(src []byte) (int, error) {
var (
fc easyproto.FieldContext
err error
n int
)
for len(src) > 0 {
src, err = fc.NextField(src)
if err != nil {
return 0, fmt.Errorf("cannot read next field in labels")
}
if fc.FieldNum > 2 {
return 0, fmt.Errorf("unexpected field number in labels")
}
val, ok := fc.String()
if !ok {
return 0, fmt.Errorf("cannot read string value in labels")
}

n += EncodeStringSize(len(val))
}
return n, nil
}

func (b *StringLabelsBuilder) Add(name, value string) {
i := len(b.buf)
i -= len(value)
copy(b.buf[i:], value)
i = encodeSize(b.buf, i, len(value))

i -= len(name)
copy(b.buf[i:], name)
encodeSize(b.buf, i, len(name))
}

func init() {
b := NewStringsLabelsBuilder(labelSize(&Label{
Name: "name",
Value: "value",
}))

b.Add("name", "value")

lbls := b.Labels()

if lbls.String() != `{name="value"}` {
panic("only stringlabels build is supported in Thanos")
}
}
20 changes: 20 additions & 0 deletions pkg/store/labelpb/stringlabels_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package labelpb

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestStringLabelsBuilder(t *testing.T) {
b := NewStringsLabelsBuilder(labelSize(&Label{
Name: "name",
Value: "value",
}))

b.Add("name", "value")

lbls := b.Labels()

require.Equal(t, `{name="value"}`, lbls.String())
}
22 changes: 22 additions & 0 deletions pkg/store/storepb/custom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,28 @@ import (
"github.com/thanos-io/thanos/pkg/store/labelpb"
)

func TestGetLabelsSize(t *testing.T) {
s := &Series{
Labels: labelpb.PromLabelsToLabelpbLabels(labels.FromStrings("a", "b")),
Chunks: []*AggrChunk{
{
MinTime: 1,
MaxTime: 2,
Raw: &Chunk{Type: Chunk_XOR, Data: []byte{1, 2, 3}},
},
},
}

in, err := s.MarshalVT()
testutil.Ok(t, err)

sz, err := labelpb.GetLabelsBufferSize(in, 1)
testutil.Ok(t, err)

// 2 sizes, 2 values (a, b).
testutil.Equals(t, 4, sz)
}

type sample struct {
t int64
v float64
Expand Down
3 changes: 3 additions & 0 deletions pkg/store/storepb/types.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions pkg/store/storepb/types_vtproto.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e63e87d

Please sign in to comment.