LCOV - code coverage report
Current view: top level - libs/utils/src - generation.rs (source / functions) Coverage Total Hit
Test: 5187d4b6d9cfe1c429baf0147b0578521d04e1ed.info Lines: 85.9 % 85 73
Test Date: 2024-06-26 21:48:01 Functions: 51.7 % 29 15

            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              : #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
      11              : pub enum Generation {
      12              :     // The None Generation is used in the metadata of layers written before generations were
      13              :     // introduced.  A running Tenant always has a valid generation, but the layer metadata may
      14              :     // include None generations.
      15              :     None,
      16              : 
      17              :     Valid(u32),
      18              : }
      19              : 
      20              : /// The Generation type represents a number associated with a Tenant, which
      21              : /// increments every time the tenant is attached to a new pageserver, or
      22              : /// an attached pageserver restarts.
      23              : ///
      24              : /// It is included as a suffix in S3 keys, as a protection against split-brain
      25              : /// scenarios where pageservers might otherwise issue conflicting writes to
      26              : /// remote storage
      27              : impl Generation {
      28              :     pub const MAX: Self = Self::Valid(u32::MAX);
      29              : 
      30              :     /// Create a new Generation that represents a legacy key format with
      31              :     /// no generation suffix
      32           62 :     pub fn none() -> Self {
      33           62 :         Self::None
      34           62 :     }
      35              : 
      36          227 :     pub const fn new(v: u32) -> Self {
      37          227 :         Self::Valid(v)
      38          227 :     }
      39              : 
      40        73714 :     pub fn is_none(&self) -> bool {
      41        73714 :         matches!(self, Self::None)
      42        73714 :     }
      43              : 
      44              :     #[track_caller]
      45         6666 :     pub fn get_suffix(&self) -> impl std::fmt::Display {
      46         6666 :         match self {
      47         6654 :             Self::Valid(v) => GenerationFileSuffix(Some(*v)),
      48           12 :             Self::None => GenerationFileSuffix(None),
      49              :         }
      50         6666 :     }
      51              : 
      52              :     /// `suffix` is the part after "-" in a key
      53              :     ///
      54              :     /// Returns None if parsing was unsuccessful
      55           12 :     pub fn parse_suffix(suffix: &str) -> Option<Generation> {
      56           12 :         u32::from_str_radix(suffix, 16).map(Generation::new).ok()
      57           12 :     }
      58              : 
      59              :     #[track_caller]
      60           18 :     pub fn previous(&self) -> Generation {
      61           18 :         match self {
      62           18 :             Self::Valid(n) => {
      63           18 :                 if *n == 0 {
      64              :                     // Since a tenant may be upgraded from a pre-generations state, interpret the "previous" generation
      65              :                     // to 0 as being "no generation".
      66            0 :                     Self::None
      67              :                 } else {
      68           18 :                     Self::Valid(n - 1)
      69              :                 }
      70              :             }
      71            0 :             Self::None => Self::None,
      72              :         }
      73           18 :     }
      74              : 
      75              :     #[track_caller]
      76           12 :     pub fn next(&self) -> Generation {
      77           12 :         match self {
      78           12 :             Self::Valid(n) => Self::Valid(*n + 1),
      79            0 :             Self::None => Self::Valid(1),
      80              :         }
      81           12 :     }
      82              : 
      83            0 :     pub fn into(self) -> Option<u32> {
      84            0 :         if let Self::Valid(v) = self {
      85            0 :             Some(v)
      86              :         } else {
      87            0 :             None
      88              :         }
      89            0 :     }
      90              : }
      91              : 
      92              : struct GenerationFileSuffix(Option<u32>);
      93              : 
      94              : impl std::fmt::Display for GenerationFileSuffix {
      95         6666 :     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
      96         6666 :         if let Some(g) = self.0 {
      97         6654 :             write!(f, "-{g:08x}")
      98              :         } else {
      99           12 :             Ok(())
     100              :         }
     101         6666 :     }
     102              : }
     103              : 
     104              : impl Serialize for Generation {
     105        35203 :     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     106        35203 :     where
     107        35203 :         S: serde::Serializer,
     108        35203 :     {
     109        35203 :         if let Self::Valid(v) = self {
     110        35203 :             v.serialize(serializer)
     111              :         } else {
     112              :             // We should never be asked to serialize a None. Structures
     113              :             // that include an optional generation should convert None to an
     114              :             // Option<Generation>::None
     115            0 :             Err(serde::ser::Error::custom(
     116            0 :                 "Tried to serialize invalid generation ({self})",
     117            0 :             ))
     118              :         }
     119        35203 :     }
     120              : }
     121              : 
     122              : impl<'de> Deserialize<'de> for Generation {
     123           34 :     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     124           34 :     where
     125           34 :         D: serde::Deserializer<'de>,
     126           34 :     {
     127           34 :         Ok(Self::Valid(u32::deserialize(deserializer)?))
     128           34 :     }
     129              : }
     130              : 
     131              : // We intentionally do not implement Display for Generation, to reduce the
     132              : // risk of a bug where the generation is used in a format!() string directly
     133              : // instead of using get_suffix().
     134              : impl Debug for Generation {
     135         1508 :     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     136         1508 :         match self {
     137         1508 :             Self::Valid(v) => {
     138         1508 :                 write!(f, "{:08x}", v)
     139              :             }
     140              :             Self::None => {
     141            0 :                 write!(f, "<none>")
     142              :             }
     143              :         }
     144         1508 :     }
     145              : }
     146              : 
     147              : #[cfg(test)]
     148              : mod test {
     149              :     use super::*;
     150              : 
     151              :     #[test]
     152            2 :     fn generation_gt() {
     153            2 :         // Important that a None generation compares less than a valid one, during upgrades from
     154            2 :         // pre-generation systems.
     155            2 :         assert!(Generation::none() < Generation::new(0));
     156            2 :         assert!(Generation::none() < Generation::new(1));
     157            2 :     }
     158              : 
     159              :     #[test]
     160            2 :     fn suffix_is_stable() {
     161            2 :         use std::fmt::Write as _;
     162            2 : 
     163            2 :         // the suffix must remain stable through-out the pageserver remote storage evolution and
     164            2 :         // not be changed accidentially without thinking about migration
     165            2 :         let examples = [
     166            2 :             (line!(), Generation::None, ""),
     167            2 :             (line!(), Generation::Valid(0), "-00000000"),
     168            2 :             (line!(), Generation::Valid(u32::MAX), "-ffffffff"),
     169            2 :         ];
     170            2 : 
     171            2 :         let mut s = String::new();
     172            8 :         for (line, gen, expected) in examples {
     173            6 :             s.clear();
     174            6 :             write!(s, "{}", &gen.get_suffix()).expect("string grows");
     175            6 :             assert_eq!(s, expected, "example on {line}");
     176              :         }
     177            2 :     }
     178              : }
        

Generated by: LCOV version 2.1-beta