You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2026-06-03 18:35:08 +02:00
sdk/metric: apply default cardinality limit of 2000 (#8247)
Per https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#cardinality-limits: > If none of the previous values are defined, the default value of 2000 SHOULD be used.
This commit is contained in:
@@ -25,7 +25,7 @@ type config struct {
|
||||
cardinalityLimit int
|
||||
}
|
||||
|
||||
const defaultCardinalityLimit = 0
|
||||
const defaultCardinalityLimit = 2000
|
||||
|
||||
// readerSignals returns a force-flush and shutdown function for a
|
||||
// MeterProvider to call in their respective options. All Readers c contains
|
||||
@@ -172,7 +172,10 @@ func WithExemplarFilter(filter exemplar.Filter) Option {
|
||||
// The cardinality limit is the hard limit on the number of metric datapoints
|
||||
// that can be collected for a single instrument in a single collect cycle.
|
||||
//
|
||||
// Setting this to a zero or negative value means no limit is applied.
|
||||
// By default, if this option is not used, a limit of
|
||||
// 2000 is applied.
|
||||
//
|
||||
// Setting this to a zero or negative means no limit is applied.
|
||||
// This value applies to all instrument kinds, but can be overridden per kind by
|
||||
// the reader's cardinality limit selector (see [WithCardinalityLimitSelector]).
|
||||
func WithCardinalityLimit(limit int) Option {
|
||||
|
||||
@@ -338,6 +338,12 @@ func TestWithCardinalityLimit(t *testing.T) {
|
||||
options: []Option{},
|
||||
expectedLimit: 1234,
|
||||
},
|
||||
{
|
||||
name: "zero cardinality limit from env disables limit",
|
||||
envValue: "0",
|
||||
options: []Option{},
|
||||
expectedLimit: 0,
|
||||
},
|
||||
{
|
||||
name: "invalid env value uses default",
|
||||
envValue: "not-a-number",
|
||||
|
||||
+3
-5
@@ -44,9 +44,7 @@
|
||||
// Cardinality refers to the number of unique attributes collected. High cardinality can lead to
|
||||
// excessive memory usage, increased storage costs, and backend performance issues.
|
||||
//
|
||||
// Currently, the OpenTelemetry Go Metric SDK does not enforce a cardinality limit by default
|
||||
// (note that this may change in a future release). Use [WithCardinalityLimit] to set the
|
||||
// cardinality limit as desired.
|
||||
// By default, the OpenTelemetry Go Metric SDK enforces a cardinality limit of 2000.
|
||||
//
|
||||
// New attribute sets are dropped when the cardinality limit is reached. The measurement of
|
||||
// these sets are aggregated into
|
||||
@@ -57,8 +55,8 @@
|
||||
//
|
||||
// Recommendations:
|
||||
//
|
||||
// - Set the limit based on the theoretical maximum combinations or expected
|
||||
// active combinations. The OpenTelemetry Specification recommends a default of 2000.
|
||||
// - Tune the limit based on the theoretical maximum combinations or expected
|
||||
// active combinations. The SDK default is 2000.
|
||||
// - A too high of a limit increases worst-case memory overhead in the SDK and may cause downstream
|
||||
// issues for databases that cannot handle high cardinality.
|
||||
// - A too low of a limit causes loss of attribute detail as more data falls into overflow.
|
||||
|
||||
@@ -45,7 +45,6 @@ func Example() {
|
||||
meterProvider := metric.NewMeterProvider(
|
||||
metric.WithResource(res),
|
||||
metric.WithReader(reader),
|
||||
metric.WithCardinalityLimit(2000),
|
||||
)
|
||||
|
||||
// Handle shutdown properly so that nothing leaks.
|
||||
|
||||
+36
-18
@@ -176,32 +176,40 @@ func TestMeterProviderMixingOnRegisterErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMeterProviderCardinalityLimit(t *testing.T) {
|
||||
const uniqueAttributesCount = 10
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
options []Option
|
||||
wantDataPoints int
|
||||
name string
|
||||
options []Option
|
||||
uniqueAttributesCount int
|
||||
wantDataPoints int
|
||||
wantOverflowPoints int
|
||||
}{
|
||||
{
|
||||
name: "no limit (default)",
|
||||
options: nil,
|
||||
wantDataPoints: uniqueAttributesCount,
|
||||
name: "default limit",
|
||||
options: nil,
|
||||
uniqueAttributesCount: defaultCardinalityLimit + 5,
|
||||
wantDataPoints: defaultCardinalityLimit,
|
||||
wantOverflowPoints: 1,
|
||||
},
|
||||
{
|
||||
name: "no limit (limit=0)",
|
||||
options: []Option{WithCardinalityLimit(0)},
|
||||
wantDataPoints: uniqueAttributesCount,
|
||||
name: "no limit (limit=0)",
|
||||
options: []Option{WithCardinalityLimit(0)},
|
||||
uniqueAttributesCount: 10,
|
||||
wantDataPoints: 10,
|
||||
wantOverflowPoints: 0,
|
||||
},
|
||||
{
|
||||
name: "no limit (negative)",
|
||||
options: []Option{WithCardinalityLimit(-5)},
|
||||
wantDataPoints: uniqueAttributesCount,
|
||||
name: "no limit (negative)",
|
||||
options: []Option{WithCardinalityLimit(-5)},
|
||||
uniqueAttributesCount: 10,
|
||||
wantDataPoints: 10,
|
||||
wantOverflowPoints: 0,
|
||||
},
|
||||
{
|
||||
name: "limit=5",
|
||||
options: []Option{WithCardinalityLimit(5)},
|
||||
wantDataPoints: 5,
|
||||
name: "limit=5",
|
||||
options: []Option{WithCardinalityLimit(5)},
|
||||
uniqueAttributesCount: 10,
|
||||
wantDataPoints: 5,
|
||||
wantOverflowPoints: 1,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -216,7 +224,7 @@ func TestMeterProviderCardinalityLimit(t *testing.T) {
|
||||
counter, err := meter.Int64Counter("metric")
|
||||
require.NoError(t, err, "failed to create counter")
|
||||
|
||||
for i := range uniqueAttributesCount {
|
||||
for i := range tt.uniqueAttributesCount {
|
||||
counter.Add(
|
||||
t.Context(),
|
||||
1,
|
||||
@@ -241,6 +249,16 @@ func TestMeterProviderCardinalityLimit(t *testing.T) {
|
||||
tt.wantDataPoints,
|
||||
"unexpected number of data points",
|
||||
)
|
||||
|
||||
overflow := attribute.NewSet(attribute.Bool("otel.metric.overflow", true))
|
||||
var overflowPoints int
|
||||
for _, dp := range sumData.DataPoints {
|
||||
attrs := dp.Attributes
|
||||
if attrs.Equals(&overflow) {
|
||||
overflowPoints++
|
||||
}
|
||||
}
|
||||
assert.Equal(t, tt.wantOverflowPoints, overflowPoints, "unexpected overflow data points")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user