@@ -5,12 +5,136 @@ mod last_value;
5
5
mod sum;
6
6
7
7
use core:: fmt;
8
+ use std:: collections:: HashMap ;
9
+ use std:: marker:: PhantomData ;
8
10
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 } ;
11
13
14
+ use aggregate:: is_under_cardinality_limit;
12
15
pub ( crate ) use aggregate:: { AggregateBuilder , ComputeAggregation , Measure } ;
13
16
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
+ }
14
138
15
139
/// Marks a type that can have a value added and retrieved atomically. Required since
16
140
/// different types have different backing atomic mechanisms
0 commit comments