Skip to content

Commit efd6ee4

Browse files
committed
InstrumentationScope to include attributes in hash and eq check
1 parent cb81eb6 commit efd6ee4

File tree

4 files changed

+102
-5
lines changed

4 files changed

+102
-5
lines changed

opentelemetry-sdk/src/metrics/meter_provider.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,25 @@ mod tests {
562562
assert_eq!(provider.inner.meters.lock().unwrap().len(), 5);
563563
}
564564

565+
#[test]
566+
fn same_meter_reused_same_scope_attributes() {
567+
let meter_provider = super::SdkMeterProvider::builder().build();
568+
let make_scope = |attributes| {
569+
InstrumentationScope::builder("test.meter")
570+
.with_version("v0.1.0")
571+
.with_schema_url("http://example.com")
572+
.with_attributes(attributes)
573+
.build()
574+
};
575+
576+
let _meter1 =
577+
meter_provider.meter_with_scope(make_scope(vec![KeyValue::new("key", "value1")]));
578+
let _meter2 =
579+
meter_provider.meter_with_scope(make_scope(vec![KeyValue::new("key", "value1")]));
580+
581+
assert_eq!(meter_provider.inner.meters.lock().unwrap().len(), 1);
582+
}
583+
565584
#[test]
566585
fn with_resource_multiple_calls_ensure_additive() {
567586
let builder = SdkMeterProvider::builder()

opentelemetry-sdk/src/metrics/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ mod tests {
782782
.build();
783783

784784
// Act
785-
// Meters are identical except for scope attributes, but scope attributes are not an identifying property.
785+
// Meters are identical.
786786
// Hence there should be a single metric stream output for this test.
787787
let make_scope = |attributes| {
788788
InstrumentationScope::builder("test.meter")
@@ -795,7 +795,7 @@ mod tests {
795795
let meter1 =
796796
meter_provider.meter_with_scope(make_scope(vec![KeyValue::new("key", "value1")]));
797797
let meter2 =
798-
meter_provider.meter_with_scope(make_scope(vec![KeyValue::new("key", "value2")]));
798+
meter_provider.meter_with_scope(make_scope(vec![KeyValue::new("key", "value1")]));
799799

800800
let counter1 = meter1
801801
.u64_counter("my_counter")

opentelemetry/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- *Breaking* Moved `TraceError` enum from `opentelemetry::trace::TraceError` to `opentelemetry_sdk::trace::TraceError`
77
- *Breaking* Moved `TraceResult` type alias from `opentelemetry::trace::TraceResult` to `opentelemetry_sdk::trace::TraceResult`
88
- {PLACEHOLDER} - Remove the above completely. // TODO fill this when changes are actually in.
9+
- Bug Fix: `InstrumentationScope` implementation for `PartialEq` and `Hash` fixed to include Attributes also.
910

1011
## 0.28.0
1112

opentelemetry/src/common.rs

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -427,22 +427,33 @@ pub struct InstrumentationScope {
427427
attributes: Vec<KeyValue>,
428428
}
429429

430-
// Uniqueness for InstrumentationScope does not depend on attributes
431-
impl Eq for InstrumentationScope {}
432-
433430
impl PartialEq for InstrumentationScope {
434431
fn eq(&self, other: &Self) -> bool {
435432
self.name == other.name
436433
&& self.version == other.version
437434
&& self.schema_url == other.schema_url
435+
&& {
436+
let mut self_attrs = self.attributes.clone();
437+
let mut other_attrs = other.attributes.clone();
438+
self_attrs.sort_unstable_by(|a, b| a.key.cmp(&b.key));
439+
other_attrs.sort_unstable_by(|a, b| a.key.cmp(&b.key));
440+
self_attrs == other_attrs
441+
}
438442
}
439443
}
440444

445+
impl Eq for InstrumentationScope {}
446+
441447
impl hash::Hash for InstrumentationScope {
442448
fn hash<H: hash::Hasher>(&self, state: &mut H) {
443449
self.name.hash(state);
444450
self.version.hash(state);
445451
self.schema_url.hash(state);
452+
let mut sorted_attrs = self.attributes.clone();
453+
sorted_attrs.sort_unstable_by(|a, b| a.key.cmp(&b.key));
454+
for attribute in sorted_attrs {
455+
attribute.hash(state);
456+
}
446457
}
447458
}
448459

@@ -561,3 +572,69 @@ impl InstrumentationScopeBuilder {
561572
}
562573
}
563574
}
575+
576+
#[cfg(test)]
577+
mod tests {
578+
use std::hash::{Hash, Hasher};
579+
580+
use crate::{InstrumentationScope, KeyValue};
581+
582+
#[test]
583+
fn instrumentation_scope_equality() {
584+
let scope1 = InstrumentationScope::builder("my-crate")
585+
.with_version("v0.1.0")
586+
.with_schema_url("https://opentelemetry.io/schemas/1.17.0")
587+
.with_attributes([KeyValue::new("k", "v")])
588+
.build();
589+
let scope2 = InstrumentationScope::builder("my-crate")
590+
.with_version("v0.1.0")
591+
.with_schema_url("https://opentelemetry.io/schemas/1.17.0")
592+
.with_attributes([KeyValue::new("k", "v")])
593+
.build();
594+
assert_eq!(scope1, scope2);
595+
}
596+
597+
#[test]
598+
fn instrumentation_scope_equality_attributes_diff_order() {
599+
let scope1 = InstrumentationScope::builder("my-crate")
600+
.with_version("v0.1.0")
601+
.with_schema_url("https://opentelemetry.io/schemas/1.17.0")
602+
.with_attributes([KeyValue::new("k1", "v1"), KeyValue::new("k2", "v2")])
603+
.build();
604+
let scope2 = InstrumentationScope::builder("my-crate")
605+
.with_version("v0.1.0")
606+
.with_schema_url("https://opentelemetry.io/schemas/1.17.0")
607+
.with_attributes([KeyValue::new("k2", "v2"), KeyValue::new("k1", "v1")])
608+
.build();
609+
assert_eq!(scope1, scope2);
610+
611+
// assert hash are same for both scopes
612+
let mut hasher1 = std::collections::hash_map::DefaultHasher::new();
613+
scope1.hash(&mut hasher1);
614+
let mut hasher2 = std::collections::hash_map::DefaultHasher::new();
615+
scope2.hash(&mut hasher2);
616+
assert_eq!(hasher1.finish(), hasher2.finish());
617+
}
618+
619+
#[test]
620+
fn instrumentation_scope_equality_different_attributes() {
621+
let scope1 = InstrumentationScope::builder("my-crate")
622+
.with_version("v0.1.0")
623+
.with_schema_url("https://opentelemetry.io/schemas/1.17.0")
624+
.with_attributes([KeyValue::new("k1", "v1"), KeyValue::new("k2", "v2")])
625+
.build();
626+
let scope2 = InstrumentationScope::builder("my-crate")
627+
.with_version("v0.1.0")
628+
.with_schema_url("https://opentelemetry.io/schemas/1.17.0")
629+
.with_attributes([KeyValue::new("k2", "v3"), KeyValue::new("k4", "v5")])
630+
.build();
631+
assert_ne!(scope1, scope2);
632+
633+
// assert hash are same for both scopes
634+
let mut hasher1 = std::collections::hash_map::DefaultHasher::new();
635+
scope1.hash(&mut hasher1);
636+
let mut hasher2 = std::collections::hash_map::DefaultHasher::new();
637+
scope2.hash(&mut hasher2);
638+
assert_ne!(hasher1.finish(), hasher2.finish());
639+
}
640+
}

0 commit comments

Comments
 (0)