mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2024-12-12 10:04:29 +02:00
b5292b8459
* Add the cache type * Add cache unit tests * Test cache concurrency * Add the instrumentCache * Use the instrumentCache to deduplicate creation * Drop unique check from addAggregator * Fix aggregatorCache* docs * Update cachedAggregator and aggregator method docs * Remove unnecessary type constraint * Remove unused errAlreadyRegistered * Rename to not shadow imports * Add changes to changelog * Fix changelog English * Store resolvers in the meter instead of caches * Test all Aggregator[N] impls are comparable * Fix lint * Add documentation that Aggregators need to be comparable * Update sdk/metric/internal/aggregator.go Co-authored-by: Anthony Mirabella <a9@aneurysm9.com> * Update sdk/metric/instrument.go Co-authored-by: Anthony Mirabella <a9@aneurysm9.com> * Update sdk/metric/instrument.go Co-authored-by: Anthony Mirabella <a9@aneurysm9.com> * Update sdk/metric/internal/aggregator_test.go Co-authored-by: Anthony Mirabella <a9@aneurysm9.com> * Fix pipeline_test.go use of newInstrumentCache Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
163 lines
4.7 KiB
Go
163 lines
4.7 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package internal // import "go.opentelemetry.io/otel/sdk/metric/internal"
|
|
|
|
import (
|
|
"strconv"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
|
"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
|
|
)
|
|
|
|
const (
|
|
defaultGoroutines = 5
|
|
defaultMeasurements = 30
|
|
defaultCycles = 3
|
|
)
|
|
|
|
var (
|
|
alice = attribute.NewSet(attribute.String("user", "alice"), attribute.Bool("admin", true))
|
|
bob = attribute.NewSet(attribute.String("user", "bob"), attribute.Bool("admin", false))
|
|
carol = attribute.NewSet(attribute.String("user", "carol"), attribute.Bool("admin", false))
|
|
|
|
monoIncr = setMap{alice: 1, bob: 10, carol: 2}
|
|
nonMonoIncr = setMap{alice: 1, bob: -1, carol: 2}
|
|
|
|
// Sat Jan 01 2000 00:00:00 GMT+0000.
|
|
staticTime = time.Unix(946684800, 0)
|
|
staticNowFunc = func() time.Time { return staticTime }
|
|
// Pass to t.Cleanup to override the now function with staticNowFunc and
|
|
// revert once the test completes. E.g. t.Cleanup(mockTime(now)).
|
|
mockTime = func(orig func() time.Time) (cleanup func()) {
|
|
now = staticNowFunc
|
|
return func() { now = orig }
|
|
}
|
|
)
|
|
|
|
// setMap maps attribute sets to a number.
|
|
type setMap map[attribute.Set]int
|
|
|
|
// expectFunc is a function that returns an Aggregation of expected values for
|
|
// a cycle that contains m measurements (total across all goroutines). Each
|
|
// call advances the cycle.
|
|
type expectFunc func(m int) metricdata.Aggregation
|
|
|
|
// aggregatorTester runs an acceptance test on an Aggregator. It will ask an
|
|
// Aggregator to aggregate a set of values as if they were real measurements
|
|
// made MeasurementN number of times. This will be done in GoroutineN number
|
|
// of different goroutines. After the Aggregator has been asked to aggregate
|
|
// all these measurements, it is validated using a passed expecterFunc. This
|
|
// set of operation is a signle cycle, and the the aggregatorTester will run
|
|
// CycleN number of cycles.
|
|
type aggregatorTester[N int64 | float64] struct {
|
|
// GoroutineN is the number of goroutines aggregatorTester will use to run
|
|
// the test with.
|
|
GoroutineN int
|
|
// MeasurementN is the number of measurements that are made each cycle a
|
|
// goroutine runs the test.
|
|
MeasurementN int
|
|
// CycleN is the number of times a goroutine will make a set of
|
|
// measurements.
|
|
CycleN int
|
|
}
|
|
|
|
func (at *aggregatorTester[N]) Run(a Aggregator[N], incr setMap, eFunc expectFunc) func(*testing.T) {
|
|
m := at.MeasurementN * at.GoroutineN
|
|
return func(t *testing.T) {
|
|
t.Run("Comparable", func(t *testing.T) {
|
|
assert.NotPanics(t, func() {
|
|
_ = map[Aggregator[N]]struct{}{a: {}}
|
|
})
|
|
})
|
|
|
|
t.Run("Correctness", func(t *testing.T) {
|
|
for i := 0; i < at.CycleN; i++ {
|
|
var wg sync.WaitGroup
|
|
wg.Add(at.GoroutineN)
|
|
for j := 0; j < at.GoroutineN; j++ {
|
|
go func() {
|
|
defer wg.Done()
|
|
for k := 0; k < at.MeasurementN; k++ {
|
|
for attrs, n := range incr {
|
|
a.Aggregate(N(n), attrs)
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
|
|
metricdatatest.AssertAggregationsEqual(t, eFunc(m), a.Aggregation())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
var bmarkResults metricdata.Aggregation
|
|
|
|
func benchmarkAggregatorN[N int64 | float64](b *testing.B, factory func() Aggregator[N], count int) {
|
|
attrs := make([]attribute.Set, count)
|
|
for i := range attrs {
|
|
attrs[i] = attribute.NewSet(attribute.Int("value", i))
|
|
}
|
|
|
|
b.Run("Aggregate", func(b *testing.B) {
|
|
agg := factory()
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
for _, attr := range attrs {
|
|
agg.Aggregate(1, attr)
|
|
}
|
|
}
|
|
bmarkResults = agg.Aggregation()
|
|
})
|
|
|
|
b.Run("Aggregations", func(b *testing.B) {
|
|
aggs := make([]Aggregator[N], b.N)
|
|
for n := range aggs {
|
|
a := factory()
|
|
for _, attr := range attrs {
|
|
a.Aggregate(1, attr)
|
|
}
|
|
aggs[n] = a
|
|
}
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
bmarkResults = aggs[n].Aggregation()
|
|
}
|
|
})
|
|
}
|
|
|
|
func benchmarkAggregator[N int64 | float64](factory func() Aggregator[N]) func(*testing.B) {
|
|
counts := []int{1, 10, 100}
|
|
return func(b *testing.B) {
|
|
for _, n := range counts {
|
|
b.Run(strconv.Itoa(n), func(b *testing.B) {
|
|
benchmarkAggregatorN(b, factory, n)
|
|
})
|
|
}
|
|
}
|
|
}
|