Skip to content

Commit ae90430

Browse files
authored
[client] Fix race condition in Metadata.Get (#9595)
**Description:** Fixes a bug where `Get` was not thread safe. **Link to tracking Issue:** <Issue number if applicable> Closes #9594 **Testing:** <Describe what testing was performed and which tests were added.> Tested locally
1 parent 4430ca9 commit ae90430

File tree

3 files changed

+44
-17
lines changed

3 files changed

+44
-17
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: bug_fix
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
7+
component: client
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Make `Metadata.Get` thread safe
11+
12+
# One or more tracking issues or pull requests related to the change
13+
issues: [9595]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# Optional: The change log or logs in which this entry should be included.
21+
# e.g. '[user]' or '[user, api]'
22+
# Include 'user' if the change is relevant to end users.
23+
# Include 'api' if there is a change to a library API.
24+
# Default: '[user]'
25+
change_logs: []

client/client.go

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -141,28 +141,24 @@ func FromContext(ctx context.Context) Info {
141141

142142
// NewMetadata creates a new Metadata object to use in Info. md is used as-is.
143143
func NewMetadata(md map[string][]string) Metadata {
144+
c := make(map[string][]string, len(md))
145+
for k, v := range md {
146+
c[strings.ToLower(k)] = v
147+
}
144148
return Metadata{
145-
data: md,
149+
data: c,
146150
}
147151
}
148152

149153
// Get gets the value of the key from metadata, returning a copy.
150154
func (m Metadata) Get(key string) []string {
151-
vals := m.data[key]
155+
if len(m.data) == 0 {
156+
return nil
157+
}
158+
159+
vals := m.data[strings.ToLower(key)]
152160
if len(vals) == 0 {
153-
// we didn't find the key, but perhaps it just has different cases?
154-
for k, v := range m.data {
155-
if strings.EqualFold(key, k) {
156-
vals = v
157-
// we optimize for the next lookup
158-
m.data[key] = v
159-
}
160-
}
161-
162-
// if it's still not found, it's really not here
163-
if len(vals) == 0 {
164-
return nil
165-
}
161+
return nil
166162
}
167163

168164
ret := make([]string, len(vals))

client/client_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,11 @@ func TestFromContext(t *testing.T) {
7777
}
7878

7979
func TestMetadata(t *testing.T) {
80-
source := map[string][]string{"test-key": {"test-val"}}
80+
source := map[string][]string{"test-key": {"test-val"}, "TEST-KEY-2": {"test-val"}}
8181
md := NewMetadata(source)
8282
assert.Equal(t, []string{"test-val"}, md.Get("test-key"))
83-
assert.Equal(t, []string{"test-val"}, md.Get("test-KEY")) // case insensitive lookup
83+
assert.Equal(t, []string{"test-val"}, md.Get("test-KEY")) // case insensitive lookup
84+
assert.Equal(t, []string{"test-val"}, md.Get("test-key-2")) // case insensitive lookup
8485

8586
// test if copy. In regular use, source cannot change
8687
val := md.Get("test-key")
@@ -89,3 +90,8 @@ func TestMetadata(t *testing.T) {
8990

9091
assert.Empty(t, md.Get("non-existent-key"))
9192
}
93+
94+
func TestUninstantiatedMetadata(t *testing.T) {
95+
i := Info{}
96+
assert.Empty(t, i.Metadata.Get("test"))
97+
}

0 commit comments

Comments
 (0)