LCOV - code coverage report
Current view: top level - pageserver/src - repository.rs (source / functions) Coverage Total Hit
Test: b837401fb09d2d9818b70e630fdb67e9799b7b0d.info Lines: 93.7 % 174 163
Test Date: 2024-04-18 15:32:49 Functions: 55.0 % 20 11

            Line data    Source code
       1              : use crate::walrecord::NeonWalRecord;
       2              : use anyhow::Result;
       3              : use bytes::Bytes;
       4              : use serde::{Deserialize, Serialize};
       5              : use std::ops::AddAssign;
       6              : use std::time::Duration;
       7              : 
       8              : pub use pageserver_api::key::{Key, KEY_SIZE};
       9              : 
      10              : /// A 'value' stored for a one Key.
      11     13708210 : #[derive(Debug, Clone, Serialize, Deserialize)]
      12              : #[cfg_attr(test, derive(PartialEq))]
      13              : pub enum Value {
      14              :     /// An Image value contains a full copy of the value
      15              :     Image(Bytes),
      16              :     /// A WalRecord value contains a WAL record that needs to be
      17              :     /// replayed get the full value. Replaying the WAL record
      18              :     /// might need a previous version of the value (if will_init()
      19              :     /// returns false), or it may be replayed stand-alone (true).
      20              :     WalRecord(NeonWalRecord),
      21              : }
      22              : 
      23              : impl Value {
      24            0 :     pub fn is_image(&self) -> bool {
      25            0 :         matches!(self, Value::Image(_))
      26            0 :     }
      27              : 
      28      6351478 :     pub fn will_init(&self) -> bool {
      29      6351478 :         match self {
      30      6351470 :             Value::Image(_) => true,
      31            8 :             Value::WalRecord(rec) => rec.will_init(),
      32              :         }
      33      6351478 :     }
      34              : }
      35              : 
      36              : #[cfg(test)]
      37              : #[derive(Debug, PartialEq)]
      38              : pub(crate) enum InvalidInput {
      39              :     TooShortValue,
      40              :     TooShortPostgresRecord,
      41              : }
      42              : 
      43              : /// We could have a ValueRef where everything is `serde(borrow)`. Before implementing that, lets
      44              : /// use this type for querying if a slice looks some particular way.
      45              : #[cfg(test)]
      46              : pub(crate) struct ValueBytes;
      47              : 
      48              : #[cfg(test)]
      49              : impl ValueBytes {
      50          100 :     pub(crate) fn will_init(raw: &[u8]) -> Result<bool, InvalidInput> {
      51          100 :         if raw.len() < 12 {
      52           48 :             return Err(InvalidInput::TooShortValue);
      53           52 :         }
      54           52 : 
      55           52 :         let value_discriminator = &raw[0..4];
      56           52 : 
      57           52 :         if value_discriminator == [0, 0, 0, 0] {
      58              :             // Value::Image always initializes
      59           16 :             return Ok(true);
      60           36 :         }
      61           36 : 
      62           36 :         if value_discriminator != [0, 0, 0, 1] {
      63              :             // not a Value::WalRecord(..)
      64            0 :             return Ok(false);
      65           36 :         }
      66           36 : 
      67           36 :         let walrecord_discriminator = &raw[4..8];
      68           36 : 
      69           36 :         if walrecord_discriminator != [0, 0, 0, 0] {
      70              :             // only NeonWalRecord::Postgres can have will_init
      71            2 :             return Ok(false);
      72           34 :         }
      73           34 : 
      74           34 :         if raw.len() < 17 {
      75           10 :             return Err(InvalidInput::TooShortPostgresRecord);
      76           24 :         }
      77           24 : 
      78           24 :         Ok(raw[8] == 1)
      79          100 :     }
      80              : }
      81              : 
      82              : #[cfg(test)]
      83              : mod test {
      84              :     use super::*;
      85              : 
      86              :     use utils::bin_ser::BeSer;
      87              : 
      88              :     macro_rules! roundtrip {
      89              :         ($orig:expr, $expected:expr) => {{
      90              :             let orig: Value = $orig;
      91              : 
      92              :             let actual = Value::ser(&orig).unwrap();
      93              :             let expected: &[u8] = &$expected;
      94              : 
      95              :             assert_eq!(utils::Hex(&actual), utils::Hex(expected));
      96              : 
      97              :             let deser = Value::des(&actual).unwrap();
      98              : 
      99              :             assert_eq!(orig, deser);
     100              :         }};
     101              :     }
     102              : 
     103              :     #[test]
     104            2 :     fn image_roundtrip() {
     105            2 :         let image = Bytes::from_static(b"foobar");
     106            2 :         let image = Value::Image(image);
     107            2 : 
     108            2 :         #[rustfmt::skip]
     109            2 :         let expected = [
     110            2 :             // top level discriminator of 4 bytes
     111            2 :             0x00, 0x00, 0x00, 0x00,
     112            2 :             // 8 byte length
     113            2 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
     114            2 :             // foobar
     115            2 :             0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72
     116            2 :         ];
     117            2 : 
     118            2 :         roundtrip!(image, expected);
     119              : 
     120            2 :         assert!(ValueBytes::will_init(&expected).unwrap());
     121            2 :     }
     122              : 
     123              :     #[test]
     124            2 :     fn walrecord_postgres_roundtrip() {
     125            2 :         let rec = NeonWalRecord::Postgres {
     126            2 :             will_init: true,
     127            2 :             rec: Bytes::from_static(b"foobar"),
     128            2 :         };
     129            2 :         let rec = Value::WalRecord(rec);
     130            2 : 
     131            2 :         #[rustfmt::skip]
     132            2 :         let expected = [
     133            2 :             // flattened discriminator of total 8 bytes
     134            2 :             0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
     135            2 :             // will_init
     136            2 :             0x01,
     137            2 :             // 8 byte length
     138            2 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
     139            2 :             // foobar
     140            2 :             0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72
     141            2 :         ];
     142            2 : 
     143            2 :         roundtrip!(rec, expected);
     144              : 
     145            2 :         assert!(ValueBytes::will_init(&expected).unwrap());
     146            2 :     }
     147              : 
     148              :     #[test]
     149            2 :     fn bytes_inspection_too_short_image() {
     150            2 :         let rec = Value::Image(Bytes::from_static(b""));
     151            2 : 
     152            2 :         #[rustfmt::skip]
     153            2 :         let expected = [
     154            2 :             // top level discriminator of 4 bytes
     155            2 :             0x00, 0x00, 0x00, 0x00,
     156            2 :             // 8 byte length
     157            2 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     158            2 :         ];
     159            2 : 
     160            2 :         roundtrip!(rec, expected);
     161              : 
     162            2 :         assert!(ValueBytes::will_init(&expected).unwrap());
     163            2 :         assert_eq!(expected.len(), 12);
     164           26 :         for len in 0..12 {
     165           24 :             assert_eq!(
     166           24 :                 ValueBytes::will_init(&expected[..len]).unwrap_err(),
     167           24 :                 InvalidInput::TooShortValue
     168           24 :             );
     169              :         }
     170            2 :     }
     171              : 
     172              :     #[test]
     173            2 :     fn bytes_inspection_too_short_postgres_record() {
     174            2 :         let rec = NeonWalRecord::Postgres {
     175            2 :             will_init: false,
     176            2 :             rec: Bytes::from_static(b""),
     177            2 :         };
     178            2 :         let rec = Value::WalRecord(rec);
     179            2 : 
     180            2 :         #[rustfmt::skip]
     181            2 :         let expected = [
     182            2 :             // flattened discriminator of total 8 bytes
     183            2 :             0x00, 0x00, 0x00, 0x01,
     184            2 :             0x00, 0x00, 0x00, 0x00,
     185            2 :             // will_init
     186            2 :             0x00,
     187            2 :             // 8 byte length
     188            2 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     189            2 :         ];
     190            2 : 
     191            2 :         roundtrip!(rec, expected);
     192              : 
     193            2 :         assert!(!ValueBytes::will_init(&expected).unwrap());
     194            2 :         assert_eq!(expected.len(), 17);
     195           12 :         for len in 12..17 {
     196           10 :             assert_eq!(
     197           10 :                 ValueBytes::will_init(&expected[..len]).unwrap_err(),
     198           10 :                 InvalidInput::TooShortPostgresRecord
     199           10 :             )
     200              :         }
     201           26 :         for len in 0..12 {
     202           24 :             assert_eq!(
     203           24 :                 ValueBytes::will_init(&expected[..len]).unwrap_err(),
     204           24 :                 InvalidInput::TooShortValue
     205           24 :             )
     206              :         }
     207            2 :     }
     208              : 
     209              :     #[test]
     210            2 :     fn clear_visibility_map_flags_example() {
     211            2 :         let rec = NeonWalRecord::ClearVisibilityMapFlags {
     212            2 :             new_heap_blkno: Some(0x11),
     213            2 :             old_heap_blkno: None,
     214            2 :             flags: 0x03,
     215            2 :         };
     216            2 :         let rec = Value::WalRecord(rec);
     217            2 : 
     218            2 :         #[rustfmt::skip]
     219            2 :         let expected = [
     220            2 :             // discriminators
     221            2 :             0x00, 0x00, 0x00, 0x01,
     222            2 :             0x00, 0x00, 0x00, 0x01,
     223            2 :             // Some == 1 followed by 4 bytes
     224            2 :             0x01, 0x00, 0x00, 0x00, 0x11,
     225            2 :             // None == 0
     226            2 :             0x00,
     227            2 :             // flags
     228            2 :             0x03
     229            2 :         ];
     230            2 : 
     231            2 :         roundtrip!(rec, expected);
     232              : 
     233            2 :         assert!(!ValueBytes::will_init(&expected).unwrap());
     234            2 :     }
     235              : }
     236              : 
     237              : ///
     238              : /// Result of performing GC
     239              : ///
     240            0 : #[derive(Default, Serialize, Debug)]
     241              : pub struct GcResult {
     242              :     pub layers_total: u64,
     243              :     pub layers_needed_by_cutoff: u64,
     244              :     pub layers_needed_by_pitr: u64,
     245              :     pub layers_needed_by_branches: u64,
     246              :     pub layers_not_updated: u64,
     247              :     pub layers_removed: u64, // # of layer files removed because they have been made obsolete by newer ondisk files.
     248              : 
     249              :     #[serde(serialize_with = "serialize_duration_as_millis")]
     250              :     pub elapsed: Duration,
     251              : 
     252              :     /// The layers which were garbage collected.
     253              :     ///
     254              :     /// Used in `/v1/tenant/:tenant_id/timeline/:timeline_id/do_gc` to wait for the layers to be
     255              :     /// dropped in tests.
     256              :     #[cfg(feature = "testing")]
     257              :     #[serde(skip)]
     258              :     pub(crate) doomed_layers: Vec<crate::tenant::storage_layer::Layer>,
     259              : }
     260              : 
     261              : // helper function for `GcResult`, serializing a `Duration` as an integer number of milliseconds
     262            0 : fn serialize_duration_as_millis<S>(d: &Duration, serializer: S) -> Result<S::Ok, S::Error>
     263            0 : where
     264            0 :     S: serde::Serializer,
     265            0 : {
     266            0 :     d.as_millis().serialize(serializer)
     267            0 : }
     268              : 
     269              : impl AddAssign for GcResult {
     270            8 :     fn add_assign(&mut self, other: Self) {
     271            8 :         self.layers_total += other.layers_total;
     272            8 :         self.layers_needed_by_pitr += other.layers_needed_by_pitr;
     273            8 :         self.layers_needed_by_cutoff += other.layers_needed_by_cutoff;
     274            8 :         self.layers_needed_by_branches += other.layers_needed_by_branches;
     275            8 :         self.layers_not_updated += other.layers_not_updated;
     276            8 :         self.layers_removed += other.layers_removed;
     277            8 : 
     278            8 :         self.elapsed += other.elapsed;
     279            8 : 
     280            8 :         #[cfg(feature = "testing")]
     281            8 :         {
     282            8 :             let mut other = other;
     283            8 :             self.doomed_layers.append(&mut other.doomed_layers);
     284            8 :         }
     285            8 :     }
     286              : }
        

Generated by: LCOV version 2.1-beta