1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2026-06-03 18:35:08 +02:00
Files
opentelemetry-go/metric/instrument_test.go
T
David Ashpole 48dd8b1a09 Add x.Settable to allow reusing attribute options (#8178)
Fixes https://github.com/open-telemetry/opentelemetry-go/issues/7851

This adopts this proposal from @MrAlias:
https://github.com/open-telemetry/opentelemetry-go/issues/7851#issuecomment-3837399805

This adds a Set function to attrOpt, and changes attrOpt functions to
use a pointer receiver. Users can use this by checking if the attribute
option implements the `Settable` interface in `metric/x`. There are no
public API changes to the metric package, as this is still an
experiment. See the benchmark change and the interface documentation for
how it can be used to avoid calling metric.WithAttributeSet.

I updated the benchmark in
https://github.com/open-telemetry/opentelemetry-go/commit/448394b549375f4f6742201e199e0339d0b78524,
but didn't commit it here to avoid a dependency between the SDK and the
`metric/x` package.

As expected, this removes one allocation from the Dynamic case!

The results are:

```
                                                                          │    main.txt    │            resettable.txt            │
                                                                          │     sec/op     │     sec/op        vs base        │
EndToEndCounterAdd/NoFilter/Attributes/1/Dynamic/WithAttributeSet-24         238.2n ±  5%    220.7n ±  9%   -7.39% (p=0.002 n=6)
EndToEndCounterAdd/NoFilter/Attributes/5/Dynamic/WithAttributeSet-24         611.1n ±  4%    620.3n ±  2%        ~ (p=0.394 n=6)
EndToEndCounterAdd/NoFilter/Attributes/10/Dynamic/WithAttributeSet-24        1.147µ ±  7%    1.171µ ±  5%        ~ (p=0.258 n=6)
EndToEndCounterAdd/Filtered/Attributes/1/Dynamic/WithAttributeSet-24         363.8n ±  6%    363.5n ±  6%        ~ (p=1.000 n=6)
EndToEndCounterAdd/Filtered/Attributes/5/Dynamic/WithAttributeSet-24         1.464µ ±  5%    1.475µ ± 10%        ~ (p=0.727 n=6)
EndToEndCounterAdd/Filtered/Attributes/10/Dynamic/WithAttributeSet-24        2.924µ ±  7%    2.589µ ±  6%  -11.47% (p=0.002 n=6)
                                                                          │    main.txt    │            resettable.txt            │
                                                                          │      B/op      │      B/op         vs base        │
EndToEndCounterAdd/NoFilter/Attributes/1/Dynamic/WithAttributeSet-24          88.00 ± 0%      64.00 ± 0%  -27.27% (p=0.002 n=6)
EndToEndCounterAdd/NoFilter/Attributes/5/Dynamic/WithAttributeSet-24          344.0 ± 0%      321.0 ± 0%   -6.69% (p=0.002 n=6)
EndToEndCounterAdd/NoFilter/Attributes/10/Dynamic/WithAttributeSet-24         729.0 ± 0%      706.0 ± 0%   -3.16% (p=0.002 n=6)
EndToEndCounterAdd/Filtered/Attributes/1/Dynamic/WithAttributeSet-24          152.0 ± 0%      128.0 ± 0%  -15.79% (p=0.002 n=6)
EndToEndCounterAdd/Filtered/Attributes/5/Dynamic/WithAttributeSet-24          921.0 ± 0%      899.0 ± 0%   -2.39% (p=0.002 n=6)
EndToEndCounterAdd/Filtered/Attributes/10/Dynamic/WithAttributeSet-24       2.026Ki ± 0%    2.006Ki ± 0%   -1.01% (p=0.002 n=6)
                                                                          │    main.txt    │            resettable.txt            │
                                                                          │   allocs/op    │   allocs/op       vs base        │
EndToEndCounterAdd/NoFilter/Attributes/1/Dynamic/WithAttributeSet-24          2.000 ± 0%      1.000 ± 0%  -50.00% (p=0.002 n=6)
EndToEndCounterAdd/NoFilter/Attributes/5/Dynamic/WithAttributeSet-24          2.000 ± 0%      1.000 ± 0%  -50.00% (p=0.002 n=6)
EndToEndCounterAdd/NoFilter/Attributes/10/Dynamic/WithAttributeSet-24         2.000 ± 0%      1.000 ± 0%  -50.00% (p=0.002 n=6)
EndToEndCounterAdd/Filtered/Attributes/1/Dynamic/WithAttributeSet-24          3.000 ± 0%      2.000 ± 0%  -33.33% (p=0.002 n=6)
EndToEndCounterAdd/Filtered/Attributes/5/Dynamic/WithAttributeSet-24          4.000 ± 0%      3.000 ± 0%  -25.00% (p=0.002 n=6)
EndToEndCounterAdd/Filtered/Attributes/10/Dynamic/WithAttributeSet-24         4.000 ± 0%      3.000 ± 0%  -25.00% (p=0.002 n=6)
```

---------

Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
2026-04-13 12:47:46 -04:00

162 lines
4.1 KiB
Go

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package metric // import "go.opentelemetry.io/otel/metric"
import (
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/attribute"
)
type attrConf interface {
Attributes() attribute.Set
}
func TestConfigAttrs(t *testing.T) {
t.Run("AddConfig", testConfAttr(func(mo ...MeasurementOption) attrConf {
opts := make([]AddOption, len(mo))
for i := range mo {
opts[i] = mo[i].(AddOption)
}
return NewAddConfig(opts)
}))
t.Run("RecordConfig", testConfAttr(func(mo ...MeasurementOption) attrConf {
opts := make([]RecordOption, len(mo))
for i := range mo {
opts[i] = mo[i].(RecordOption)
}
return NewRecordConfig(opts)
}))
t.Run("ObserveConfig", testConfAttr(func(mo ...MeasurementOption) attrConf {
opts := make([]ObserveOption, len(mo))
for i := range mo {
opts[i] = mo[i].(ObserveOption)
}
return NewObserveConfig(opts)
}))
}
func testConfAttr(newConf func(...MeasurementOption) attrConf) func(t *testing.T) {
return func(t *testing.T) {
t.Run("ZeroConfigEmpty", func(t *testing.T) {
c := newConf()
assert.Equal(t, *attribute.EmptySet(), c.Attributes())
})
t.Run("EmptySet", func(t *testing.T) {
c := newConf(WithAttributeSet(*attribute.EmptySet()))
assert.Equal(t, *attribute.EmptySet(), c.Attributes())
})
aliceAttr := attribute.String("user", "Alice")
alice := attribute.NewSet(aliceAttr)
t.Run("SingleWithAttributeSet", func(t *testing.T) {
c := newConf(WithAttributeSet(alice))
assert.Equal(t, alice, c.Attributes())
})
t.Run("SingleWithAttributes", func(t *testing.T) {
c := newConf(WithAttributes(aliceAttr))
assert.Equal(t, alice, c.Attributes())
})
bobAttr := attribute.String("user", "Bob")
bob := attribute.NewSet(bobAttr)
t.Run("MultiWithAttributeSet", func(t *testing.T) {
c := newConf(WithAttributeSet(alice), WithAttributeSet(bob))
assert.Equal(t, bob, c.Attributes())
})
t.Run("MergedWithAttributes", func(t *testing.T) {
c := newConf(WithAttributes(aliceAttr, bobAttr))
assert.Equal(t, bob, c.Attributes())
})
t.Run("MultiWithAttributeSet", func(t *testing.T) {
c := newConf(WithAttributes(aliceAttr), WithAttributes(bobAttr))
assert.Equal(t, bob, c.Attributes())
})
t.Run("MergedEmpty", func(t *testing.T) {
c := newConf(WithAttributeSet(alice), WithAttributeSet(*attribute.EmptySet()))
assert.Equal(t, alice, c.Attributes())
})
}
}
func TestWithAttributesConcurrentSafe(*testing.T) {
attrs := []attribute.KeyValue{
attribute.String("user", "Alice"),
attribute.Bool("admin", true),
attribute.String("user", "Bob"),
}
var wg sync.WaitGroup
wg.Go(func() {
opt := []AddOption{WithAttributes(attrs...)}
_ = NewAddConfig(opt)
})
wg.Go(func() {
opt := []AddOption{WithAttributes(attrs...)}
_ = NewAddConfig(opt)
})
wg.Go(func() {
opt := []RecordOption{WithAttributes(attrs...)}
_ = NewRecordConfig(opt)
})
wg.Go(func() {
opt := []RecordOption{WithAttributes(attrs...)}
_ = NewRecordConfig(opt)
})
wg.Go(func() {
opt := []ObserveOption{WithAttributes(attrs...)}
_ = NewObserveConfig(opt)
})
wg.Go(func() {
opt := []ObserveOption{WithAttributes(attrs...)}
_ = NewObserveConfig(opt)
})
wg.Wait()
}
func TestSettableOptions(t *testing.T) {
type settable interface {
Set(attribute.Set)
}
aliceAttr := attribute.String("user", "Alice")
alice := attribute.NewSet(aliceAttr)
bobAttr := attribute.String("user", "Bob")
bob := attribute.NewSet(bobAttr)
t.Run("WithAttributeSet", func(t *testing.T) {
opt := WithAttributeSet(alice)
r, ok := opt.(settable)
require.True(t, ok, "WithAttributeSet option does not implement settable")
r.Set(bob)
c := NewAddConfig([]AddOption{opt.(AddOption)})
assert.Equal(t, bob, c.Attributes())
})
t.Run("WithAttributes", func(t *testing.T) {
opt := WithAttributes(aliceAttr)
r, ok := opt.(settable)
require.True(t, ok, "WithAttributes option does not implement settable")
r.Set(bob)
c := NewAddConfig([]AddOption{opt.(AddOption)})
assert.Equal(t, bob, c.Attributes())
})
}