LCOV - code coverage report
Current view: top level - libs/pageserver_api/src - value.rs (source / functions) Coverage Total Hit
Test: 09e7485004805bd42b53a0c369170b3228136512.info Lines: 99.3 % 151 150
Test Date: 2024-11-21 18:36:18 Functions: 61.1 % 18 11

            Line data    Source code
       1              : //! This module defines the value type used by the storage engine.
       2              : //!
       3              : //! A [`Value`] represents either a completely new value for one Key ([`Value::Image`]),
       4              : //! or a "delta" of how to get from previous version of the value to the new one
       5              : //! ([`Value::WalRecord`]])
       6              : //!
       7              : //! Note that the [`Value`] type is used for the permananent storage format, so any
       8              : //! changes to it must be backwards compatible.
       9              : 
      10              : use crate::record::NeonWalRecord;
      11              : use bytes::Bytes;
      12              : use serde::{Deserialize, Serialize};
      13              : 
      14      5496346 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
      15              : pub enum Value {
      16              :     /// An Image value contains a full copy of the value
      17              :     Image(Bytes),
      18              :     /// A WalRecord value contains a WAL record that needs to be
      19              :     /// replayed get the full value. Replaying the WAL record
      20              :     /// might need a previous version of the value (if will_init()
      21              :     /// returns false), or it may be replayed stand-alone (true).
      22              :     WalRecord(NeonWalRecord),
      23              : }
      24              : 
      25              : impl Value {
      26              :     #[inline(always)]
      27          712 :     pub fn is_image(&self) -> bool {
      28          712 :         matches!(self, Value::Image(_))
      29          712 :     }
      30              : 
      31              :     #[inline(always)]
      32      7178889 :     pub fn will_init(&self) -> bool {
      33      7178889 :         match self {
      34      7032524 :             Value::Image(_) => true,
      35       146365 :             Value::WalRecord(rec) => rec.will_init(),
      36              :         }
      37      7178889 :     }
      38              : }
      39              : 
      40              : #[derive(Debug, PartialEq)]
      41              : pub enum InvalidInput {
      42              :     TooShortValue,
      43              :     TooShortPostgresRecord,
      44              : }
      45              : 
      46              : /// We could have a ValueRef where everything is `serde(borrow)`. Before implementing that, lets
      47              : /// use this type for querying if a slice looks some particular way.
      48              : pub struct ValueBytes;
      49              : 
      50              : impl ValueBytes {
      51              :     #[inline(always)]
      52           66 :     pub fn will_init(raw: &[u8]) -> Result<bool, InvalidInput> {
      53           66 :         if raw.len() < 12 {
      54           24 :             return Err(InvalidInput::TooShortValue);
      55           42 :         }
      56           42 : 
      57           42 :         let value_discriminator = &raw[0..4];
      58           42 : 
      59           42 :         if value_discriminator == [0, 0, 0, 0] {
      60              :             // Value::Image always initializes
      61           14 :             return Ok(true);
      62           28 :         }
      63           28 : 
      64           28 :         if value_discriminator != [0, 0, 0, 1] {
      65              :             // not a Value::WalRecord(..)
      66            0 :             return Ok(false);
      67           28 :         }
      68           28 : 
      69           28 :         let walrecord_discriminator = &raw[4..8];
      70           28 : 
      71           28 :         if walrecord_discriminator != [0, 0, 0, 0] {
      72              :             // only NeonWalRecord::Postgres can have will_init
      73            1 :             return Ok(false);
      74           27 :         }
      75           27 : 
      76           27 :         if raw.len() < 17 {
      77            5 :             return Err(InvalidInput::TooShortPostgresRecord);
      78           22 :         }
      79           22 : 
      80           22 :         Ok(raw[8] == 1)
      81           66 :     }
      82              : }
      83              : 
      84              : #[cfg(test)]
      85              : mod test {
      86              :     use super::*;
      87              : 
      88              :     use bytes::Bytes;
      89              :     use utils::bin_ser::BeSer;
      90              : 
      91              :     macro_rules! roundtrip {
      92              :         ($orig:expr, $expected:expr) => {{
      93              :             let orig: Value = $orig;
      94              : 
      95              :             let actual = Value::ser(&orig).unwrap();
      96              :             let expected: &[u8] = &$expected;
      97              : 
      98              :             assert_eq!(utils::Hex(&actual), utils::Hex(expected));
      99              : 
     100              :             let deser = Value::des(&actual).unwrap();
     101              : 
     102              :             assert_eq!(orig, deser);
     103              :         }};
     104              :     }
     105              : 
     106              :     #[test]
     107            1 :     fn image_roundtrip() {
     108            1 :         let image = Bytes::from_static(b"foobar");
     109            1 :         let image = Value::Image(image);
     110            1 : 
     111            1 :         #[rustfmt::skip]
     112            1 :         let expected = [
     113            1 :             // top level discriminator of 4 bytes
     114            1 :             0x00, 0x00, 0x00, 0x00,
     115            1 :             // 8 byte length
     116            1 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
     117            1 :             // foobar
     118            1 :             0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72
     119            1 :         ];
     120            1 : 
     121            1 :         roundtrip!(image, expected);
     122              : 
     123            1 :         assert!(ValueBytes::will_init(&expected).unwrap());
     124            1 :     }
     125              : 
     126              :     #[test]
     127            1 :     fn walrecord_postgres_roundtrip() {
     128            1 :         let rec = NeonWalRecord::Postgres {
     129            1 :             will_init: true,
     130            1 :             rec: Bytes::from_static(b"foobar"),
     131            1 :         };
     132            1 :         let rec = Value::WalRecord(rec);
     133            1 : 
     134            1 :         #[rustfmt::skip]
     135            1 :         let expected = [
     136            1 :             // flattened discriminator of total 8 bytes
     137            1 :             0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
     138            1 :             // will_init
     139            1 :             0x01,
     140            1 :             // 8 byte length
     141            1 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
     142            1 :             // foobar
     143            1 :             0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72
     144            1 :         ];
     145            1 : 
     146            1 :         roundtrip!(rec, expected);
     147              : 
     148            1 :         assert!(ValueBytes::will_init(&expected).unwrap());
     149            1 :     }
     150              : 
     151              :     #[test]
     152            1 :     fn bytes_inspection_too_short_image() {
     153            1 :         let rec = Value::Image(Bytes::from_static(b""));
     154            1 : 
     155            1 :         #[rustfmt::skip]
     156            1 :         let expected = [
     157            1 :             // top level discriminator of 4 bytes
     158            1 :             0x00, 0x00, 0x00, 0x00,
     159            1 :             // 8 byte length
     160            1 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     161            1 :         ];
     162            1 : 
     163            1 :         roundtrip!(rec, expected);
     164              : 
     165            1 :         assert!(ValueBytes::will_init(&expected).unwrap());
     166            1 :         assert_eq!(expected.len(), 12);
     167           13 :         for len in 0..12 {
     168           12 :             assert_eq!(
     169           12 :                 ValueBytes::will_init(&expected[..len]).unwrap_err(),
     170           12 :                 InvalidInput::TooShortValue
     171           12 :             );
     172              :         }
     173            1 :     }
     174              : 
     175              :     #[test]
     176            1 :     fn bytes_inspection_too_short_postgres_record() {
     177            1 :         let rec = NeonWalRecord::Postgres {
     178            1 :             will_init: false,
     179            1 :             rec: Bytes::from_static(b""),
     180            1 :         };
     181            1 :         let rec = Value::WalRecord(rec);
     182            1 : 
     183            1 :         #[rustfmt::skip]
     184            1 :         let expected = [
     185            1 :             // flattened discriminator of total 8 bytes
     186            1 :             0x00, 0x00, 0x00, 0x01,
     187            1 :             0x00, 0x00, 0x00, 0x00,
     188            1 :             // will_init
     189            1 :             0x00,
     190            1 :             // 8 byte length
     191            1 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     192            1 :         ];
     193            1 : 
     194            1 :         roundtrip!(rec, expected);
     195              : 
     196            1 :         assert!(!ValueBytes::will_init(&expected).unwrap());
     197            1 :         assert_eq!(expected.len(), 17);
     198            6 :         for len in 12..17 {
     199            5 :             assert_eq!(
     200            5 :                 ValueBytes::will_init(&expected[..len]).unwrap_err(),
     201            5 :                 InvalidInput::TooShortPostgresRecord
     202            5 :             )
     203              :         }
     204           13 :         for len in 0..12 {
     205           12 :             assert_eq!(
     206           12 :                 ValueBytes::will_init(&expected[..len]).unwrap_err(),
     207           12 :                 InvalidInput::TooShortValue
     208           12 :             )
     209              :         }
     210            1 :     }
     211              : 
     212              :     #[test]
     213            1 :     fn clear_visibility_map_flags_example() {
     214            1 :         let rec = NeonWalRecord::ClearVisibilityMapFlags {
     215            1 :             new_heap_blkno: Some(0x11),
     216            1 :             old_heap_blkno: None,
     217            1 :             flags: 0x03,
     218            1 :         };
     219            1 :         let rec = Value::WalRecord(rec);
     220            1 : 
     221            1 :         #[rustfmt::skip]
     222            1 :         let expected = [
     223            1 :             // discriminators
     224            1 :             0x00, 0x00, 0x00, 0x01,
     225            1 :             0x00, 0x00, 0x00, 0x01,
     226            1 :             // Some == 1 followed by 4 bytes
     227            1 :             0x01, 0x00, 0x00, 0x00, 0x11,
     228            1 :             // None == 0
     229            1 :             0x00,
     230            1 :             // flags
     231            1 :             0x03
     232            1 :         ];
     233            1 : 
     234            1 :         roundtrip!(rec, expected);
     235              : 
     236            1 :         assert!(!ValueBytes::will_init(&expected).unwrap());
     237            1 :     }
     238              : }
        

Generated by: LCOV version 2.1-beta