Skip to content

Commit 88f7446

Browse files
authored
Merge pull request #345 from cloudflare/ivan/mutex
Protect decoder cache with a mutex
2 parents bd3ea84 + 716b8e4 commit 88f7446

File tree

2 files changed

+62
-5
lines changed

2 files changed

+62
-5
lines changed

decoder/decoder.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,16 @@ func NewSet() (*Set, error) {
6161
}, nil
6262
}
6363

64-
// Decode transforms input byte field into a string according to configuration
65-
func (s *Set) Decode(in []byte, label config.Label) ([]byte, error) {
64+
// decode transforms input byte field into a string according to configuration
65+
func (s *Set) decode(in []byte, label config.Label) ([]byte, error) {
6666
result := in
6767

6868
for _, decoder := range label.Decoders {
6969
if _, ok := s.decoders[decoder.Name]; !ok {
7070
return result, fmt.Errorf("unknown decoder %q", decoder.Name)
7171
}
7272

73-
s.mu.Lock()
7473
decoded, err := s.decoders[decoder.Name].Decode(result, decoder)
75-
s.mu.Unlock()
7674
if err != nil {
7775
if err == ErrSkipLabelSet {
7876
return decoded, err
@@ -89,6 +87,9 @@ func (s *Set) Decode(in []byte, label config.Label) ([]byte, error) {
8987
// DecodeLabels transforms eBPF map key bytes into a list of label values
9088
// according to configuration
9189
func (s *Set) DecodeLabels(in []byte, labels []config.Label) ([]string, error) {
90+
s.mu.Lock()
91+
defer s.mu.Unlock()
92+
9293
// string(in) must not be a variable to avoid allocation:
9394
// * https://github.com/golang/go/commit/f5f5a8b6209f8
9495
if cached, ok := s.cache[string(in)]; ok {
@@ -132,7 +133,7 @@ func (s *Set) decodeLabels(in []byte, labels []config.Label) ([]string, error) {
132133

133134
size := label.Size
134135

135-
decoded, err := s.Decode(in[off:off+size], label)
136+
decoded, err := s.decode(in[off:off+size], label)
136137
if err != nil {
137138
return nil, err
138139
}

decoder/decoder_test.go

+56
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package decoder
22

33
import (
44
"fmt"
5+
"sync"
56
"testing"
67

78
"github.com/cloudflare/ebpf_exporter/v2/config"
@@ -151,6 +152,61 @@ func TestDecodeLabels(t *testing.T) {
151152
}
152153
}
153154

155+
func TestConcurrency(t *testing.T) {
156+
in := append([]byte{0x8, 0x0, 0x0, 0x0}, zeroPaddedString("bananas", 32)...)
157+
158+
labels := []config.Label{
159+
{
160+
Name: "number",
161+
Size: 4,
162+
Decoders: []config.Decoder{
163+
{
164+
Name: "uint",
165+
},
166+
},
167+
},
168+
{
169+
Name: "fruit",
170+
Size: 32,
171+
Decoders: []config.Decoder{
172+
{
173+
Name: "string",
174+
},
175+
{
176+
Name: "regexp",
177+
Regexps: []string{
178+
"^bananas$",
179+
"$is-banana-even-fruit$",
180+
},
181+
},
182+
},
183+
},
184+
}
185+
186+
s, err := NewSet()
187+
if err != nil {
188+
t.Fatal(err)
189+
}
190+
191+
count := 1000
192+
193+
wg := sync.WaitGroup{}
194+
wg.Add(count)
195+
196+
for i := 0; i < count; i++ {
197+
go func() {
198+
defer wg.Done()
199+
200+
_, err := s.DecodeLabels(in, labels)
201+
if err != nil {
202+
t.Error(err)
203+
}
204+
}()
205+
}
206+
207+
wg.Wait()
208+
}
209+
154210
func BenchmarkCache(b *testing.B) {
155211
in := []byte{
156212
0x8, 0xab, 0xce, 0xef,

0 commit comments

Comments
 (0)