Skip to content

Commit 58245f3

Browse files
committed
Introduce pprofile.PutAttribute helper
1 parent 564818f commit 58245f3

File tree

3 files changed

+194
-0
lines changed

3 files changed

+194
-0
lines changed

.chloggen/profiles-PutAttributes.yaml

+25
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/profile
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Introduce PutAttribute helper method to modify the content of attributable records.
11+
12+
# One or more tracking issues or pull requests related to the change
13+
issues: []
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

+58
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,61 @@ func AddAttribute(table AttributeTableSlice, record attributable, key string, va
6666

6767
return nil
6868
}
69+
70+
// PutAttribute updates an AttributeTable and a record's AttributeIndices to
71+
// add a new attribute.
72+
// The assumption is that attributes are a map as for other signals (metrics, logs, etc.), thus
73+
// the same key must not appear twice in a list of attributes / attribute indices.
74+
// The record can be any struct that implements an `AttributeIndices` method.
75+
func PutAttribute(table AttributeTableSlice, record attributable, key string, value pcommon.Value) error {
76+
for i := range record.AttributeIndices().Len() {
77+
idx := record.AttributeIndices().At(i)
78+
attr := table.At(int(idx))
79+
if attr.Key() == key {
80+
if attr.Value().Equal(value) {
81+
// Attribute already exists, nothing to do.
82+
return nil
83+
}
84+
85+
// Attribute exists, but the value is different, so update it.
86+
87+
// If the attribute table already contains the key/value pair, just update the index.
88+
for j := range table.Len() {
89+
a := table.At(j)
90+
if a.Key() == key && a.Value().Equal(value) {
91+
if j >= math.MaxInt32 {
92+
return fmt.Errorf("attribute %s=%#v has too high an index %d to be added to AttributeIndices", key, value, j)
93+
}
94+
record.AttributeIndices().SetAt(i, int32(j)) //nolint:gosec // overflow checked
95+
return nil
96+
}
97+
}
98+
99+
// Add the key/value pair as a new attribute to the table...
100+
entry := table.AppendEmpty()
101+
entry.SetKey(key)
102+
value.CopyTo(entry.Value())
103+
104+
if table.Len() >= math.MaxInt32 {
105+
return errors.New("AttributeTable can't take more attributes")
106+
}
107+
108+
// ...and update the index.
109+
record.AttributeIndices().SetAt(i, int32(table.Len()-1)) //nolint:gosec // overflow checked
110+
return nil
111+
}
112+
}
113+
114+
// Add the key/value pair as a new attribute to the table...
115+
entry := table.AppendEmpty()
116+
entry.SetKey(key)
117+
value.CopyTo(entry.Value())
118+
119+
if table.Len() >= math.MaxInt32 {
120+
return errors.New("AttributeTable can't take more attributes")
121+
}
122+
123+
// ...and add to the index.
124+
record.AttributeIndices().Append(int32(table.Len() - 1)) //nolint:gosec // overflow checked
125+
return nil
126+
}

pdata/pprofile/attributes_test.go

+111
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,48 @@ func TestAddAttribute(t *testing.T) {
7474
assert.Equal(t, []int32{0}, mapp.AttributeIndices().AsRaw())
7575
}
7676

77+
func TestPutAttribute(t *testing.T) {
78+
table := NewAttributeTableSlice()
79+
indices := NewProfile()
80+
81+
// Put a first attribute.
82+
require.NoError(t, PutAttribute(table, indices, "hello", pcommon.NewValueStr("world")))
83+
assert.Equal(t, 1, table.Len())
84+
assert.Equal(t, []int32{0}, indices.AttributeIndices().AsRaw())
85+
86+
// Put an attribute, same key, same value.
87+
// This should be a no-op.
88+
require.NoError(t, PutAttribute(table, indices, "hello", pcommon.NewValueStr("world")))
89+
assert.Equal(t, 1, table.Len())
90+
assert.Equal(t, []int32{0}, indices.AttributeIndices().AsRaw())
91+
92+
// Put an attribute, same key, different value.
93+
// This updates the index and adds to the table.
94+
fmt.Printf("test\n")
95+
require.NoError(t, PutAttribute(table, indices, "hello", pcommon.NewValueStr("world2")))
96+
assert.Equal(t, 2, table.Len())
97+
assert.Equal(t, []int32{1}, indices.AttributeIndices().AsRaw())
98+
99+
// Put an attribute that already exists in the table.
100+
// This updates the index and does not add to the table.
101+
require.NoError(t, PutAttribute(table, indices, "hello", pcommon.NewValueStr("world")))
102+
assert.Equal(t, 2, table.Len())
103+
assert.Equal(t, []int32{0}, indices.AttributeIndices().AsRaw())
104+
105+
// Put a new attribute.
106+
// This adds an index and adds to the table.
107+
require.NoError(t, PutAttribute(table, indices, "good", pcommon.NewValueStr("day")))
108+
assert.Equal(t, 3, table.Len())
109+
assert.Equal(t, []int32{0, 2}, indices.AttributeIndices().AsRaw())
110+
111+
// Add multiple distinct attributes.
112+
for i := range 100 {
113+
require.NoError(t, PutAttribute(table, indices, fmt.Sprintf("key_%d", i), pcommon.NewValueStr("day")))
114+
assert.Equal(t, i+4, table.Len())
115+
assert.Equal(t, i+3, indices.AttributeIndices().Len())
116+
}
117+
}
118+
77119
func BenchmarkFromAttributeIndices(b *testing.B) {
78120
table := NewAttributeTableSlice()
79121

@@ -162,3 +204,72 @@ func BenchmarkAddAttribute(b *testing.B) {
162204
})
163205
}
164206
}
207+
208+
func BenchmarkPutAttribute(b *testing.B) {
209+
for _, bb := range []struct {
210+
name string
211+
key string
212+
value pcommon.Value
213+
214+
runBefore func(*testing.B, AttributeTableSlice, attributable)
215+
}{
216+
{
217+
name: "with a new string attribute",
218+
key: "attribute",
219+
value: pcommon.NewValueStr("test"),
220+
},
221+
{
222+
name: "with an existing attribute",
223+
key: "attribute",
224+
value: pcommon.NewValueStr("test"),
225+
226+
runBefore: func(_ *testing.B, table AttributeTableSlice, _ attributable) {
227+
entry := table.AppendEmpty()
228+
entry.SetKey("attribute")
229+
entry.Value().SetStr("test")
230+
},
231+
},
232+
{
233+
name: "with a duplicate attribute",
234+
key: "attribute",
235+
value: pcommon.NewValueStr("test"),
236+
237+
runBefore: func(_ *testing.B, table AttributeTableSlice, obj attributable) {
238+
require.NoError(b, PutAttribute(table, obj, "attribute", pcommon.NewValueStr("test")))
239+
},
240+
},
241+
{
242+
name: "with a hundred attributes to loop through",
243+
key: "attribute",
244+
value: pcommon.NewValueStr("test"),
245+
246+
runBefore: func(_ *testing.B, table AttributeTableSlice, _ attributable) {
247+
for i := range 100 {
248+
entry := table.AppendEmpty()
249+
entry.SetKey(fmt.Sprintf("attr_%d", i))
250+
entry.Value().SetStr("test")
251+
}
252+
253+
entry := table.AppendEmpty()
254+
entry.SetKey("attribute")
255+
entry.Value().SetStr("test")
256+
},
257+
},
258+
} {
259+
b.Run(bb.name, func(b *testing.B) {
260+
table := NewAttributeTableSlice()
261+
obj := NewLocation()
262+
263+
if bb.runBefore != nil {
264+
bb.runBefore(b, table, obj)
265+
}
266+
267+
b.ResetTimer()
268+
b.ReportAllocs()
269+
270+
for n := 0; n < b.N; n++ {
271+
_ = PutAttribute(table, obj, bb.key, bb.value)
272+
}
273+
})
274+
}
275+
}

0 commit comments

Comments
 (0)