LCOV - code coverage report
Current view: top level - libs/utils/src - generation.rs (source / functions) Coverage Total Hit
Test: 32f4a56327bc9da697706839ed4836b2a00a408f.info Lines: 80.3 % 71 57
Test Date: 2024-02-07 07:37:29 Functions: 63.6 % 33 21

            Line data    Source code
       1              : use std::fmt::Debug;
       2              : 
       3              : use serde::{Deserialize, Serialize};
       4              : 
       5              : /// Tenant generations are used to provide split-brain safety and allow
       6              : /// multiple pageservers to attach the same tenant concurrently.
       7              : ///
       8              : /// See docs/rfcs/025-generation-numbers.md for detail on how generation
       9              : /// numbers are used.
      10       703686 : #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
      11              : pub enum Generation {
      12              :     // Generations with this magic value will not add a suffix to S3 keys, and will not
      13              :     // be included in persisted index_part.json.  This value is only to be used
      14              :     // during migration from pre-generation metadata to generation-aware metadata,
      15              :     // and should eventually go away.
      16              :     //
      17              :     // A special Generation is used rather than always wrapping Generation in an Option,
      18              :     // so that code handling generations doesn't have to be aware of the legacy
      19              :     // case everywhere it touches a generation.
      20              :     None,
      21              :     // Generations with this magic value may never be used to construct S3 keys:
      22              :     // we will panic if someone tries to.  This is for Tenants in the "Broken" state,
      23              :     // so that we can satisfy their constructor with a Generation without risking
      24              :     // a code bug using it in an S3 write (broken tenants should never write)
      25              :     Broken,
      26              :     Valid(u32),
      27              : }
      28              : 
      29              : /// The Generation type represents a number associated with a Tenant, which
      30              : /// increments every time the tenant is attached to a new pageserver, or
      31              : /// an attached pageserver restarts.
      32              : ///
      33              : /// It is included as a suffix in S3 keys, as a protection against split-brain
      34              : /// scenarios where pageservers might otherwise issue conflicting writes to
      35              : /// remote storage
      36              : impl Generation {
      37              :     /// Create a new Generation that represents a legacy key format with
      38              :     /// no generation suffix
      39          926 :     pub fn none() -> Self {
      40          926 :         Self::None
      41          926 :     }
      42              : 
      43              :     // Create a new generation that will panic if you try to use get_suffix
      44            0 :     pub fn broken() -> Self {
      45            0 :         Self::Broken
      46            0 :     }
      47              : 
      48         3691 :     pub fn new(v: u32) -> Self {
      49         3691 :         Self::Valid(v)
      50         3691 :     }
      51              : 
      52      1251691 :     pub fn is_none(&self) -> bool {
      53      1251691 :         matches!(self, Self::None)
      54      1251691 :     }
      55              : 
      56              :     #[track_caller]
      57        90516 :     pub fn get_suffix(&self) -> String {
      58        90516 :         match self {
      59        88814 :             Self::Valid(v) => {
      60        88814 :                 format!("-{:08x}", v)
      61              :             }
      62         1702 :             Self::None => "".into(),
      63              :             Self::Broken => {
      64            0 :                 panic!("Tried to use a broken generation");
      65              :             }
      66              :         }
      67        90516 :     }
      68              : 
      69              :     /// `suffix` is the part after "-" in a key
      70              :     ///
      71              :     /// Returns None if parsing was unsuccessful
      72          671 :     pub fn parse_suffix(suffix: &str) -> Option<Generation> {
      73          671 :         u32::from_str_radix(suffix, 16).map(Generation::new).ok()
      74          671 :     }
      75              : 
      76              :     #[track_caller]
      77          451 :     pub fn previous(&self) -> Generation {
      78          451 :         match self {
      79          451 :             Self::Valid(n) => {
      80          451 :                 if *n == 0 {
      81              :                     // Since a tenant may be upgraded from a pre-generations state, interpret the "previous" generation
      82              :                     // to 0 as being "no generation".
      83            0 :                     Self::None
      84              :                 } else {
      85          451 :                     Self::Valid(n - 1)
      86              :                 }
      87              :             }
      88            0 :             Self::None => Self::None,
      89            0 :             Self::Broken => panic!("Attempted to use a broken generation"),
      90              :         }
      91          451 :     }
      92              : 
      93            2 :     pub fn next(&self) -> Generation {
      94            2 :         match self {
      95            2 :             Self::Valid(n) => Self::Valid(*n + 1),
      96            0 :             Self::None => Self::Valid(1),
      97            0 :             Self::Broken => panic!("Attempted to use a broken generation"),
      98              :         }
      99            2 :     }
     100              : 
     101         4449 :     pub fn into(self) -> Option<u32> {
     102         4449 :         if let Self::Valid(v) = self {
     103         4449 :             Some(v)
     104              :         } else {
     105            0 :             None
     106              :         }
     107         4449 :     }
     108              : }
     109              : 
     110              : impl Serialize for Generation {
     111       608828 :     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     112       608828 :     where
     113       608828 :         S: serde::Serializer,
     114       608828 :     {
     115       608828 :         if let Self::Valid(v) = self {
     116       608828 :             v.serialize(serializer)
     117              :         } else {
     118              :             // We should never be asked to serialize a None or Broken.  Structures
     119              :             // that include an optional generation should convert None to an
     120              :             // Option<Generation>::None
     121            0 :             Err(serde::ser::Error::custom(
     122            0 :                 "Tried to serialize invalid generation ({self})",
     123            0 :             ))
     124              :         }
     125       608828 :     }
     126              : }
     127              : 
     128              : impl<'de> Deserialize<'de> for Generation {
     129        59482 :     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     130        59482 :     where
     131        59482 :         D: serde::Deserializer<'de>,
     132        59482 :     {
     133        59482 :         Ok(Self::Valid(u32::deserialize(deserializer)?))
     134        59482 :     }
     135              : }
     136              : 
     137              : // We intentionally do not implement Display for Generation, to reduce the
     138              : // risk of a bug where the generation is used in a format!() string directly
     139              : // instead of using get_suffix().
     140              : impl Debug for Generation {
     141        29390 :     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     142        29390 :         match self {
     143        28831 :             Self::Valid(v) => {
     144        28831 :                 write!(f, "{:08x}", v)
     145              :             }
     146              :             Self::None => {
     147          559 :                 write!(f, "<none>")
     148              :             }
     149              :             Self::Broken => {
     150            0 :                 write!(f, "<broken>")
     151              :             }
     152              :         }
     153        29390 :     }
     154              : }
     155              : 
     156              : #[cfg(test)]
     157              : mod test {
     158              :     use super::*;
     159              : 
     160            2 :     #[test]
     161            2 :     fn generation_gt() {
     162              :         // Important that a None generation compares less than a valid one, during upgrades from
     163              :         // pre-generation systems.
     164            2 :         assert!(Generation::none() < Generation::new(0));
     165            2 :         assert!(Generation::none() < Generation::new(1));
     166            2 :     }
     167              : }
        

Generated by: LCOV version 2.1-beta