Line data Source code
1 : //! This module defines the WAL record format used within the pageserver.
2 :
3 : use bytes::Bytes;
4 : use postgres_ffi::walrecord::{MultiXactMember, describe_postgres_wal_record};
5 : use postgres_ffi::{MultiXactId, MultiXactOffset, TransactionId};
6 : use postgres_ffi_types::TimestampTz;
7 : use serde::{Deserialize, Serialize};
8 : use utils::bin_ser::DeserializeError;
9 :
10 : /// Each update to a page is represented by a NeonWalRecord. It can be a wrapper
11 : /// around a PostgreSQL WAL record, or a custom neon-specific "record".
12 0 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
13 : pub enum NeonWalRecord {
14 : /// Native PostgreSQL WAL record
15 : Postgres { will_init: bool, rec: Bytes },
16 :
17 : /// Clear bits in heap visibility map. ('flags' is bitmap of bits to clear)
18 : ClearVisibilityMapFlags {
19 : new_heap_blkno: Option<u32>,
20 : old_heap_blkno: Option<u32>,
21 : flags: u8,
22 : },
23 : /// Mark transaction IDs as committed on a CLOG page
24 : ClogSetCommitted {
25 : xids: Vec<TransactionId>,
26 : timestamp: TimestampTz,
27 : },
28 : /// Mark transaction IDs as aborted on a CLOG page
29 : ClogSetAborted { xids: Vec<TransactionId> },
30 : /// Extend multixact offsets SLRU
31 : MultixactOffsetCreate {
32 : mid: MultiXactId,
33 : moff: MultiXactOffset,
34 : },
35 : /// Extend multixact members SLRU.
36 : MultixactMembersCreate {
37 : moff: MultiXactOffset,
38 : members: Vec<MultiXactMember>,
39 : },
40 : /// Update the map of AUX files, either writing or dropping an entry
41 : AuxFile {
42 : file_path: String,
43 : content: Option<Bytes>,
44 : },
45 : // Truncate visibility map page
46 : TruncateVisibilityMap {
47 : trunc_byte: usize,
48 : trunc_offs: usize,
49 : },
50 :
51 : /// A testing record for unit testing purposes. It supports append data to an existing image, or clear it.
52 : #[cfg(feature = "testing")]
53 : Test {
54 : /// Append a string to the image.
55 : append: String,
56 : /// Clear the image before appending.
57 : clear: bool,
58 : /// Treat this record as an init record. `clear` should be set to true if this field is set
59 : /// to true. This record does not need the history WALs to reconstruct. See [`NeonWalRecord::will_init`] and
60 : /// its references in `timeline.rs`.
61 : will_init: bool,
62 : /// Only append the record if the current image is the same as the one specified in this field.
63 : only_if: Option<String>,
64 : },
65 : }
66 :
67 : impl NeonWalRecord {
68 : /// Does replaying this WAL record initialize the page from scratch, or does
69 : /// it need to be applied over the previous image of the page?
70 131000 : pub fn will_init(&self) -> bool {
71 : // If you change this function, you'll also need to change ValueBytes::will_init
72 131000 : match self {
73 72813 : NeonWalRecord::Postgres { will_init, rec: _ } => *will_init,
74 : #[cfg(feature = "testing")]
75 58177 : NeonWalRecord::Test { will_init, .. } => *will_init,
76 : // None of the special neon record types currently initialize the page
77 10 : _ => false,
78 : }
79 131000 : }
80 :
81 : #[cfg(feature = "testing")]
82 29946 : pub fn wal_append(s: impl AsRef<str>) -> Self {
83 29946 : Self::Test {
84 29946 : append: s.as_ref().to_string(),
85 29946 : clear: false,
86 29946 : will_init: false,
87 29946 : only_if: None,
88 29946 : }
89 23 : }
90 :
91 : #[cfg(feature = "testing")]
92 3 : pub fn wal_append_conditional(s: impl AsRef<str>, only_if: impl AsRef<str>) -> Self {
93 3 : Self::Test {
94 3 : append: s.as_ref().to_string(),
95 3 : clear: false,
96 3 : will_init: false,
97 3 : only_if: Some(only_if.as_ref().to_string()),
98 3 : }
99 0 : }
100 :
101 : #[cfg(feature = "testing")]
102 1 : pub fn wal_clear(s: impl AsRef<str>) -> Self {
103 1 : Self::Test {
104 1 : append: s.as_ref().to_string(),
105 1 : clear: true,
106 1 : will_init: false,
107 1 : only_if: None,
108 1 : }
109 0 : }
110 :
111 : #[cfg(feature = "testing")]
112 323 : pub fn wal_init(s: impl AsRef<str>) -> Self {
113 323 : Self::Test {
114 323 : append: s.as_ref().to_string(),
115 323 : clear: true,
116 323 : will_init: true,
117 323 : only_if: None,
118 323 : }
119 0 : }
120 : }
121 :
122 : /// Build a human-readable string to describe a WAL record
123 : ///
124 : /// For debugging purposes
125 0 : pub fn describe_wal_record(rec: &NeonWalRecord) -> Result<String, DeserializeError> {
126 0 : match rec {
127 0 : NeonWalRecord::Postgres { will_init, rec } => Ok(format!(
128 0 : "will_init: {}, {}",
129 : will_init,
130 0 : describe_postgres_wal_record(rec)?
131 : )),
132 0 : _ => Ok(format!("{rec:?}")),
133 : }
134 0 : }
|