LCOV - code coverage report
Current view: top level - proxy/src/cache - common.rs (source / functions) Coverage Total Hit
Test: ef1c66bb4fbe62e3fa18f8b9d22d3134c7ecd2da.info Lines: 79.5 % 73 58
Test Date: 2025-07-25 10:34:39 Functions: 66.7 % 21 14

            Line data    Source code
       1              : use std::{
       2              :     ops::{Deref, DerefMut},
       3              :     time::{Duration, Instant},
       4              : };
       5              : 
       6              : use moka::Expiry;
       7              : 
       8              : use crate::control_plane::messages::ControlPlaneErrorMessage;
       9              : 
      10              : /// Default TTL used when caching errors from control plane.
      11              : pub const DEFAULT_ERROR_TTL: Duration = Duration::from_secs(30);
      12              : 
      13              : /// A generic trait which exposes types of cache's key and value,
      14              : /// as well as the notion of cache entry invalidation.
      15              : /// This is useful for [`Cached`].
      16              : pub(crate) trait Cache {
      17              :     /// Entry's key.
      18              :     type Key;
      19              : 
      20              :     /// Entry's value.
      21              :     type Value;
      22              : 
      23              :     /// Used for entry invalidation.
      24              :     type LookupInfo<Key>;
      25              : 
      26              :     /// Invalidate an entry using a lookup info.
      27              :     /// We don't have an empty default impl because it's error-prone.
      28              :     fn invalidate(&self, _: &Self::LookupInfo<Self::Key>);
      29              : }
      30              : 
      31              : impl<C: Cache> Cache for &C {
      32              :     type Key = C::Key;
      33              :     type Value = C::Value;
      34              :     type LookupInfo<Key> = C::LookupInfo<Key>;
      35              : 
      36            6 :     fn invalidate(&self, info: &Self::LookupInfo<Self::Key>) {
      37            6 :         C::invalidate(self, info);
      38            6 :     }
      39              : }
      40              : 
      41              : /// Wrapper for convenient entry invalidation.
      42              : pub(crate) struct Cached<C: Cache, V = <C as Cache>::Value> {
      43              :     /// Cache + lookup info.
      44              :     pub(crate) token: Option<(C, C::LookupInfo<C::Key>)>,
      45              : 
      46              :     /// The value itself.
      47              :     pub(crate) value: V,
      48              : }
      49              : 
      50              : impl<C: Cache, V> Cached<C, V> {
      51              :     /// Place any entry into this wrapper; invalidation will be a no-op.
      52            1 :     pub(crate) fn new_uncached(value: V) -> Self {
      53            1 :         Self { token: None, value }
      54            1 :     }
      55              : 
      56            0 :     pub(crate) fn take_value(self) -> (Cached<C, ()>, V) {
      57            0 :         (
      58            0 :             Cached {
      59            0 :                 token: self.token,
      60            0 :                 value: (),
      61            0 :             },
      62            0 :             self.value,
      63            0 :         )
      64            0 :     }
      65              : 
      66           17 :     pub(crate) fn map<U>(self, f: impl FnOnce(V) -> U) -> Cached<C, U> {
      67           17 :         Cached {
      68           17 :             token: self.token,
      69           17 :             value: f(self.value),
      70           17 :         }
      71           17 :     }
      72              : 
      73              :     /// Drop this entry from a cache if it's still there.
      74            6 :     pub(crate) fn invalidate(self) -> V {
      75            6 :         if let Some((cache, info)) = &self.token {
      76            6 :             cache.invalidate(info);
      77            6 :         }
      78            6 :         self.value
      79            6 :     }
      80              : 
      81              :     /// Tell if this entry is actually cached.
      82           16 :     pub(crate) fn cached(&self) -> bool {
      83           16 :         self.token.is_some()
      84           16 :     }
      85              : }
      86              : 
      87              : impl<C: Cache, V> Deref for Cached<C, V> {
      88              :     type Target = V;
      89              : 
      90            0 :     fn deref(&self) -> &Self::Target {
      91            0 :         &self.value
      92            0 :     }
      93              : }
      94              : 
      95              : impl<C: Cache, V> DerefMut for Cached<C, V> {
      96            0 :     fn deref_mut(&mut self) -> &mut Self::Target {
      97            0 :         &mut self.value
      98            0 :     }
      99              : }
     100              : 
     101              : pub type ControlPlaneResult<T> = Result<T, Box<ControlPlaneErrorMessage>>;
     102              : 
     103              : #[derive(Clone, Copy)]
     104              : pub struct CplaneExpiry {
     105              :     pub error: Duration,
     106              : }
     107              : 
     108              : impl Default for CplaneExpiry {
     109            2 :     fn default() -> Self {
     110            2 :         Self {
     111            2 :             error: DEFAULT_ERROR_TTL,
     112            2 :         }
     113            2 :     }
     114              : }
     115              : 
     116              : impl CplaneExpiry {
     117           12 :     pub fn expire_early<V>(
     118           12 :         &self,
     119           12 :         value: &ControlPlaneResult<V>,
     120           12 :         updated: Instant,
     121           12 :     ) -> Option<Duration> {
     122           12 :         match value {
     123            8 :             Ok(_) => None,
     124            4 :             Err(err) => Some(self.expire_err_early(err, updated)),
     125              :         }
     126           12 :     }
     127              : 
     128            4 :     pub fn expire_err_early(&self, err: &ControlPlaneErrorMessage, updated: Instant) -> Duration {
     129            4 :         err.status
     130            4 :             .as_ref()
     131            4 :             .and_then(|s| s.details.retry_info.as_ref())
     132            4 :             .map_or(self.error, |r| r.retry_at.into_std() - updated)
     133            4 :     }
     134              : }
     135              : 
     136              : impl<K, V> Expiry<K, ControlPlaneResult<V>> for CplaneExpiry {
     137            7 :     fn expire_after_create(
     138            7 :         &self,
     139            7 :         _key: &K,
     140            7 :         value: &ControlPlaneResult<V>,
     141            7 :         created_at: Instant,
     142            7 :     ) -> Option<Duration> {
     143            7 :         self.expire_early(value, created_at)
     144            7 :     }
     145              : 
     146            5 :     fn expire_after_update(
     147            5 :         &self,
     148            5 :         _key: &K,
     149            5 :         value: &ControlPlaneResult<V>,
     150            5 :         updated_at: Instant,
     151            5 :         _duration_until_expiry: Option<Duration>,
     152            5 :     ) -> Option<Duration> {
     153            5 :         self.expire_early(value, updated_at)
     154            5 :     }
     155              : }
        

Generated by: LCOV version 2.1-beta