Skip to content

Commit 507b480

Browse files
committed
fix: [#1514] bug. Don't allow merge metric collections with the same metrin name
It was not possible to merge counters or gauges if the metric names was duplicate but possible when the metric name was duplciate across types. For example, the target collection (the one is mutated) contains a counter with a name that is being used in the source collection (the wan we get metric from) for a gauge metric.
1 parent 376f242 commit 507b480

File tree

1 file changed

+82
-0
lines changed

1 file changed

+82
-0
lines changed

packages/metrics/src/metric_collection.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,33 @@ impl MetricCollection {
5050
///
5151
/// Returns an error if a metric name already exists in the current collection.
5252
pub fn merge(&mut self, other: &Self) -> Result<(), Error> {
53+
self.check_cross_type_collision(other)?;
5354
self.counters.merge(&other.counters)?;
5455
self.gauges.merge(&other.gauges)?;
5556
Ok(())
5657
}
5758

59+
/// Returns a set of all metric names in this collection.
60+
fn collect_names(&self) -> HashSet<MetricName> {
61+
self.counters.names().chain(self.gauges.names()).cloned().collect()
62+
}
63+
64+
/// Checks for name collisions between this collection and another one.
65+
fn check_cross_type_collision(&self, other: &Self) -> Result<(), Error> {
66+
let self_names: HashSet<_> = self.collect_names();
67+
let other_names: HashSet<_> = other.collect_names();
68+
69+
let cross_type_collisions = self_names.intersection(&other_names).next();
70+
71+
if let Some(name) = cross_type_collisions {
72+
return Err(Error::MetricNameCollisionInMerge {
73+
metric_name: (*name).clone(),
74+
});
75+
}
76+
77+
Ok(())
78+
}
79+
5880
// Counter-specific methods
5981

6082
pub fn describe_counter(&mut self, name: &MetricName, opt_unit: Option<Unit>, opt_description: Option<MetricDescription>) {
@@ -774,6 +796,66 @@ mod tests {
774796
assert_eq!(prometheus_output, "");
775797
}
776798

799+
#[test]
800+
fn it_should_allow_merging_metric_collections() {
801+
let time = DurationSinceUnixEpoch::from_secs(1_743_552_000);
802+
let label_set: LabelSet = (label_name!("label_name"), LabelValue::new("value")).into();
803+
804+
let mut collection1 = MetricCollection::default();
805+
collection1
806+
.increase_counter(&metric_name!("test_counter"), &label_set, time)
807+
.unwrap();
808+
809+
let mut collection2 = MetricCollection::default();
810+
collection2
811+
.set_gauge(&metric_name!("test_gauge"), &label_set, 1.0, time)
812+
.unwrap();
813+
814+
collection1.merge(&collection2).unwrap();
815+
816+
assert!(collection1.contains_counter(&metric_name!("test_counter")));
817+
assert!(collection1.contains_gauge(&metric_name!("test_gauge")));
818+
}
819+
820+
#[test]
821+
fn it_should_not_allow_merging_metric_collections_with_name_collisions_for_the_same_metric_types() {
822+
let time = DurationSinceUnixEpoch::from_secs(1_743_552_000);
823+
let label_set: LabelSet = (label_name!("label_name"), LabelValue::new("value")).into();
824+
825+
let mut collection1 = MetricCollection::default();
826+
collection1
827+
.increase_counter(&metric_name!("test_metric"), &label_set, time)
828+
.unwrap();
829+
830+
let mut collection2 = MetricCollection::default();
831+
collection2
832+
.increase_counter(&metric_name!("test_metric"), &label_set, time)
833+
.unwrap();
834+
let result = collection1.merge(&collection2);
835+
836+
assert!(result.is_err());
837+
}
838+
839+
#[test]
840+
fn it_should_not_allow_merging_metric_collections_with_name_collisions_for_different_metric_types() {
841+
let time = DurationSinceUnixEpoch::from_secs(1_743_552_000);
842+
let label_set: LabelSet = (label_name!("label_name"), LabelValue::new("value")).into();
843+
844+
let mut collection1 = MetricCollection::default();
845+
collection1
846+
.increase_counter(&metric_name!("test_metric"), &label_set, time)
847+
.unwrap();
848+
849+
let mut collection2 = MetricCollection::default();
850+
collection2
851+
.set_gauge(&metric_name!("test_metric"), &label_set, 1.0, time)
852+
.unwrap();
853+
854+
let result = collection1.merge(&collection2);
855+
856+
assert!(result.is_err());
857+
}
858+
777859
mod for_counters {
778860

779861
use pretty_assertions::assert_eq;

0 commit comments

Comments
 (0)