LCOV - code coverage report
Current view: top level - proxy/src/cache - common.rs (source / functions) Coverage Total Hit
Test: c8f8d331b83562868d9054d9e0e68f866772aeaa.info Lines: 89.7 % 58 52
Test Date: 2025-07-26 17:20:05 Functions: 78.9 % 19 15

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

Generated by: LCOV version 2.1-beta