LCOV - differential code coverage report
Current view: top level - libs/remote_storage/src/s3_bucket - metrics.rs (source / functions) Coverage Total Hit LBC UBC CBC
Current: cd44433dd675caa99df17a61b18949c8387e2242.info Lines: 95.7 % 117 112 2 3 112
Current Date: 2024-01-09 02:06:09 Functions: 88.1 % 42 37 5 37
Baseline: 66c52a629a0f4a503e193045e0df4c77139e344b.info
Baseline Date: 2024-01-08 15:34:46

           TLA  Line data    Source code
       1                 : use metrics::{
       2                 :     register_histogram_vec, register_int_counter, register_int_counter_vec, Histogram, IntCounter,
       3                 : };
       4                 : use once_cell::sync::Lazy;
       5                 : 
       6                 : pub(super) static BUCKET_METRICS: Lazy<BucketMetrics> = Lazy::new(Default::default);
       7                 : 
       8 UBC           0 : #[derive(Clone, Copy, Debug)]
       9                 : pub(crate) enum RequestKind {
      10                 :     Get = 0,
      11                 :     Put = 1,
      12                 :     Delete = 2,
      13                 :     List = 3,
      14                 :     Copy = 4,
      15                 : }
      16                 : 
      17                 : use RequestKind::*;
      18                 : 
      19                 : impl RequestKind {
      20 CBC        5400 :     const fn as_str(&self) -> &'static str {
      21            5400 :         match self {
      22            1080 :             Get => "get_object",
      23            1080 :             Put => "put_object",
      24            1080 :             Delete => "delete_object",
      25            1080 :             List => "list_objects",
      26            1080 :             Copy => "copy_object",
      27                 :         }
      28            5400 :     }
      29           62387 :     const fn as_index(&self) -> usize {
      30           62387 :         *self as usize
      31           62387 :     }
      32                 : }
      33                 : 
      34                 : pub(super) struct RequestTyped<C>([C; 5]);
      35                 : 
      36                 : impl<C> RequestTyped<C> {
      37           56987 :     pub(super) fn get(&self, kind: RequestKind) -> &C {
      38           56987 :         &self.0[kind.as_index()]
      39           56987 :     }
      40                 : 
      41            1080 :     fn build_with(mut f: impl FnMut(RequestKind) -> C) -> Self {
      42            1080 :         use RequestKind::*;
      43            1080 :         let mut it = [Get, Put, Delete, List, Copy].into_iter();
      44            5400 :         let arr = std::array::from_fn::<C, 5, _>(|index| {
      45            5400 :             let next = it.next().unwrap();
      46            5400 :             assert_eq!(index, next.as_index());
      47            5400 :             f(next)
      48            5400 :         });
      49                 : 
      50            1080 :         if let Some(next) = it.next() {
      51 UBC           0 :             panic!("unexpected {next:?}");
      52 CBC        1080 :         }
      53            1080 : 
      54            1080 :         RequestTyped(arr)
      55            1080 :     }
      56                 : }
      57                 : 
      58                 : impl RequestTyped<Histogram> {
      59           28494 :     pub(super) fn observe_elapsed(&self, kind: RequestKind, started_at: std::time::Instant) {
      60           28494 :         self.get(kind).observe(started_at.elapsed().as_secs_f64())
      61           28494 :     }
      62                 : }
      63                 : 
      64                 : pub(super) struct PassFailCancelledRequestTyped<C> {
      65                 :     success: RequestTyped<C>,
      66                 :     fail: RequestTyped<C>,
      67                 :     cancelled: RequestTyped<C>,
      68                 : }
      69                 : 
      70 UBC           0 : #[derive(Debug, Clone, Copy)]
      71                 : pub(super) enum AttemptOutcome {
      72                 :     Ok,
      73                 :     Err,
      74                 :     Cancelled,
      75                 : }
      76                 : 
      77                 : impl<T, E> From<&Result<T, E>> for AttemptOutcome {
      78 CBC       17943 :     fn from(value: &Result<T, E>) -> Self {
      79           17943 :         match value {
      80           17943 :             Ok(_) => AttemptOutcome::Ok,
      81 LBC         (1) :             Err(_) => AttemptOutcome::Err,
      82                 :         }
      83 CBC       17943 :     }
      84                 : }
      85                 : 
      86                 : impl AttemptOutcome {
      87            3240 :     pub(super) fn as_str(&self) -> &'static str {
      88            3240 :         match self {
      89            1080 :             AttemptOutcome::Ok => "ok",
      90            1080 :             AttemptOutcome::Err => "err",
      91            1080 :             AttemptOutcome::Cancelled => "cancelled",
      92                 :         }
      93            3240 :     }
      94                 : }
      95                 : 
      96                 : impl<C> PassFailCancelledRequestTyped<C> {
      97           28493 :     pub(super) fn get(&self, kind: RequestKind, outcome: AttemptOutcome) -> &C {
      98           28493 :         let target = match outcome {
      99           28452 :             AttemptOutcome::Ok => &self.success,
     100 LBC         (1) :             AttemptOutcome::Err => &self.fail,
     101 CBC          41 :             AttemptOutcome::Cancelled => &self.cancelled,
     102                 :         };
     103           28493 :         target.get(kind)
     104           28493 :     }
     105                 : 
     106             216 :     fn build_with(mut f: impl FnMut(RequestKind, AttemptOutcome) -> C) -> Self {
     107            1080 :         let success = RequestTyped::build_with(|kind| f(kind, AttemptOutcome::Ok));
     108            1080 :         let fail = RequestTyped::build_with(|kind| f(kind, AttemptOutcome::Err));
     109            1080 :         let cancelled = RequestTyped::build_with(|kind| f(kind, AttemptOutcome::Cancelled));
     110             216 : 
     111             216 :         PassFailCancelledRequestTyped {
     112             216 :             success,
     113             216 :             fail,
     114             216 :             cancelled,
     115             216 :         }
     116             216 :     }
     117                 : }
     118                 : 
     119                 : impl PassFailCancelledRequestTyped<Histogram> {
     120           28401 :     pub(super) fn observe_elapsed(
     121           28401 :         &self,
     122           28401 :         kind: RequestKind,
     123           28401 :         outcome: impl Into<AttemptOutcome>,
     124           28401 :         started_at: std::time::Instant,
     125           28401 :     ) {
     126           28401 :         self.get(kind, outcome.into())
     127           28401 :             .observe(started_at.elapsed().as_secs_f64())
     128           28401 :     }
     129                 : }
     130                 : 
     131                 : pub(super) struct BucketMetrics {
     132                 :     /// Full request duration until successful completion, error or cancellation.
     133                 :     pub(super) req_seconds: PassFailCancelledRequestTyped<Histogram>,
     134                 :     /// Total amount of seconds waited on queue.
     135                 :     pub(super) wait_seconds: RequestTyped<Histogram>,
     136                 : 
     137                 :     /// Track how many semaphore awaits were cancelled per request type.
     138                 :     ///
     139                 :     /// This is in case cancellations are happening more than expected.
     140                 :     pub(super) cancelled_waits: RequestTyped<IntCounter>,
     141                 : 
     142                 :     /// Total amount of deleted objects in batches or single requests.
     143                 :     pub(super) deleted_objects_total: IntCounter,
     144                 : }
     145                 : 
     146                 : impl Default for BucketMetrics {
     147             216 :     fn default() -> Self {
     148             216 :         let buckets = [0.01, 0.10, 0.5, 1.0, 5.0, 10.0, 50.0, 100.0];
     149             216 : 
     150             216 :         let req_seconds = register_histogram_vec!(
     151             216 :             "remote_storage_s3_request_seconds",
     152             216 :             "Seconds to complete a request",
     153             216 :             &["request_type", "result"],
     154             216 :             buckets.to_vec(),
     155             216 :         )
     156             216 :         .unwrap();
     157            3240 :         let req_seconds = PassFailCancelledRequestTyped::build_with(|kind, outcome| {
     158            3240 :             req_seconds.with_label_values(&[kind.as_str(), outcome.as_str()])
     159            3240 :         });
     160             216 : 
     161             216 :         let wait_seconds = register_histogram_vec!(
     162             216 :             "remote_storage_s3_wait_seconds",
     163             216 :             "Seconds rate limited",
     164             216 :             &["request_type"],
     165             216 :             buckets.to_vec(),
     166             216 :         )
     167             216 :         .unwrap();
     168             216 :         let wait_seconds =
     169            1080 :             RequestTyped::build_with(|kind| wait_seconds.with_label_values(&[kind.as_str()]));
     170             216 : 
     171             216 :         let cancelled_waits = register_int_counter_vec!(
     172             216 :             "remote_storage_s3_cancelled_waits_total",
     173             216 :             "Times a semaphore wait has been cancelled per request type",
     174             216 :             &["request_type"],
     175             216 :         )
     176             216 :         .unwrap();
     177             216 :         let cancelled_waits =
     178            1080 :             RequestTyped::build_with(|kind| cancelled_waits.with_label_values(&[kind.as_str()]));
     179             216 : 
     180             216 :         let deleted_objects_total = register_int_counter!(
     181             216 :             "remote_storage_s3_deleted_objects_total",
     182             216 :             "Amount of deleted objects in total",
     183             216 :         )
     184             216 :         .unwrap();
     185             216 : 
     186             216 :         Self {
     187             216 :             req_seconds,
     188             216 :             wait_seconds,
     189             216 :             cancelled_waits,
     190             216 :             deleted_objects_total,
     191             216 :         }
     192             216 :     }
     193                 : }
        

Generated by: LCOV version 2.1-beta