[AGTMETRICS-309] add support for tag cardinality#327
Merged
brianna-wang-dd merged 68 commits intomasterfrom Jul 21, 2025
Merged
[AGTMETRICS-309] add support for tag cardinality#327brianna-wang-dd merged 68 commits intomasterfrom
brianna-wang-dd merged 68 commits intomasterfrom
Conversation
This reverts commit 57c0ec6.
…adog-go into brianna.wang/tag-cardinality
StephenWakely
requested changes
Jul 15, 2025
StephenWakely
previously approved these changes
Jul 17, 2025
Contributor
StephenWakely
left a comment
There was a problem hiding this comment.
Nicely done! I just have a few comments which are pretty pedantic so shouldn't block the PR, but feel free to address!
rayz
approved these changes
Jul 21, 2025
| func (a *aggregator) count(name string, value int64, tags []string) error { | ||
| context := getContext(name, tags) | ||
| func (a *aggregator) count(name string, value int64, tags []string, cardinality Cardinality) error { | ||
| resolvedCardinality := resolveCardinality(cardinality) |
Contributor
There was a problem hiding this comment.
This is redundant
Comment on lines
+81
to
+90
| func parseTagCardinality(parameters []Parameter) Cardinality { | ||
| cardinality := CardinalityNotSet | ||
| for _, o := range parameters { | ||
| c, ok := o.(Cardinality) | ||
| if ok { | ||
| cardinality = c | ||
| } | ||
| } | ||
| return resolveCardinality(cardinality) | ||
| } |
Contributor
There was a problem hiding this comment.
This is called in the hot path and has to obtain global lock to get the default value which has high performance hit. Another problem is that second client with different options (o.tagCardinality) would override global default.
A better approach could be to get rid of global value and store default cardinality from environment inside of the client structure:
diff --git a/statsd/aggregator.go b/statsd/aggregator.go
index 03629d2..283b39b 100644
--- a/statsd/aggregator.go
+++ b/statsd/aggregator.go
@@ -224,8 +224,7 @@ func getContextAndTags(name string, tags []string, cardinality Cardinality) (str
}
func (a *aggregator) count(name string, value int64, tags []string, cardinality Cardinality) error {
- resolvedCardinality := resolveCardinality(cardinality)
- context := getContext(name, tags, resolvedCardinality)
+ context := getContext(name, tags, cardinality)
a.countsM.RLock()
if count, found := a.counts[context]; found {
count.sample(value)
@@ -234,10 +233,10 @@ func (a *aggregator) count(name string, value int64, tags []string, cardinality
}
a.countsM.RUnlock()
- metric := newCountMetric(name, value, tags, resolvedCardinality)
+ metric := newCountMetric(name, value, tags, cardinality)
a.countsM.Lock()
- // Check if another goroutines hasn't created the value betwen the RUnlock and 'Lock'
+ // Check if another goroutines hasn't created the value between the RUnlock and 'Lock'
if count, found := a.counts[context]; found {
count.sample(value)
a.countsM.Unlock()
diff --git a/statsd/statsd_benchmark_test.go b/statsd/statsd_benchmark_test.go
index 35ea6ed..f03566f 100644
--- a/statsd/statsd_benchmark_test.go
+++ b/statsd/statsd_benchmark_test.go
@@ -86,7 +86,7 @@ func benchmarkStatsdDifferentMetrics(b *testing.B, transport string, extraOption
name := fmt.Sprintf("test.metric%d", testNumber)
tags := []string{"tag:tag"}
for pb.Next() {
- client.Gauge(name, 1, tags, 1)
+ client.Count(name, 1, tags, 1)
}
})
client.Flush()
@@ -106,7 +106,7 @@ func benchmarkStatsdSameMetrics(b *testing.B, transport string, extraOptions ...
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
tags := []string{"tag:tag"}
- client.Gauge("test.metric", 1, tags, 1)
+ client.Count("test.metric", 1, tags, 1)
}
})
client.Flush()
diff --git a/statsd/statsdex.go b/statsd/statsdex.go
index d9e9b4e..ffba55f 100644
--- a/statsd/statsdex.go
+++ b/statsd/statsdex.go
@@ -300,6 +300,7 @@ type ClientEx struct {
errorOnBlockedChannel bool
errorHandler ErrorHandler
originDetection bool
+ defaultTagCardinality Cardinality
}
// statsdTelemetry contains telemetry metrics about the client
@@ -479,6 +480,7 @@ func newWithWriter(w Transport, o *Options, writerName string) (*ClientEx, error
// Initializes the global tag cardinality with either the value passed in by the user or the value from the DD_CARDINALITY/DATADOG_CARDINALITY environment variable.
initTagCardinality(o.tagCardinality)
+ c.defaultTagCardinality = CardinalityNotSet // TODO o.tagCardinality
initContainerID(o.containerID, fillInContainerID(o), isHostCgroupNamespace())
isUDS := writerName == writerNameUDS
@@ -738,7 +740,7 @@ func (c *ClientEx) Count(name string, value int64, tags []string, rate float64,
return ErrNoClient
}
atomic.AddUint64(&c.telemetry.totalMetricsCount, 1)
- cardinality := parseTagCardinality(parameters)
+ cardinality := parseTagCardinality2(parameters, c.defaultTagCardinality)
if c.agg != nil {
return c.agg.count(name, value, tags, cardinality)
}
diff --git a/statsd/tag_cardinality.go b/statsd/tag_cardinality.go
index 1289e47..524c013 100644
--- a/statsd/tag_cardinality.go
+++ b/statsd/tag_cardinality.go
@@ -97,3 +97,13 @@ func resolveCardinality(card Cardinality) Cardinality {
}
return card
}
+
+func parseTagCardinality2(parameters []Parameter, defaultCardinality Cardinality) Cardinality {
+ for _, o := range parameters {
+ c, ok := o.(Cardinality)
+ if ok {
+ return c
+ }
+ }
+ return defaultCardinality
+}
Contributor
There was a problem hiding this comment.
Here is benchstat for one of the benchmarks for the above:
goos: darwin
goarch: arm64
pkg: github.com/DataDog/datadog-go/v5/statsd
cpu: Apple M4 Max
│ HEAD~1 │ HEAD │
│ sec/op │ sec/op vs base │
StatsdUDPSameMetricMutexAggregation-16 254.8n ± 0% 156.3n ± 0% -38.65% (p=0.000 n=10)
│ HEAD~1 │ HEAD │
│ %_dropRate │ %_dropRate vs base │
StatsdUDPSameMetricMutexAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
¹ all samples are equal
AlexanderYastrebov
added a commit
to AlexanderYastrebov/datadog-go
that referenced
this pull request
Dec 19, 2025
The change DataDog#327 introduced support for tag cardinality with global default value that could be overriden per metric call via parameter. Default cardinality value is stored in a global variable guarded by a mutex which has two downsides: * performance impact - each measure call has to obtain mutex to get the default value * two clients created with different default cardinality override the same global value This change eliminates global default value and uses default value per statsd client specified during client construction with a fallback to environment variables. User supplied cardinality value is validated during client construction and during parameter parsing with a fallback to default value such that all internal components can assume they always receive a valid value. Performance improvement (via `Statsd.*Aggregation` benchmarks): ``` goos: darwin goarch: arm64 pkg: github.com/DataDog/datadog-go/v5/statsd cpu: Apple M4 Max │ master │ HEAD │ │ sec/op │ sec/op vs base │ StatsdUDPSameMetricMutexAggregation-16 248.3n ± 1% 142.3n ± 0% -42.68% (p=0.000 n=10) StatsdUDPSameMetricChannelAggregation-16 252.3n ± 0% 143.8n ± 1% -43.02% (p=0.000 n=10) StatsdUDPDifferentMetricMutexAggregation-16 250.7n ± 1% 148.6n ± 0% -40.71% (p=0.000 n=10) StatsdUDPDifferentMetricChannelAggregation-16 252.5n ± 1% 150.3n ± 0% -40.48% (p=0.000 n=10) StatsdUDSSameMetricMutexAggregation-16 248.9n ± 1% 143.0n ± 1% -42.53% (p=0.000 n=10) StatsdUDSSameMetricChannelAggregation-16 251.9n ± 1% 144.0n ± 1% -42.87% (p=0.000 n=10) StatsdUDPSifferentMetricMutexAggregation-16 252.1n ± 0% 148.8n ± 1% -40.97% (p=0.000 n=10) StatsdUDSDifferentMetricChannelAggregation-16 252.8n ± 1% 149.5n ± 0% -40.85% (p=0.000 n=10) geomean 251.2n 146.3n -41.77% │ master │ HEAD │ │ %_dropRate │ %_dropRate vs base │ StatsdUDPSameMetricMutexAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDPSameMetricChannelAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDPDifferentMetricMutexAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDPDifferentMetricChannelAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDSSameMetricMutexAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDSSameMetricChannelAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDPSifferentMetricMutexAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDSDifferentMetricChannelAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ geomean ² +0.00% ² ¹ all samples are equal ² summaries must be >0 to compute geomean ```
AlexanderYastrebov
added a commit
to AlexanderYastrebov/datadog-go
that referenced
this pull request
Jan 8, 2026
The change DataDog#327 introduced support for tag cardinality with global default value that could be overriden per metric call via parameter. Default cardinality value is stored in a global variable guarded by a mutex which has two downsides: * performance impact - each measure call has to obtain mutex to get the default value * two clients created with different default cardinality override the same global value This change eliminates global default value and uses default value per statsd client specified during client construction with a fallback to environment variables. User supplied cardinality value is validated during client construction and during parameter parsing with a fallback to default value such that all internal components can assume they always receive a valid value. Performance improvement (via `Statsd.*Aggregation` benchmarks): ``` goos: darwin goarch: arm64 pkg: github.com/DataDog/datadog-go/v5/statsd cpu: Apple M4 Max │ master │ HEAD │ │ sec/op │ sec/op vs base │ StatsdUDPSameMetricMutexAggregation-16 248.3n ± 1% 142.3n ± 0% -42.68% (p=0.000 n=10) StatsdUDPSameMetricChannelAggregation-16 252.3n ± 0% 143.8n ± 1% -43.02% (p=0.000 n=10) StatsdUDPDifferentMetricMutexAggregation-16 250.7n ± 1% 148.6n ± 0% -40.71% (p=0.000 n=10) StatsdUDPDifferentMetricChannelAggregation-16 252.5n ± 1% 150.3n ± 0% -40.48% (p=0.000 n=10) StatsdUDSSameMetricMutexAggregation-16 248.9n ± 1% 143.0n ± 1% -42.53% (p=0.000 n=10) StatsdUDSSameMetricChannelAggregation-16 251.9n ± 1% 144.0n ± 1% -42.87% (p=0.000 n=10) StatsdUDPSifferentMetricMutexAggregation-16 252.1n ± 0% 148.8n ± 1% -40.97% (p=0.000 n=10) StatsdUDSDifferentMetricChannelAggregation-16 252.8n ± 1% 149.5n ± 0% -40.85% (p=0.000 n=10) geomean 251.2n 146.3n -41.77% │ master │ HEAD │ │ %_dropRate │ %_dropRate vs base │ StatsdUDPSameMetricMutexAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDPSameMetricChannelAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDPDifferentMetricMutexAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDPDifferentMetricChannelAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDSSameMetricMutexAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDSSameMetricChannelAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDPSifferentMetricMutexAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDSDifferentMetricChannelAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ geomean ² +0.00% ² ¹ all samples are equal ² summaries must be >0 to compute geomean ```
StephenWakely
pushed a commit
that referenced
this pull request
Jan 8, 2026
* statsd: remove global cardinality The change #327 introduced support for tag cardinality with global default value that could be overriden per metric call via parameter. Default cardinality value is stored in a global variable guarded by a mutex which has two downsides: * performance impact - each measure call has to obtain mutex to get the default value * two clients created with different default cardinality override the same global value This change eliminates global default value and uses default value per statsd client specified during client construction with a fallback to environment variables. User supplied cardinality value is validated during client construction and during parameter parsing with a fallback to default value such that all internal components can assume they always receive a valid value. Performance improvement (via `Statsd.*Aggregation` benchmarks): ``` goos: darwin goarch: arm64 pkg: github.com/DataDog/datadog-go/v5/statsd cpu: Apple M4 Max │ master │ HEAD │ │ sec/op │ sec/op vs base │ StatsdUDPSameMetricMutexAggregation-16 248.3n ± 1% 142.3n ± 0% -42.68% (p=0.000 n=10) StatsdUDPSameMetricChannelAggregation-16 252.3n ± 0% 143.8n ± 1% -43.02% (p=0.000 n=10) StatsdUDPDifferentMetricMutexAggregation-16 250.7n ± 1% 148.6n ± 0% -40.71% (p=0.000 n=10) StatsdUDPDifferentMetricChannelAggregation-16 252.5n ± 1% 150.3n ± 0% -40.48% (p=0.000 n=10) StatsdUDSSameMetricMutexAggregation-16 248.9n ± 1% 143.0n ± 1% -42.53% (p=0.000 n=10) StatsdUDSSameMetricChannelAggregation-16 251.9n ± 1% 144.0n ± 1% -42.87% (p=0.000 n=10) StatsdUDPSifferentMetricMutexAggregation-16 252.1n ± 0% 148.8n ± 1% -40.97% (p=0.000 n=10) StatsdUDSDifferentMetricChannelAggregation-16 252.8n ± 1% 149.5n ± 0% -40.85% (p=0.000 n=10) geomean 251.2n 146.3n -41.77% │ master │ HEAD │ │ %_dropRate │ %_dropRate vs base │ StatsdUDPSameMetricMutexAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDPSameMetricChannelAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDPDifferentMetricMutexAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDPDifferentMetricChannelAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDSSameMetricMutexAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDSSameMetricChannelAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDPSifferentMetricMutexAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ StatsdUDSDifferentMetricChannelAggregation-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ geomean ² +0.00% ² ¹ all samples are equal ² summaries must be >0 to compute geomean ``` * statsd: set CardinalityNotSet to zero Set CardinalityNotSet to zero to avoid gap between it and CardinalityNone which equals to one.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR implements tag cardinality for this library by sending a cardinality field for each metric as
card:<cardinality>field. Possible tag cardinality values arenone,low,orchestrator, orhigh.system.mangos:21|g#foo:bar,baz:boo|card:highThe tag cardinality can be specified as a global setting (through the user passing the option to the constructor when setting up
Statsdor by reading the value fromDD_CARDINALITYorDATADOG_CARDINALITY). This can be overridden for each metric when creating one.In order to ensure backward compatibility, a variadic parameters are added to each metric function that allow the tag cardinality to be passed in as type
CardinalityParameter.This PR was tested with unit test for tag cardinality specific functionality (such as global initialization from the 3 possible sources, checking the validity of inputs, and getting the global tag cardinality) and general metric functions (which included testing the metric-specific overrides for the tag cardinality).