LCOV - code coverage report
Current view: top level - pageserver/src/tenant/storage_layer/layer - failpoints.rs (source / functions) Coverage Total Hit
Test: b4ae4c4857f9ef3e144e982a35ee23bc84c71983.info Lines: 87.5 % 48 42
Test Date: 2024-10-22 22:13:45 Functions: 81.8 % 11 9

            Line data    Source code
       1              : //! failpoints for unit tests, implying `#[cfg(test)]`.
       2              : //!
       3              : //! These are not accessible over http.
       4              : 
       5              : use super::*;
       6              : 
       7              : impl Layer {
       8              :     /// Enable a failpoint from a unit test.
       9           18 :     pub(super) fn enable_failpoint(&self, failpoint: Failpoint) {
      10           18 :         self.0.failpoints.lock().unwrap().push(failpoint);
      11           18 :     }
      12              : }
      13              : 
      14              : impl LayerInner {
      15              :     /// Query if this failpoint is enabled, as in, arrive at a failpoint.
      16              :     ///
      17              :     /// Calls to this method need to be `#[cfg(test)]` guarded.
      18           38 :     pub(super) async fn failpoint(&self, kind: FailpointKind) -> Result<(), FailpointHit> {
      19           20 :         let fut = {
      20           38 :             let mut fps = self.failpoints.lock().unwrap();
      21           38 :             // find the *last* failpoint for cases in which we need to use multiple for the same
      22           38 :             // thing (two blocked evictions)
      23           38 :             let fp = fps.iter_mut().rfind(|x| x.kind() == kind);
      24              : 
      25           38 :             let Some(fp) = fp else {
      26           18 :                 return Ok(());
      27              :             };
      28              : 
      29           20 :             fp.hit()
      30           20 :         };
      31           20 : 
      32           20 :         fut.await
      33           38 :     }
      34              : }
      35              : 
      36              : #[derive(Debug, PartialEq, Eq)]
      37              : pub(crate) enum FailpointKind {
      38              :     /// Failpoint acts as an accurate cancelled by drop here; see the only site of use.
      39              :     AfterDeterminingLayerNeedsNoDownload,
      40              :     /// Failpoint for stalling eviction starting
      41              :     WaitBeforeStartingEvicting,
      42              :     /// Failpoint hit in the spawned task
      43              :     WaitBeforeDownloading,
      44              : }
      45              : 
      46              : pub(crate) enum Failpoint {
      47              :     AfterDeterminingLayerNeedsNoDownload,
      48              :     WaitBeforeStartingEvicting(
      49              :         Option<utils::completion::Completion>,
      50              :         utils::completion::Barrier,
      51              :     ),
      52              :     WaitBeforeDownloading(
      53              :         Option<utils::completion::Completion>,
      54              :         utils::completion::Barrier,
      55              :     ),
      56              : }
      57              : 
      58              : impl Failpoint {
      59           32 :     fn kind(&self) -> FailpointKind {
      60           32 :         match self {
      61              :             Failpoint::AfterDeterminingLayerNeedsNoDownload => {
      62            4 :                 FailpointKind::AfterDeterminingLayerNeedsNoDownload
      63              :             }
      64           24 :             Failpoint::WaitBeforeStartingEvicting(..) => FailpointKind::WaitBeforeStartingEvicting,
      65            4 :             Failpoint::WaitBeforeDownloading(..) => FailpointKind::WaitBeforeDownloading,
      66              :         }
      67           32 :     }
      68              : 
      69           20 :     fn hit(&mut self) -> impl std::future::Future<Output = Result<(), FailpointHit>> + 'static {
      70              :         use futures::future::FutureExt;
      71              : 
      72              :         // use boxed futures to avoid Either hurdles
      73           20 :         match self {
      74              :             Failpoint::AfterDeterminingLayerNeedsNoDownload => {
      75            2 :                 let kind = self.kind();
      76              : 
      77            2 :                 async move { Err(FailpointHit(kind)) }.boxed()
      78              :             }
      79           16 :             Failpoint::WaitBeforeStartingEvicting(arrival, b)
      80            2 :             | Failpoint::WaitBeforeDownloading(arrival, b) => {
      81              :                 // first one signals arrival
      82           18 :                 drop(arrival.take());
      83           18 : 
      84           18 :                 let b = b.clone();
      85              : 
      86           18 :                 async move {
      87           18 :                     tracing::trace!("waiting on a failpoint barrier");
      88           18 :                     b.wait().await;
      89           18 :                     tracing::trace!("done waiting on a failpoint barrier");
      90           18 :                     Ok(())
      91           18 :                 }
      92           18 :                 .boxed()
      93              :             }
      94              :         }
      95           20 :     }
      96              : }
      97              : 
      98              : impl std::fmt::Display for FailpointKind {
      99            0 :     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     100            0 :         std::fmt::Debug::fmt(self, f)
     101            0 :     }
     102              : }
     103              : 
     104              : #[derive(Debug)]
     105              : pub(crate) struct FailpointHit(FailpointKind);
     106              : 
     107              : impl std::fmt::Display for FailpointHit {
     108            0 :     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     109            0 :         std::fmt::Debug::fmt(self, f)
     110            0 :     }
     111              : }
     112              : 
     113              : impl std::error::Error for FailpointHit {}
     114              : 
     115              : impl From<FailpointHit> for DownloadError {
     116            2 :     fn from(value: FailpointHit) -> Self {
     117            2 :         DownloadError::Failpoint(value.0)
     118            2 :     }
     119              : }
        

Generated by: LCOV version 2.1-beta