Skip to content

Commit 348ec9e

Browse files
authored
Metric attributes need sort with keys only (#1718)
1 parent ecfad5f commit 348ec9e

File tree

2 files changed

+99
-41
lines changed

2 files changed

+99
-41
lines changed

opentelemetry-sdk/src/attributes/set.rs

+1-41
Original file line numberDiff line numberDiff line change
@@ -39,47 +39,7 @@ impl PartialOrd for HashKeyValue {
3939

4040
impl Ord for HashKeyValue {
4141
fn cmp(&self, other: &Self) -> Ordering {
42-
match self.0.key.cmp(&other.0.key) {
43-
Ordering::Equal => match type_order(&self.0.value).cmp(&type_order(&other.0.value)) {
44-
Ordering::Equal => match (&self.0.value, &other.0.value) {
45-
(Value::F64(f), Value::F64(of)) => OrderedFloat(*f).cmp(&OrderedFloat(*of)),
46-
(Value::Array(Array::Bool(b)), Value::Array(Array::Bool(ob))) => b.cmp(ob),
47-
(Value::Array(Array::I64(i)), Value::Array(Array::I64(oi))) => i.cmp(oi),
48-
(Value::Array(Array::String(s)), Value::Array(Array::String(os))) => s.cmp(os),
49-
(Value::Array(Array::F64(f)), Value::Array(Array::F64(of))) => {
50-
match f.len().cmp(&of.len()) {
51-
Ordering::Equal => f
52-
.iter()
53-
.map(|x| OrderedFloat(*x))
54-
.collect::<Vec<_>>()
55-
.cmp(&of.iter().map(|x| OrderedFloat(*x)).collect()),
56-
other => other,
57-
}
58-
}
59-
(Value::Bool(b), Value::Bool(ob)) => b.cmp(ob),
60-
(Value::I64(i), Value::I64(oi)) => i.cmp(oi),
61-
(Value::String(s), Value::String(os)) => s.cmp(os),
62-
_ => Ordering::Equal,
63-
},
64-
other => other, // 2nd order by value types
65-
},
66-
other => other, // 1st order by key
67-
}
68-
}
69-
}
70-
71-
fn type_order(v: &Value) -> u8 {
72-
match v {
73-
Value::Bool(_) => 1,
74-
Value::I64(_) => 2,
75-
Value::F64(_) => 3,
76-
Value::String(_) => 4,
77-
Value::Array(a) => match a {
78-
Array::Bool(_) => 5,
79-
Array::I64(_) => 6,
80-
Array::F64(_) => 7,
81-
Array::String(_) => 8,
82-
},
42+
self.0.key.cmp(&other.0.key)
8343
}
8444
}
8545

opentelemetry-sdk/src/metrics/mod.rs

+98
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,104 @@ mod tests {
607607
assert_eq!(data_point.value, 30);
608608
}
609609

610+
// "multi_thread" tokio flavor must be used else flush won't
611+
// be able to make progress!
612+
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
613+
async fn counter_aggregation_attribute_order() {
614+
// Run this test with stdout enabled to see output.
615+
// cargo test counter_aggregation_attribute_order --features=metrics,testing -- --nocapture
616+
617+
// Arrange
618+
let exporter = InMemoryMetricsExporter::default();
619+
let reader = PeriodicReader::builder(exporter.clone(), runtime::Tokio).build();
620+
let meter_provider = SdkMeterProvider::builder().with_reader(reader).build();
621+
622+
// Act
623+
let meter = meter_provider.meter("test");
624+
let counter = meter.u64_counter("my_counter").init();
625+
// Add the same set of attributes in different order. (they are expected
626+
// to be treated as same attributes)
627+
counter.add(
628+
1,
629+
&[
630+
KeyValue::new("A", "a"),
631+
KeyValue::new("B", "b"),
632+
KeyValue::new("C", "c"),
633+
],
634+
);
635+
counter.add(
636+
1,
637+
&[
638+
KeyValue::new("A", "a"),
639+
KeyValue::new("C", "c"),
640+
KeyValue::new("B", "b"),
641+
],
642+
);
643+
counter.add(
644+
1,
645+
&[
646+
KeyValue::new("B", "b"),
647+
KeyValue::new("A", "a"),
648+
KeyValue::new("C", "c"),
649+
],
650+
);
651+
counter.add(
652+
1,
653+
&[
654+
KeyValue::new("B", "b"),
655+
KeyValue::new("C", "c"),
656+
KeyValue::new("A", "a"),
657+
],
658+
);
659+
counter.add(
660+
1,
661+
&[
662+
KeyValue::new("C", "c"),
663+
KeyValue::new("B", "b"),
664+
KeyValue::new("A", "a"),
665+
],
666+
);
667+
counter.add(
668+
1,
669+
&[
670+
KeyValue::new("C", "c"),
671+
KeyValue::new("A", "a"),
672+
KeyValue::new("B", "b"),
673+
],
674+
);
675+
676+
meter_provider.force_flush().unwrap();
677+
678+
// Assert
679+
let resource_metrics = exporter
680+
.get_finished_metrics()
681+
.expect("metrics are expected to be exported.");
682+
assert!(!resource_metrics.is_empty());
683+
let metric = &resource_metrics[0].scope_metrics[0].metrics[0];
684+
assert_eq!(metric.name, "my_counter");
685+
let sum = metric
686+
.data
687+
.as_any()
688+
.downcast_ref::<data::Sum<u64>>()
689+
.expect("Sum aggregation expected for Counter instruments by default");
690+
691+
// Expecting 1 time-series.
692+
assert_eq!(
693+
sum.data_points.len(),
694+
1,
695+
"Expected only one data point as attributes are same, but just reordered."
696+
);
697+
assert_eq!(
698+
sum.temporality,
699+
data::Temporality::Cumulative,
700+
"Should produce cumulative by default."
701+
);
702+
703+
// validate the sole datapoint
704+
let data_point1 = &sum.data_points[0];
705+
assert_eq!(data_point1.value, 6);
706+
}
707+
610708
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
611709
async fn no_attr_cumulative_counter() {
612710
let mut test_context = TestContext::new(Some(Temporality::Cumulative));

0 commit comments

Comments
 (0)