Skip to main content

graft_client/runtime/storage/
snapshot.rs

1// seems like rust analyzer has a bug that causes this warning to spuriously
2// fire on camel case types that also use underscores which is what zerocopy
3// generates for enum struct variants
4#![allow(non_camel_case_types)]
5
6use std::fmt::{Debug, Display};
7
8use culprit::{Culprit, ResultExt};
9use fjall::Slice;
10use graft_core::{lsn::LSN, page_count::PageCount};
11use serde::Serialize;
12use zerocopy::{ByteHash, Immutable, IntoBytes, KnownLayout, TryFromBytes};
13
14use super::{StorageErr, volume_state::VolumeStateTag};
15
16/// `RemoteLSN` tracks the relationship between the a server LSN and the local LSN
17/// it maps to.
18#[derive(KnownLayout, Immutable, TryFromBytes, IntoBytes, Clone, PartialEq, Eq, Serialize)]
19#[repr(u8)]
20pub enum RemoteMapping {
21    Unmapped {
22        #[serde(skip)]
23        _padding: [u8; 23],
24    },
25    Mapped {
26        #[serde(skip)]
27        _padding: [u8; 7],
28
29        /// the local LSN that maps to the remote LSN
30        local: LSN,
31
32        /// the remote LSN
33        remote: LSN,
34    },
35}
36
37impl RemoteMapping {
38    #[inline]
39    pub fn new(remote: LSN, local: LSN) -> Self {
40        Self::Mapped { _padding: [0; 7], remote, local }
41    }
42
43    #[inline]
44    pub fn lsn(&self) -> Option<LSN> {
45        match self {
46            Self::Mapped { remote, .. } => Some(*remote),
47            Self::Unmapped { .. } => None,
48        }
49    }
50
51    #[inline]
52    pub fn local(&self) -> Option<LSN> {
53        match self {
54            Self::Mapped { local, .. } => Some(*local),
55            Self::Unmapped { .. } => None,
56        }
57    }
58
59    /// returns the remote -> local LSN mapping as a single option tuple
60    #[inline]
61    pub fn splat(&self) -> Option<(LSN, LSN)> {
62        match self {
63            Self::Mapped { remote, local, .. } => Some((*remote, *local)),
64            Self::Unmapped { .. } => None,
65        }
66    }
67}
68
69impl Default for RemoteMapping {
70    #[inline]
71    fn default() -> Self {
72        Self::Unmapped { _padding: [0; 23] }
73    }
74}
75
76#[derive(
77    KnownLayout, Immutable, TryFromBytes, IntoBytes, ByteHash, Clone, PartialEq, Eq, Serialize,
78)]
79#[repr(C)]
80pub struct Snapshot {
81    /// resolve page reads at this local LSN
82    local: LSN,
83    /// the last known server LSN along with it's local LSN
84    remote: RemoteMapping,
85    /// the logical number of pages in this snapshot
86    pages: PageCount,
87
88    #[serde(skip)]
89    _padding: [u8; 4],
90}
91
92impl Snapshot {
93    #[inline]
94    pub fn new(local: LSN, remote: RemoteMapping, pages: PageCount) -> Self {
95        Self { local, remote, pages, _padding: [0; 4] }
96    }
97
98    #[track_caller]
99    pub(crate) fn try_from_bytes(bytes: &[u8]) -> Result<Self, Culprit<StorageErr>> {
100        Self::try_read_from_bytes(bytes)
101            .or_ctx(|e| StorageErr::CorruptVolumeState(VolumeStateTag::Snapshot, e.into()))
102    }
103
104    /// the local LSN backing this snapshot
105    #[inline]
106    pub fn local(&self) -> LSN {
107        self.local
108    }
109
110    /// the last known remote LSN as of this snapshot
111    #[inline]
112    pub fn remote(&self) -> Option<LSN> {
113        self.remote.lsn()
114    }
115
116    /// the local LSN corresponding to the last known remote LSN as of this snapshot
117    #[inline]
118    pub fn remote_local(&self) -> Option<LSN> {
119        self.remote.local()
120    }
121
122    /// Returns this snapshot's remote LSN along with the
123    /// local LSN the remote LSN corresponds to
124    #[inline]
125    pub fn remote_mapping(&self) -> &RemoteMapping {
126        &self.remote
127    }
128
129    #[inline]
130    pub fn pages(&self) -> PageCount {
131        self.pages
132    }
133}
134
135impl Debug for Snapshot {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        Display::fmt(self, f)
138    }
139}
140
141impl Display for Snapshot {
142    // Snapshot[5;3] means local 5 pages 3
143    // Snapshot[5;3][2r3] means local 5 pages 3 and local 2 maps to remote 3
144    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145        write!(f, "Snapshot[{};{}]", self.local(), self.pages(),)?;
146        if let Some((r, l)) = self.remote.splat() {
147            write!(f, "[{l}r{r}]")?;
148        }
149        Ok(())
150    }
151}
152
153impl From<Snapshot> for Slice {
154    fn from(snapshot: Snapshot) -> Slice {
155        snapshot.as_bytes().into()
156    }
157}