Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .chloggen/profiles-PutAttributes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
component: pdata/profile

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Replace AddAttribute with the PutAttribute helper method to modify the content of attributable records.

# One or more tracking issues or pull requests related to the change
issues: [12798]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [api]
48 changes: 30 additions & 18 deletions pdata/pprofile/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import (
"errors"
"fmt"
"math"

"go.opentelemetry.io/collector/pdata/pcommon"
Expand All @@ -31,38 +30,51 @@
return m
}

// AddAttribute updates an AttributeTable and a record's AttributeIndices to
// PutAttribute updates an AttributeTable and a record's AttributeIndices to
// add a new attribute.
// The record can by any struct that implements an `AttributeIndices` method.
func AddAttribute(table AttributeTableSlice, record attributable, key string, value pcommon.Value) error {
for i := range table.Len() {
a := table.At(i)
// The assumption is that attributes are a map as for other signals (metrics, logs, etc.), thus
// the same key must not appear twice in a list of attributes / attribute indices.
// The record can be any struct that implements an `AttributeIndices` method.
func PutAttribute(table AttributeTableSlice, record attributable, key string, value pcommon.Value) error {
if record.AttributeIndices().Len() >= math.MaxInt32-1 {
return errors.New("AttributeTable can't take more attributes")
}

Check warning on line 41 in pdata/pprofile/attributes.go

View check run for this annotation

Codecov / codecov/patch

pdata/pprofile/attributes.go#L40-L41

Added lines #L40 - L41 were not covered by tests

if a.Key() == key && a.Value().Equal(value) {
if i >= math.MaxInt32 {
return fmt.Errorf("Attribute %s=%#v has too high an index to be added to AttributeIndices", key, value)
for i := range record.AttributeIndices().Len() {
idx := record.AttributeIndices().At(i)
attr := table.At(int(idx))
if attr.Key() == key {
if attr.Value().Equal(value) {
// Attribute already exists, nothing to do.
return nil
}

for j := range record.AttributeIndices().Len() {
v := record.AttributeIndices().At(j)
if v == int32(i) { //nolint:gosec // overflow checked
// If the attribute table already contains the key/value pair, just update the index.
for j := range table.Len() {
a := table.At(j)
if a.Key() == key && a.Value().Equal(value) {
record.AttributeIndices().SetAt(i, int32(j)) //nolint:gosec // overflow checked
return nil
}
}

record.AttributeIndices().Append(int32(i)) //nolint:gosec // overflow checked
// Add the key/value pair as a new attribute to the table...
entry := table.AppendEmpty()
entry.SetKey(key)
value.CopyTo(entry.Value())

// ...and update the existing index.
record.AttributeIndices().SetAt(i, int32(table.Len()-1)) //nolint:gosec // overflow checked
return nil
}
}

if table.Len() >= math.MaxInt32 {
return errors.New("AttributeTable can't take more attributes")
}
table.EnsureCapacity(table.Len() + 1)
// Add the key/value pair as a new attribute to the table...
entry := table.AppendEmpty()
entry.SetKey(key)
value.CopyTo(entry.Value())
record.AttributeIndices().Append(int32(table.Len()) - 1) //nolint:gosec // overflow checked

// ...and add a new index to the indices.
record.AttributeIndices().Append(int32(table.Len() - 1)) //nolint:gosec // overflow checked
return nil
}
78 changes: 53 additions & 25 deletions pdata/pprofile/attributes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,34 +44,62 @@ func TestFromAttributeIndices(t *testing.T) {
assert.Equal(t, attrs.AsRaw(), m)
}

func TestAddAttribute(t *testing.T) {
func testPutAttribute(t *testing.T, record attributable) {
table := NewAttributeTableSlice()
att := table.AppendEmpty()
att.SetKey("hello")
att.Value().SetStr("world")

// Add a brand new attribute
loc := NewLocation()
err := AddAttribute(table, loc, "bonjour", pcommon.NewValueStr("monde"))
require.NoError(t, err)

// Put a first attribute.
require.NoError(t, PutAttribute(table, record, "hello", pcommon.NewValueStr("world")))
assert.Equal(t, 1, table.Len())
assert.Equal(t, []int32{0}, record.AttributeIndices().AsRaw())

// Put an attribute, same key, same value.
// This should be a no-op.
require.NoError(t, PutAttribute(table, record, "hello", pcommon.NewValueStr("world")))
assert.Equal(t, 1, table.Len())
assert.Equal(t, []int32{0}, record.AttributeIndices().AsRaw())

// Put an attribute, same key, different value.
// This updates the index and adds to the table.
fmt.Printf("test\n")
require.NoError(t, PutAttribute(table, record, "hello", pcommon.NewValueStr("world2")))
assert.Equal(t, 2, table.Len())
assert.Equal(t, []int32{1}, loc.AttributeIndices().AsRaw())

// Add an already existing attribute
mapp := NewMapping()
err = AddAttribute(table, mapp, "hello", pcommon.NewValueStr("world"))
require.NoError(t, err)
assert.Equal(t, []int32{1}, record.AttributeIndices().AsRaw())

// Put an attribute that already exists in the table.
// This updates the index and does not add to the table.
require.NoError(t, PutAttribute(table, record, "hello", pcommon.NewValueStr("world")))
assert.Equal(t, 2, table.Len())
assert.Equal(t, []int32{0}, mapp.AttributeIndices().AsRaw())

// Add a duplicate attribute
err = AddAttribute(table, mapp, "hello", pcommon.NewValueStr("world"))
require.NoError(t, err)
assert.Equal(t, []int32{0}, record.AttributeIndices().AsRaw())

// Put a new attribute.
// This adds an index and adds to the table.
require.NoError(t, PutAttribute(table, record, "good", pcommon.NewValueStr("day")))
assert.Equal(t, 3, table.Len())
assert.Equal(t, []int32{0, 2}, record.AttributeIndices().AsRaw())

// Add multiple distinct attributes.
for i := range 100 {
require.NoError(t, PutAttribute(table, record, fmt.Sprintf("key_%d", i), pcommon.NewValueStr("day")))
assert.Equal(t, i+4, table.Len())
assert.Equal(t, i+3, record.AttributeIndices().Len())
}
}

assert.Equal(t, 2, table.Len())
assert.Equal(t, []int32{0}, mapp.AttributeIndices().AsRaw())
func TestPutAttribute(t *testing.T) {
// Test every existing record type.
for _, tt := range []struct {
name string
attr attributable
}{
{"Profile", NewProfile()},
{"Sample", NewSample()},
{"Mapping", NewMapping()},
{"Location", NewLocation()},
} {
t.Run(tt.name, func(t *testing.T) {
testPutAttribute(t, tt.attr)
})
}
}

func BenchmarkFromAttributeIndices(b *testing.B) {
Expand All @@ -94,7 +122,7 @@ func BenchmarkFromAttributeIndices(b *testing.B) {
}
}

func BenchmarkAddAttribute(b *testing.B) {
func BenchmarkPutAttribute(b *testing.B) {
for _, bb := range []struct {
name string
key string
Expand Down Expand Up @@ -124,7 +152,7 @@ func BenchmarkAddAttribute(b *testing.B) {
value: pcommon.NewValueStr("test"),

runBefore: func(_ *testing.B, table AttributeTableSlice, obj attributable) {
require.NoError(b, AddAttribute(table, obj, "attribute", pcommon.NewValueStr("test")))
require.NoError(b, PutAttribute(table, obj, "attribute", pcommon.NewValueStr("test")))
},
},
{
Expand Down Expand Up @@ -157,7 +185,7 @@ func BenchmarkAddAttribute(b *testing.B) {
b.ReportAllocs()

for n := 0; n < b.N; n++ {
_ = AddAttribute(table, obj, bb.key, bb.value)
_ = PutAttribute(table, obj, bb.key, bb.value)
}
})
}
Expand Down
Loading