LCOV - code coverage report
Current view: top level - libs/utils/src - generation.rs (source / functions) Coverage Total Hit
Test: c639aa5f7ab62b43d647b10f40d15a15686ce8a9.info Lines: 85.3 % 95 81
Test Date: 2024-02-12 20:26:03 Functions: 66.7 % 36 24

            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       679147 : #[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          933 :     pub fn none() -> Self {
      40          933 :         Self::None
      41          933 :     }
      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         3714 :     pub fn new(v: u32) -> Self {
      49         3714 :         Self::Valid(v)
      50         3714 :     }
      51              : 
      52      1215067 :     pub fn is_none(&self) -> bool {
      53      1215067 :         matches!(self, Self::None)
      54      1215067 :     }
      55              : 
      56              :     #[track_caller]
      57        91388 :     pub fn get_suffix(&self) -> impl std::fmt::Display {
      58        91388 :         match self {
      59        89663 :             Self::Valid(v) => GenerationFileSuffix(Some(*v)),
      60         1725 :             Self::None => GenerationFileSuffix(None),
      61              :             Self::Broken => {
      62            0 :                 panic!("Tried to use a broken generation");
      63              :             }
      64              :         }
      65        91388 :     }
      66              : 
      67              :     /// `suffix` is the part after "-" in a key
      68              :     ///
      69              :     /// Returns None if parsing was unsuccessful
      70          645 :     pub fn parse_suffix(suffix: &str) -> Option<Generation> {
      71          645 :         u32::from_str_radix(suffix, 16).map(Generation::new).ok()
      72          645 :     }
      73              : 
      74              :     #[track_caller]
      75          470 :     pub fn previous(&self) -> Generation {
      76          470 :         match self {
      77          470 :             Self::Valid(n) => {
      78          470 :                 if *n == 0 {
      79              :                     // Since a tenant may be upgraded from a pre-generations state, interpret the "previous" generation
      80              :                     // to 0 as being "no generation".
      81            0 :                     Self::None
      82              :                 } else {
      83          470 :                     Self::Valid(n - 1)
      84              :                 }
      85              :             }
      86            0 :             Self::None => Self::None,
      87            0 :             Self::Broken => panic!("Attempted to use a broken generation"),
      88              :         }
      89          470 :     }
      90              : 
      91              :     #[track_caller]
      92            2 :     pub fn next(&self) -> Generation {
      93            2 :         match self {
      94            2 :             Self::Valid(n) => Self::Valid(*n + 1),
      95            0 :             Self::None => Self::Valid(1),
      96            0 :             Self::Broken => panic!("Attempted to use a broken generation"),
      97              :         }
      98            2 :     }
      99              : 
     100         4503 :     pub fn into(self) -> Option<u32> {
     101         4503 :         if let Self::Valid(v) = self {
     102         4503 :             Some(v)
     103              :         } else {
     104            0 :             None
     105              :         }
     106         4503 :     }
     107              : }
     108              : 
     109              : struct GenerationFileSuffix(Option<u32>);
     110              : 
     111              : impl std::fmt::Display for GenerationFileSuffix {
     112        91388 :     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     113        91388 :         if let Some(g) = self.0 {
     114        89663 :             write!(f, "-{g:08x}")
     115              :         } else {
     116         1725 :             Ok(())
     117              :         }
     118        91388 :     }
     119              : }
     120              : 
     121              : impl Serialize for Generation {
     122       591469 :     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     123       591469 :     where
     124       591469 :         S: serde::Serializer,
     125       591469 :     {
     126       591469 :         if let Self::Valid(v) = self {
     127       591469 :             v.serialize(serializer)
     128              :         } else {
     129              :             // We should never be asked to serialize a None or Broken.  Structures
     130              :             // that include an optional generation should convert None to an
     131              :             // Option<Generation>::None
     132            0 :             Err(serde::ser::Error::custom(
     133            0 :                 "Tried to serialize invalid generation ({self})",
     134            0 :             ))
     135              :         }
     136       591469 :     }
     137              : }
     138              : 
     139              : impl<'de> Deserialize<'de> for Generation {
     140        55852 :     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     141        55852 :     where
     142        55852 :         D: serde::Deserializer<'de>,
     143        55852 :     {
     144        55852 :         Ok(Self::Valid(u32::deserialize(deserializer)?))
     145        55852 :     }
     146              : }
     147              : 
     148              : // We intentionally do not implement Display for Generation, to reduce the
     149              : // risk of a bug where the generation is used in a format!() string directly
     150              : // instead of using get_suffix().
     151              : impl Debug for Generation {
     152        29725 :     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     153        29725 :         match self {
     154        29160 :             Self::Valid(v) => {
     155        29160 :                 write!(f, "{:08x}", v)
     156              :             }
     157              :             Self::None => {
     158          565 :                 write!(f, "<none>")
     159              :             }
     160              :             Self::Broken => {
     161            0 :                 write!(f, "<broken>")
     162              :             }
     163              :         }
     164        29725 :     }
     165              : }
     166              : 
     167              : #[cfg(test)]
     168              : mod test {
     169              :     use super::*;
     170              : 
     171            2 :     #[test]
     172            2 :     fn generation_gt() {
     173            2 :         // Important that a None generation compares less than a valid one, during upgrades from
     174            2 :         // pre-generation systems.
     175            2 :         assert!(Generation::none() < Generation::new(0));
     176            2 :         assert!(Generation::none() < Generation::new(1));
     177            2 :     }
     178              : 
     179            2 :     #[test]
     180            2 :     fn suffix_is_stable() {
     181            2 :         use std::fmt::Write as _;
     182            2 : 
     183            2 :         // the suffix must remain stable through-out the pageserver remote storage evolution and
     184            2 :         // not be changed accidentially without thinking about migration
     185            2 :         let examples = [
     186            2 :             (line!(), Generation::None, ""),
     187            2 :             (line!(), Generation::Valid(0), "-00000000"),
     188            2 :             (line!(), Generation::Valid(u32::MAX), "-ffffffff"),
     189            2 :         ];
     190            2 : 
     191            2 :         let mut s = String::new();
     192            8 :         for (line, gen, expected) in examples {
     193            6 :             s.clear();
     194            6 :             write!(s, "{}", &gen.get_suffix()).expect("string grows");
     195            6 :             assert_eq!(s, expected, "example on {line}");
     196              :         }
     197            2 :     }
     198              : }
        

Generated by: LCOV version 2.1-beta