@@ -207,6 +207,40 @@ impl<S: Clone + std::hash::Hash + Eq, M, C> Family<S, M, C> {
207
207
}
208
208
}
209
209
210
+ impl < S : Clone + std:: hash:: Hash + Eq , M : Clone , C : MetricConstructor < M > > Family < S , M , C >
211
+ where
212
+ S : Clone + std:: hash:: Hash + Eq ,
213
+ M : Clone ,
214
+ C : MetricConstructor < M > ,
215
+ {
216
+ /// Access a metric with the given label set, creating it if one does not yet exist.
217
+ ///
218
+ /// ```
219
+ /// # use prometheus_client::metrics::counter::{Atomic, Counter};
220
+ /// # use prometheus_client::metrics::family::Family;
221
+ /// #
222
+ /// let family = Family::<Vec<(String, String)>, Counter>::default();
223
+ ///
224
+ /// // Will create and return the metric with label `method="GET"` when first called.
225
+ /// family.get_or_create_owned(&vec![("method".to_owned(), "GET".to_owned())]).inc();
226
+ ///
227
+ /// // Will return a clone of the existing metric on all subsequent calls.
228
+ /// family.get_or_create_owned(&vec![("method".to_owned(), "GET".to_owned())]).inc();
229
+ /// ```
230
+ ///
231
+ /// Callers wishing to avoid a clone of the metric `M` can call [`Family::get_or_create()`] to
232
+ /// return a reference to the metric instead.
233
+ pub fn get_or_create_owned ( & self , label_set : & S ) -> M {
234
+ use std:: ops:: Deref ;
235
+
236
+ let guard = self . get_or_create ( label_set) ;
237
+ let metric = guard. deref ( ) . to_owned ( ) ;
238
+ drop ( guard) ;
239
+
240
+ metric
241
+ }
242
+ }
243
+
210
244
impl < S : Clone + std:: hash:: Hash + Eq , M , C : MetricConstructor < M > > Family < S , M , C > {
211
245
/// Access a metric with the given label set, creating it if one does not
212
246
/// yet exist.
@@ -225,6 +259,10 @@ impl<S: Clone + std::hash::Hash + Eq, M, C: MetricConstructor<M>> Family<S, M, C
225
259
/// // calls.
226
260
/// family.get_or_create(&vec![("method".to_owned(), "GET".to_owned())]).inc();
227
261
/// ```
262
+ ///
263
+ /// NB: This method can cause deadlocks if multiple metrics within this family are read at
264
+ /// once. Use [`Family::get_or_create_owned()`] if you would like to avoid this by cloning the
265
+ /// metric `M`.
228
266
pub fn get_or_create ( & self , label_set : & S ) -> MappedRwLockReadGuard < M > {
229
267
if let Some ( metric) = self . get ( label_set) {
230
268
return metric;
@@ -510,4 +548,23 @@ mod tests {
510
548
let non_existent_string = string_family. get ( & "non_existent" . to_string ( ) ) ;
511
549
assert ! ( non_existent_string. is_none( ) ) ;
512
550
}
551
+
552
+ /// Tests that [`Family::get_or_create_owned()`] does not cause deadlocks.
553
+ #[ test]
554
+ fn counter_family_does_not_deadlock ( ) {
555
+ /// A structure we'll place two counters into, within a single expression.
556
+ struct S {
557
+ apples : Counter ,
558
+ oranges : Counter ,
559
+ }
560
+
561
+ let family = Family :: < ( & str , & str ) , Counter > :: default ( ) ;
562
+ let s = S {
563
+ apples : family. get_or_create_owned ( & ( "kind" , "apple" ) ) ,
564
+ oranges : family. get_or_create_owned ( & ( "kind" , "orange" ) ) ,
565
+ } ;
566
+
567
+ s. apples . inc ( ) ;
568
+ s. oranges . inc_by ( 2 ) ;
569
+ }
513
570
}
0 commit comments