Skip to content

Commit ba6cd70

Browse files
authored
Deprecate PutAttribute and introduce SetAttribute (#14041)
Part of #14016.
1 parent 5a0883d commit ba6cd70

File tree

7 files changed

+291
-5
lines changed

7 files changed

+291
-5
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: deprecation
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
7+
component: pdata/pprofile
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Deprecated `PutAttribute` helper method
11+
12+
# One or more tracking issues or pull requests related to the change
13+
issues: [14016, 14041]
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: [api]
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: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
7+
component: pdata/pprofile
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Introduce `Equal` method on the `KeyValueAndUnit` type
11+
12+
# One or more tracking issues or pull requests related to the change
13+
issues: [14041]
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: [api]
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: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
7+
component: pdata/pprofile
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Introduce `SetAttribute` helper method
11+
12+
# One or more tracking issues or pull requests related to the change
13+
issues: [14016, 14041]
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: [api]

pdata/pprofile/attributes.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ func FromAttributeIndices(table KeyValueAndUnitSlice, record attributable, dic P
3232
return m
3333
}
3434

35-
var errTooManyTableEntries = errors.New("too many entries in AttributeTable")
35+
var errTooManyAttributeTableEntries = errors.New("too many entries in AttributeTable")
3636

3737
// PutAttribute updates an AttributeTable and a record's AttributeIndices to
3838
// add or update an attribute.
3939
// The assumption is that attributes are a map as for other signals (metrics, logs, etc.), thus
4040
// the same key must not appear twice in a list of attributes / attribute indices.
4141
// The record can be any struct that implements an `AttributeIndices` method.
42+
//
43+
// Deprecated: [v0.138.0] use SetAttribute instead.
4244
func PutAttribute(table KeyValueAndUnitSlice, record attributable, dic ProfilesDictionary, key string, value pcommon.Value) error {
4345
for i := range record.AttributeIndices().Len() {
4446
idx := int(record.AttributeIndices().At(i))
@@ -58,15 +60,15 @@ func PutAttribute(table KeyValueAndUnitSlice, record attributable, dic ProfilesD
5860
a := table.At(j)
5961
if dic.StringTable().At(int(a.KeyStrindex())) == key && a.Value().Equal(value) {
6062
if j > math.MaxInt32 {
61-
return errTooManyTableEntries
63+
return errTooManyAttributeTableEntries
6264
}
6365
record.AttributeIndices().SetAt(i, int32(j)) //nolint:gosec // overflow checked
6466
return nil
6567
}
6668
}
6769

6870
if table.Len() >= math.MaxInt32 {
69-
return errTooManyTableEntries
71+
return errTooManyAttributeTableEntries
7072
}
7173

7274
// Find the key in the StringTable, or add it
@@ -94,7 +96,7 @@ func PutAttribute(table KeyValueAndUnitSlice, record attributable, dic ProfilesD
9496
a := table.At(j)
9597
if dic.StringTable().At(int(a.KeyStrindex())) == key && a.Value().Equal(value) {
9698
if j > math.MaxInt32 {
97-
return errTooManyTableEntries
99+
return errTooManyAttributeTableEntries
98100
}
99101
// Add the index of the existing attribute to the indices.
100102
record.AttributeIndices().Append(int32(j)) //nolint:gosec // overflow checked
@@ -103,7 +105,7 @@ func PutAttribute(table KeyValueAndUnitSlice, record attributable, dic ProfilesD
103105
}
104106

105107
if table.Len() >= math.MaxInt32 {
106-
return errTooManyTableEntries
108+
return errTooManyAttributeTableEntries
107109
}
108110

109111
// Find the key in the StringTable, or add it
@@ -121,3 +123,23 @@ func PutAttribute(table KeyValueAndUnitSlice, record attributable, dic ProfilesD
121123
record.AttributeIndices().Append(int32(table.Len() - 1)) //nolint:gosec // overflow checked
122124
return nil
123125
}
126+
127+
// SetAttribute updates an AttributeTable, adding or providing a value and
128+
// returns its index.
129+
func SetAttribute(table KeyValueAndUnitSlice, attr KeyValueAndUnit) (int32, error) {
130+
for j, a := range table.All() {
131+
if a.Equal(attr) {
132+
if j > math.MaxInt32 {
133+
return 0, errTooManyAttributeTableEntries
134+
}
135+
return int32(j), nil //nolint:gosec // G115 overflow checked
136+
}
137+
}
138+
139+
if table.Len() >= math.MaxInt32 {
140+
return 0, errTooManyAttributeTableEntries
141+
}
142+
143+
attr.CopyTo(table.AppendEmpty())
144+
return int32(table.Len() - 1), nil //nolint:gosec // G115 overflow checked
145+
}

pdata/pprofile/attributes_test.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,112 @@ func BenchmarkPutAttribute(b *testing.B) {
226226
})
227227
}
228228
}
229+
230+
func TestSetAttribute(t *testing.T) {
231+
table := NewKeyValueAndUnitSlice()
232+
attr := NewKeyValueAndUnit()
233+
attr.SetKeyStrindex(1)
234+
attr.SetUnitStrindex(2)
235+
require.NoError(t, attr.Value().FromRaw("test"))
236+
attr2 := NewKeyValueAndUnit()
237+
attr2.SetKeyStrindex(3)
238+
attr2.SetUnitStrindex(4)
239+
require.NoError(t, attr.Value().FromRaw("test2"))
240+
241+
// Put a first value
242+
idx, err := SetAttribute(table, attr)
243+
require.NoError(t, err)
244+
assert.Equal(t, 1, table.Len())
245+
assert.Equal(t, int32(0), idx)
246+
247+
// Put the same attribute
248+
// This should be a no-op.
249+
idx, err = SetAttribute(table, attr)
250+
require.NoError(t, err)
251+
assert.Equal(t, 1, table.Len())
252+
assert.Equal(t, int32(0), idx)
253+
254+
// Set a new value
255+
// This sets the index and adds to the table.
256+
idx, err = SetAttribute(table, attr2)
257+
require.NoError(t, err)
258+
assert.Equal(t, 2, table.Len())
259+
assert.Equal(t, int32(table.Len()-1), idx) //nolint:gosec // G115
260+
261+
// Set an existing value
262+
idx, err = SetAttribute(table, attr)
263+
require.NoError(t, err)
264+
assert.Equal(t, 2, table.Len())
265+
assert.Equal(t, int32(0), idx)
266+
// Set another existing value
267+
idx, err = SetAttribute(table, attr2)
268+
require.NoError(t, err)
269+
assert.Equal(t, 2, table.Len())
270+
assert.Equal(t, int32(table.Len()-1), idx) //nolint:gosec // G115
271+
}
272+
273+
func BenchmarkSetAttribute(b *testing.B) {
274+
for _, bb := range []struct {
275+
name string
276+
attr KeyValueAndUnit
277+
278+
runBefore func(*testing.B, KeyValueAndUnitSlice)
279+
}{
280+
{
281+
name: "with a new attribute",
282+
attr: NewKeyValueAndUnit(),
283+
},
284+
{
285+
name: "with an existing attribute",
286+
attr: func() KeyValueAndUnit {
287+
a := NewKeyValueAndUnit()
288+
a.SetKeyStrindex(1)
289+
return a
290+
}(),
291+
292+
runBefore: func(_ *testing.B, table KeyValueAndUnitSlice) {
293+
a := table.AppendEmpty()
294+
a.SetKeyStrindex(1)
295+
},
296+
},
297+
{
298+
name: "with a duplicate attribute",
299+
attr: NewKeyValueAndUnit(),
300+
301+
runBefore: func(_ *testing.B, table KeyValueAndUnitSlice) {
302+
_, err := SetAttribute(table, NewKeyValueAndUnit())
303+
require.NoError(b, err)
304+
},
305+
},
306+
{
307+
name: "with a hundred locations to loop through",
308+
attr: func() KeyValueAndUnit {
309+
a := NewKeyValueAndUnit()
310+
a.SetKeyStrindex(1)
311+
return a
312+
}(),
313+
314+
runBefore: func(_ *testing.B, table KeyValueAndUnitSlice) {
315+
for i := range 100 {
316+
l := table.AppendEmpty()
317+
l.SetKeyStrindex(int32(i)) //nolint:gosec // overflow checked
318+
}
319+
},
320+
},
321+
} {
322+
b.Run(bb.name, func(b *testing.B) {
323+
table := NewKeyValueAndUnitSlice()
324+
325+
if bb.runBefore != nil {
326+
bb.runBefore(b, table)
327+
}
328+
329+
b.ResetTimer()
330+
b.ReportAllocs()
331+
332+
for b.Loop() {
333+
_, _ = SetAttribute(table, bb.attr)
334+
}
335+
})
336+
}
337+
}

pdata/pprofile/keyvalueandunit.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package pprofile // import "go.opentelemetry.io/collector/pdata/pprofile"
5+
6+
// Equal checks equality with another KeyValueAndUnit
7+
// It assumes both structs refer to the same dictionary.
8+
func (ms KeyValueAndUnit) Equal(val KeyValueAndUnit) bool {
9+
return ms.KeyStrindex() == val.KeyStrindex() &&
10+
ms.UnitStrindex() == val.UnitStrindex() &&
11+
ms.Value().Equal(val.Value())
12+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package pprofile
5+
6+
import (
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
11+
"go.opentelemetry.io/collector/pdata/pcommon"
12+
)
13+
14+
func TestKeyValueAndUnitEqual(t *testing.T) {
15+
for _, tt := range []struct {
16+
name string
17+
orig KeyValueAndUnit
18+
dest KeyValueAndUnit
19+
want bool
20+
}{
21+
{
22+
name: "empty keyvalueandunit",
23+
orig: NewKeyValueAndUnit(),
24+
dest: NewKeyValueAndUnit(),
25+
want: true,
26+
},
27+
{
28+
name: "non-empty identical keyvalueandunit",
29+
orig: buildKeyValueAndUnit(1, 2, pcommon.NewValueStr("test")),
30+
dest: buildKeyValueAndUnit(1, 2, pcommon.NewValueStr("test")),
31+
want: true,
32+
},
33+
{
34+
name: "with different key index",
35+
orig: buildKeyValueAndUnit(1, 2, pcommon.NewValueStr("test")),
36+
dest: buildKeyValueAndUnit(2, 2, pcommon.NewValueStr("test")),
37+
want: false,
38+
},
39+
{
40+
name: "with different unit index",
41+
orig: buildKeyValueAndUnit(1, 2, pcommon.NewValueStr("test")),
42+
dest: buildKeyValueAndUnit(1, 3, pcommon.NewValueStr("test")),
43+
want: false,
44+
},
45+
{
46+
name: "with different value",
47+
orig: buildKeyValueAndUnit(1, 2, pcommon.NewValueStr("test")),
48+
dest: buildKeyValueAndUnit(1, 2, pcommon.NewValueStr("hello")),
49+
want: false,
50+
},
51+
} {
52+
t.Run(tt.name, func(t *testing.T) {
53+
if tt.want {
54+
assert.True(t, tt.orig.Equal(tt.dest))
55+
} else {
56+
assert.False(t, tt.orig.Equal(tt.dest))
57+
}
58+
})
59+
}
60+
}
61+
62+
func buildKeyValueAndUnit(keyIdx, unitIdx int32, val pcommon.Value) KeyValueAndUnit {
63+
kvu := NewKeyValueAndUnit()
64+
kvu.SetKeyStrindex(keyIdx)
65+
kvu.SetUnitStrindex(unitIdx)
66+
val.CopyTo(kvu.Value())
67+
return kvu
68+
}

0 commit comments

Comments
 (0)