LCOV - code coverage report
Current view: top level - libs/remote_storage/src/s3_bucket - metrics.rs (source / functions) Coverage Total Hit
Test: 32f4a56327bc9da697706839ed4836b2a00a408f.info Lines: 95.8 % 118 113
Test Date: 2024-02-07 07:37:29 Functions: 82.2 % 45 37

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

Generated by: LCOV version 2.1-beta