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 224 : pub fn none() -> Self {
33 224 : Self::None
34 224 : }
35 :
36 703 : pub const fn new(v: u32) -> Self {
37 703 : Self::Valid(v)
38 703 : }
39 :
40 222971 : pub fn is_none(&self) -> bool {
41 222971 : matches!(self, Self::None)
42 222971 : }
43 :
44 : #[track_caller]
45 20923 : pub fn get_suffix(&self) -> impl std::fmt::Display {
46 20923 : match self {
47 20892 : Self::Valid(v) => GenerationFileSuffix(Some(*v)),
48 31 : Self::None => GenerationFileSuffix(None),
49 : }
50 20923 : }
51 :
52 : /// `suffix` is the part after "-" in a key
53 : ///
54 : /// Returns None if parsing was unsuccessful
55 36 : pub fn parse_suffix(suffix: &str) -> Option<Generation> {
56 36 : u32::from_str_radix(suffix, 16).map(Generation::new).ok()
57 36 : }
58 :
59 : #[track_caller]
60 54 : pub fn previous(&self) -> Generation {
61 54 : match self {
62 54 : Self::Valid(n) => {
63 54 : 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 54 : Self::Valid(n - 1)
69 : }
70 : }
71 0 : Self::None => Self::None,
72 : }
73 54 : }
74 :
75 : #[track_caller]
76 36 : pub fn next(&self) -> Generation {
77 36 : match self {
78 36 : Self::Valid(n) => Self::Valid(*n + 1),
79 0 : Self::None => Self::Valid(1),
80 : }
81 36 : }
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 20923 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 20923 : if let Some(g) = self.0 {
97 20892 : write!(f, "-{g:08x}")
98 : } else {
99 31 : Ok(())
100 : }
101 20923 : }
102 : }
103 :
104 : impl Serialize for Generation {
105 106157 : fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
106 106157 : where
107 106157 : S: serde::Serializer,
108 106157 : {
109 106157 : if let Self::Valid(v) = self {
110 106157 : 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 106157 : }
120 : }
121 :
122 : impl<'de> Deserialize<'de> for Generation {
123 9486 : fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
124 9486 : where
125 9486 : D: serde::Deserializer<'de>,
126 9486 : {
127 9486 : Ok(Self::Valid(u32::deserialize(deserializer)?))
128 9486 : }
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 4698 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 4698 : match self {
137 4698 : Self::Valid(v) => {
138 4698 : write!(f, "{:08x}", v)
139 : }
140 : Self::None => {
141 0 : write!(f, "<none>")
142 : }
143 : }
144 4698 : }
145 : }
146 :
147 : #[cfg(test)]
148 : mod test {
149 : use super::*;
150 :
151 : #[test]
152 1 : fn generation_gt() {
153 1 : // Important that a None generation compares less than a valid one, during upgrades from
154 1 : // pre-generation systems.
155 1 : assert!(Generation::none() < Generation::new(0));
156 1 : assert!(Generation::none() < Generation::new(1));
157 1 : }
158 :
159 : #[test]
160 1 : fn suffix_is_stable() {
161 : use std::fmt::Write as _;
162 :
163 : // the suffix must remain stable through-out the pageserver remote storage evolution and
164 : // not be changed accidentially without thinking about migration
165 1 : let examples = [
166 1 : (line!(), Generation::None, ""),
167 1 : (line!(), Generation::Valid(0), "-00000000"),
168 1 : (line!(), Generation::Valid(u32::MAX), "-ffffffff"),
169 1 : ];
170 1 :
171 1 : let mut s = String::new();
172 4 : for (line, gen, expected) in examples {
173 3 : s.clear();
174 3 : write!(s, "{}", &gen.get_suffix()).expect("string grows");
175 3 : assert_eq!(s, expected, "example on {line}");
176 : }
177 1 : }
178 : }
|