Describe your environment
Labels: bug, sdk, metrics, data-correctness
Affected packages: opentelemetry-sdk
Found on: main @ 0a5d76b6
Environment: CPython 3.12
What happened?
_hash_attributes returns scalar attribute values unchanged as part of the aggregation key. Python compares True == 1 == 1.0 and hashes all three identically, so attribute sets that are distinct in the OpenTelemetry data model - OTLP encodes them as bool_value, int_value and double_value - collapse into a single time series. The surviving series is labelled with whichever type happened to arrive first.
The same flaw makes a sequence of pairs hash identically to the mapping it resembles.
Steps to Reproduce
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import InMemoryMetricReader
reader = InMemoryMetricReader()
provider = MeterProvider(metric_readers=[reader])
counter = provider.get_meter("m").create_counter("c")
counter.add(1, {"k": True})
counter.add(1, {"k": 1})
counter.add(1, {"k": 1.0})
data = reader.get_metrics_data()
points = data.resource_metrics[0].scope_metrics[0].metrics[0].data.data_points
print(len(points), [dict(p.attributes) for p in points], [p.value for p in points])
Expected Result
Three data points, each labelled with the attribute value and type that was actually recorded.
Actual Result
data points produced: 1 (expected 3)
attributes={'k': True} value_type=bool count=3
# unit level
_hash_attributes({'k': True}) = (('k', True),)
_hash_attributes({'k': 1}) = (('k', 1),)
_hash_attributes({'k': 1.0}) = (('k', 1.0),)
all equal as dict keys: True, same hash: True
# a different instrument, different values
h.record(1, {"flag": False}); h.record(2, {"flag": 0})
data points produced: 1 (expected 2)
Additional context
Silent metric corruption in two directions at once. Counts from unrelated series are summed together, so the surviving series over-reports; and that series is exported with an attribute value of the wrong type, so a backend grouping on it sees a label that was never recorded.
This is easy to hit without doing anything unusual: a boolean flag attribute recorded as True by one code path and as 1 by another, or a numeric attribute that arrives as an int from one caller and a float from another, is enough.
Would you like to implement a fix?
Yes
Tip
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.
Describe your environment
Labels: bug, sdk, metrics, data-correctness
Affected packages:
opentelemetry-sdkFound on:
main@0a5d76b6Environment: CPython 3.12
What happened?
_hash_attributesreturns scalar attribute values unchanged as part of the aggregation key. Python comparesTrue == 1 == 1.0and hashes all three identically, so attribute sets that are distinct in the OpenTelemetry data model - OTLP encodes them asbool_value,int_valueanddouble_value- collapse into a single time series. The surviving series is labelled with whichever type happened to arrive first.The same flaw makes a sequence of pairs hash identically to the mapping it resembles.
Steps to Reproduce
Expected Result
Three data points, each labelled with the attribute value and type that was actually recorded.
Actual Result
Additional context
Silent metric corruption in two directions at once. Counts from unrelated series are summed together, so the surviving series over-reports; and that series is exported with an attribute value of the wrong type, so a backend grouping on it sees a label that was never recorded.
This is easy to hit without doing anything unusual: a boolean flag attribute recorded as
Trueby one code path and as1by another, or a numeric attribute that arrives as anintfrom one caller and afloatfrom another, is enough.Would you like to implement a fix?
Yes
Tip
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding
+1orme too, to help us triage it. Learn more here.