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