Skip to content

Commit d583695

Browse files
authored
Move ValueMap to mod file to allow for code reuse (#2012)
1 parent ed82d78 commit d583695

File tree

2 files changed

+130
-129
lines changed

2 files changed

+130
-129
lines changed

opentelemetry-sdk/src/metrics/internal/mod.rs

+126-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,136 @@ mod last_value;
55
mod sum;
66

77
use core::fmt;
8+
use std::collections::HashMap;
9+
use std::marker::PhantomData;
810
use std::ops::{Add, AddAssign, Sub};
9-
use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
10-
use std::sync::Mutex;
11+
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU64, AtomicUsize, Ordering};
12+
use std::sync::{Arc, Mutex, RwLock};
1113

14+
use aggregate::is_under_cardinality_limit;
1215
pub(crate) use aggregate::{AggregateBuilder, ComputeAggregation, Measure};
1316
pub(crate) use exponential_histogram::{EXPO_MAX_SCALE, EXPO_MIN_SCALE};
17+
use once_cell::sync::Lazy;
18+
use opentelemetry::metrics::MetricsError;
19+
use opentelemetry::{global, KeyValue};
20+
21+
use crate::metrics::AttributeSet;
22+
23+
pub(crate) static STREAM_OVERFLOW_ATTRIBUTES: Lazy<Vec<KeyValue>> =
24+
Lazy::new(|| vec![KeyValue::new("otel.metric.overflow", "true")]);
25+
26+
/// Abstracts the update operation for a measurement.
27+
pub(crate) trait Operation {
28+
fn update_tracker<T: 'static, AT: AtomicTracker<T>>(tracker: &AT, value: T);
29+
}
30+
31+
struct Increment;
32+
33+
impl Operation for Increment {
34+
fn update_tracker<T: 'static, AT: AtomicTracker<T>>(tracker: &AT, value: T) {
35+
tracker.add(value);
36+
}
37+
}
38+
39+
struct Assign;
40+
41+
impl Operation for Assign {
42+
fn update_tracker<T: 'static, AT: AtomicTracker<T>>(tracker: &AT, value: T) {
43+
tracker.store(value);
44+
}
45+
}
46+
47+
/// The storage for sums.
48+
///
49+
/// This structure is parametrized by an `Operation` that indicates how
50+
/// updates to the underlying value trackers should be performed.
51+
pub(crate) struct ValueMap<T: Number<T>, O> {
52+
/// Trackers store the values associated with different attribute sets.
53+
trackers: RwLock<HashMap<Vec<KeyValue>, Arc<T::AtomicTracker>>>,
54+
/// Number of different attribute set stored in the `trackers` map.
55+
count: AtomicUsize,
56+
/// Indicates whether a value with no attributes has been stored.
57+
has_no_attribute_value: AtomicBool,
58+
/// Tracker for values with no attributes attached.
59+
no_attribute_tracker: T::AtomicTracker,
60+
phantom: PhantomData<O>,
61+
}
62+
63+
impl<T: Number<T>, O> Default for ValueMap<T, O> {
64+
fn default() -> Self {
65+
ValueMap::new()
66+
}
67+
}
68+
69+
impl<T: Number<T>, O> ValueMap<T, O> {
70+
fn new() -> Self {
71+
ValueMap {
72+
trackers: RwLock::new(HashMap::new()),
73+
has_no_attribute_value: AtomicBool::new(false),
74+
no_attribute_tracker: T::new_atomic_tracker(),
75+
count: AtomicUsize::new(0),
76+
phantom: PhantomData,
77+
}
78+
}
79+
}
80+
81+
impl<T: Number<T>, O: Operation> ValueMap<T, O> {
82+
fn measure(&self, measurement: T, attributes: &[KeyValue]) {
83+
if attributes.is_empty() {
84+
O::update_tracker(&self.no_attribute_tracker, measurement);
85+
self.has_no_attribute_value.store(true, Ordering::Release);
86+
return;
87+
}
88+
89+
let Ok(trackers) = self.trackers.read() else {
90+
return;
91+
};
92+
93+
// Try to retrieve and update the tracker with the attributes in the provided order first
94+
if let Some(tracker) = trackers.get(attributes) {
95+
O::update_tracker(&**tracker, measurement);
96+
return;
97+
}
98+
99+
// Try to retrieve and update the tracker with the attributes sorted.
100+
let sorted_attrs = AttributeSet::from(attributes).into_vec();
101+
if let Some(tracker) = trackers.get(sorted_attrs.as_slice()) {
102+
O::update_tracker(&**tracker, measurement);
103+
return;
104+
}
105+
106+
// Give up the read lock before acquiring the write lock.
107+
drop(trackers);
108+
109+
let Ok(mut trackers) = self.trackers.write() else {
110+
return;
111+
};
112+
113+
// Recheck both the provided and sorted orders after acquiring the write lock
114+
// in case another thread has pushed an update in the meantime.
115+
if let Some(tracker) = trackers.get(attributes) {
116+
O::update_tracker(&**tracker, measurement);
117+
} else if let Some(tracker) = trackers.get(sorted_attrs.as_slice()) {
118+
O::update_tracker(&**tracker, measurement);
119+
} else if is_under_cardinality_limit(self.count.load(Ordering::SeqCst)) {
120+
let new_tracker = Arc::new(T::new_atomic_tracker());
121+
O::update_tracker(&*new_tracker, measurement);
122+
123+
// Insert tracker with the attributes in the provided and sorted orders
124+
trackers.insert(attributes.to_vec(), new_tracker.clone());
125+
trackers.insert(sorted_attrs, new_tracker);
126+
127+
self.count.fetch_add(1, Ordering::SeqCst);
128+
} else if let Some(overflow_value) = trackers.get(STREAM_OVERFLOW_ATTRIBUTES.as_slice()) {
129+
O::update_tracker(&**overflow_value, measurement);
130+
} else {
131+
let new_tracker = T::new_atomic_tracker();
132+
O::update_tracker(&new_tracker, measurement);
133+
trackers.insert(STREAM_OVERFLOW_ATTRIBUTES.clone(), Arc::new(new_tracker));
134+
global::handle_error(MetricsError::Other("Warning: Maximum data points for metric stream exceeded. Entry added to overflow. Subsequent overflows to same metric until next collect will not be logged.".into()));
135+
}
136+
}
137+
}
14138

15139
/// Marks a type that can have a value added and retrieved atomically. Required since
16140
/// different types have different backing atomic mechanisms

opentelemetry-sdk/src/metrics/internal/sum.rs

+4-127
Original file line numberDiff line numberDiff line change
@@ -1,137 +1,14 @@
11
use std::collections::HashSet;
2-
use std::marker::PhantomData;
3-
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
2+
use std::sync::atomic::Ordering;
43
use std::sync::Arc;
54
use std::vec;
6-
use std::{
7-
collections::HashMap,
8-
sync::{Mutex, RwLock},
9-
time::SystemTime,
10-
};
5+
use std::{collections::HashMap, sync::Mutex, time::SystemTime};
116

127
use crate::metrics::data::{self, Aggregation, DataPoint, Temporality};
13-
use crate::metrics::AttributeSet;
14-
use once_cell::sync::Lazy;
158
use opentelemetry::KeyValue;
16-
use opentelemetry::{global, metrics::MetricsError};
179

18-
use super::{aggregate::is_under_cardinality_limit, AtomicTracker, Number};
19-
20-
pub(crate) static STREAM_OVERFLOW_ATTRIBUTES: Lazy<Vec<KeyValue>> =
21-
Lazy::new(|| vec![KeyValue::new("otel.metric.overflow", "true")]);
22-
23-
/// Abstracts the update operation for a measurement.
24-
trait Operation {
25-
fn update_tracker<T: 'static, AT: AtomicTracker<T>>(tracker: &AT, value: T);
26-
}
27-
28-
struct Increment;
29-
30-
impl Operation for Increment {
31-
fn update_tracker<T: 'static, AT: AtomicTracker<T>>(tracker: &AT, value: T) {
32-
tracker.add(value);
33-
}
34-
}
35-
36-
struct Assign;
37-
38-
impl Operation for Assign {
39-
fn update_tracker<T: 'static, AT: AtomicTracker<T>>(tracker: &AT, value: T) {
40-
tracker.store(value);
41-
}
42-
}
43-
44-
/// The storage for sums.
45-
///
46-
/// This structure is parametrized by an `Operation` that indicates how
47-
/// updates to the underlying value trackers should be performed.
48-
struct ValueMap<T: Number<T>, O> {
49-
/// Trackers store the values associated with different attribute sets.
50-
trackers: RwLock<HashMap<Vec<KeyValue>, Arc<T::AtomicTracker>>>,
51-
/// Number of different attribute set stored in the `trackers` map.
52-
count: AtomicUsize,
53-
/// Indicates whether a value with no attributes has been stored.
54-
has_no_attribute_value: AtomicBool,
55-
/// Tracker for values with no attributes attached.
56-
no_attribute_tracker: T::AtomicTracker,
57-
phantom: PhantomData<O>,
58-
}
59-
60-
impl<T: Number<T>, O> Default for ValueMap<T, O> {
61-
fn default() -> Self {
62-
ValueMap::new()
63-
}
64-
}
65-
66-
impl<T: Number<T>, O> ValueMap<T, O> {
67-
fn new() -> Self {
68-
ValueMap {
69-
trackers: RwLock::new(HashMap::new()),
70-
has_no_attribute_value: AtomicBool::new(false),
71-
no_attribute_tracker: T::new_atomic_tracker(),
72-
count: AtomicUsize::new(0),
73-
phantom: PhantomData,
74-
}
75-
}
76-
}
77-
78-
impl<T: Number<T>, O: Operation> ValueMap<T, O> {
79-
fn measure(&self, measurement: T, attributes: &[KeyValue]) {
80-
if attributes.is_empty() {
81-
O::update_tracker(&self.no_attribute_tracker, measurement);
82-
self.has_no_attribute_value.store(true, Ordering::Release);
83-
return;
84-
}
85-
86-
let Ok(trackers) = self.trackers.read() else {
87-
return;
88-
};
89-
90-
// Try to retrieve and update the tracker with the attributes in the provided order first
91-
if let Some(tracker) = trackers.get(attributes) {
92-
O::update_tracker(&**tracker, measurement);
93-
return;
94-
}
95-
96-
// Try to retrieve and update the tracker with the attributes sorted.
97-
let sorted_attrs = AttributeSet::from(attributes).into_vec();
98-
if let Some(tracker) = trackers.get(sorted_attrs.as_slice()) {
99-
O::update_tracker(&**tracker, measurement);
100-
return;
101-
}
102-
103-
// Give up the read lock before acquiring the write lock.
104-
drop(trackers);
105-
106-
let Ok(mut trackers) = self.trackers.write() else {
107-
return;
108-
};
109-
110-
// Recheck both the provided and sorted orders after acquiring the write lock
111-
// in case another thread has pushed an update in the meantime.
112-
if let Some(tracker) = trackers.get(attributes) {
113-
O::update_tracker(&**tracker, measurement);
114-
} else if let Some(tracker) = trackers.get(sorted_attrs.as_slice()) {
115-
O::update_tracker(&**tracker, measurement);
116-
} else if is_under_cardinality_limit(self.count.load(Ordering::SeqCst)) {
117-
let new_tracker = Arc::new(T::new_atomic_tracker());
118-
O::update_tracker(&*new_tracker, measurement);
119-
120-
// Insert tracker with the attributes in the provided and sorted orders
121-
trackers.insert(attributes.to_vec(), new_tracker.clone());
122-
trackers.insert(sorted_attrs, new_tracker);
123-
124-
self.count.fetch_add(1, Ordering::SeqCst);
125-
} else if let Some(overflow_value) = trackers.get(STREAM_OVERFLOW_ATTRIBUTES.as_slice()) {
126-
O::update_tracker(&**overflow_value, measurement);
127-
} else {
128-
let new_tracker = T::new_atomic_tracker();
129-
O::update_tracker(&new_tracker, measurement);
130-
trackers.insert(STREAM_OVERFLOW_ATTRIBUTES.clone(), Arc::new(new_tracker));
131-
global::handle_error(MetricsError::Other("Warning: Maximum data points for metric stream exceeded. Entry added to overflow. Subsequent overflows to same metric until next collect will not be logged.".into()));
132-
}
133-
}
134-
}
10+
use super::{Assign, Increment, ValueMap};
11+
use super::{AtomicTracker, Number};
13512

13613
/// Summarizes a set of measurements made as their arithmetic sum.
13714
pub(crate) struct Sum<T: Number<T>> {

0 commit comments

Comments
 (0)