LCOV - code coverage report
Current view: top level - libs/wal_decoder/src/models - record.rs (source / functions) Coverage Total Hit
Test: 1e20c4f2b28aa592527961bb32170ebbd2c9172f.info Lines: 76.1 % 46 35
Test Date: 2025-07-16 12:29:03 Functions: 50.0 % 16 8

            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 : }
        

Generated by: LCOV version 2.1-beta