-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
otlpmetric with StringSlice produces duplicate metrics #3108
Comments
Thanks for the bug report. The duplicate metrics are resulting from attribute sets not rendering a truly comparable package main
import (
"fmt"
"go.opentelemetry.io/otel/attribute"
)
func main() {
a := attribute.NewSet(attribute.StringSlice("key", []string{"value"}))
b := attribute.NewSet(attribute.StringSlice("key", []string{"value"}))
fmt.Println(a.Equivalent() == b.Equivalent())
} returns This means that the map key used by the metric instrument does not retrieve existing metrics when they exist. opentelemetry-go/sdk/metric/sdk.go Line 161 in 569f743
This is almost certainly going to be a problem with the new SDK as well:
|
This test passes when run against the main branch: func TestSetComparability(t *testing.T) {
pairs := [][2]attribute.KeyValue{
{
attribute.Bool("Bool", true),
attribute.Bool("Bool", true),
},
{
attribute.BoolSlice("BoolSlice", []bool{true, false, true}),
attribute.BoolSlice("BoolSlice", []bool{true, false, true}),
},
{
attribute.Int("Int", 34),
attribute.Int("Int", 34),
},
{
attribute.IntSlice("IntSlice", []int{312, 1, -2}),
attribute.IntSlice("IntSlice", []int{312, 1, -2}),
},
{
attribute.Int64("Int64", 98),
attribute.Int64("Int64", 98),
},
{
attribute.Int64Slice("Int64Slice", []int64{12, 1298, -219, 2}),
attribute.Int64Slice("Int64Slice", []int64{12, 1298, -219, 2}),
},
{
attribute.Float64("Float64", 19.09),
attribute.Float64("Float64", 19.09),
},
{
attribute.Float64Slice("Float64Slice", []float64{12398.1, -37.1713873737, 3}),
attribute.Float64Slice("Float64Slice", []float64{12398.1, -37.1713873737, 3}),
},
{
attribute.String("String", "string value"),
attribute.String("String", "string value"),
},
{
attribute.StringSlice("StringSlice", []string{"one", "two", "three"}),
attribute.StringSlice("StringSlice", []string{"one", "two", "three"}),
},
{
attribute.Stringer("Stringer", stringer("stringer value")),
attribute.Stringer("Stringer", stringer("stringer value")),
},
}
for _, p := range pairs {
s0, s1 := attribute.NewSet(p[0]), attribute.NewSet(p[1])
m := map[attribute.Set]struct{}{s0: {}}
assert.Containsf(t, m, s1, "%s not comparable", p[0].Value.Type())
}
} I wonder if the inequality is coming from the descriptor: opentelemetry-go/sdk/metric/sdk.go Line 160 in 569f743
or maybe the sort slice being reused? opentelemetry-go/sdk/metric/sdk.go Line 155 in 569f743
More investigation is needed. |
When a direct map look-up is used, the above test fails: func TestSetComparability(t *testing.T) {
pairs := [][2]attribute.KeyValue{
{
attribute.Bool("Bool", true),
attribute.Bool("Bool", true),
},
{
attribute.BoolSlice("BoolSlice", []bool{true, false, true}),
attribute.BoolSlice("BoolSlice", []bool{true, false, true}),
},
{
attribute.Int("Int", 34),
attribute.Int("Int", 34),
},
{
attribute.IntSlice("IntSlice", []int{312, 1, -2}),
attribute.IntSlice("IntSlice", []int{312, 1, -2}),
},
{
attribute.Int64("Int64", 98),
attribute.Int64("Int64", 98),
},
{
attribute.Int64Slice("Int64Slice", []int64{12, 1298, -219, 2}),
attribute.Int64Slice("Int64Slice", []int64{12, 1298, -219, 2}),
},
{
attribute.Float64("Float64", 19.09),
attribute.Float64("Float64", 19.09),
},
{
attribute.Float64Slice("Float64Slice", []float64{12398.1, -37.1713873737, 3}),
attribute.Float64Slice("Float64Slice", []float64{12398.1, -37.1713873737, 3}),
},
{
attribute.String("String", "string value"),
attribute.String("String", "string value"),
},
{
attribute.StringSlice("StringSlice", []string{"one", "two", "three"}),
attribute.StringSlice("StringSlice", []string{"one", "two", "three"}),
},
{
attribute.Stringer("Stringer", stringer("stringer value")),
attribute.Stringer("Stringer", stringer("stringer value")),
},
}
for _, p := range pairs {
s0, s1 := attribute.NewSet(p[0]), attribute.NewSet(p[1])
m := map[attribute.Set]struct{}{s0: {}}
_, ok := m[s1]
assert.Truef(t, ok, "%s not comparable", p[0].Value.Type())
}
}
|
I believe this can be solved inside the |
I can't prioritize working on this right away. In case someone else wants to pick this up, here's roughly how the solution looks for just one type of slice (out of 5). https://github.com/jmacd/opentelemetry-go/pull/new/jmacd/sampleslicesupport
As you can see, this is difficult to read. Potentially a generics-based solution would be easier to accept? |
Performance note: implied by the above, we have to copy the slice to read it. This isn't great. We could offer a different API to access the i'th element of a slice-valued attribute instead, that would allow the consumer of a Value to avoid copying. |
I was able to validate this passes the above test. 👍 |
@fatsheep9146 yes, please! |
* wip, progress so far * more similar structure to otel, fixing imports * fix for attributesEqual() following otel-go slice-valued attribute representation change, see open-telemetry/opentelemetry-go#3108 * more metric->view pkg fixes * remove otlpconfig pkg * export NewClient() func!! * moved some things around * fix exporter, pipeline imports * go.mod updates * fix shutdown deadlock * upgrade versions to v1.11.2 and v0.34.0 * fix example I missed * work on instrumentation unit tests * repair the tests * fixed commented out unit tests * fix/add comments, stop calling the otel skd "old" * fix go.mod Co-authored-by: Joshua MacDonald <[email protected]>
Description
Metrics added with the same key and a
StringSlice
value are not deduplicated, resulting in multiple identical output series. If running with a prometheus exporter, this causes the exporter to fail withcollected metric "metric_name" … was collected before with the same name and label values
from this error.Environment
Running in
golang:1.16
imageSteps To Reproduce
Currently outputs:
Expected behavior
I expect the key-values of StringSlice to deduplicate, with the above snippet producing two metrics with a sum of 3.
I expect the values of StringSlice to be deduplicated as-is, without sorting (e.g. the array stringified), as they are input as an ordered array.
Note: I recognize this might be intentional behavior, but it surprised me / I wasn't able to find documentation around the behavior. If this is not a bug but rather a documentation issue, I'd be happy to submit a related documentation MR.
The text was updated successfully, but these errors were encountered: