LCOV - code coverage report
Current view: top level - pageserver/src/tenant - metadata.rs (source / functions) Coverage Total Hit
Test: 691a4c28fe7169edd60b367c52d448a0a6605f1f.info Lines: 94.2 % 362 341
Test Date: 2024-05-10 13:18:37 Functions: 41.5 % 65 27

            Line data    Source code
       1              : //! Every image of a certain timeline from [`crate::tenant::Tenant`]
       2              : //! has a metadata that needs to be stored persistently.
       3              : //!
       4              : //! Later, the file gets used in [`remote_timeline_client`] as a part of
       5              : //! external storage import and export operations.
       6              : //!
       7              : //! The module contains all structs and related helper methods related to timeline metadata.
       8              : //!
       9              : //! [`remote_timeline_client`]: super::remote_timeline_client
      10              : 
      11              : use anyhow::ensure;
      12              : use serde::{de::Error, Deserialize, Serialize, Serializer};
      13              : use utils::bin_ser::SerializeError;
      14              : use utils::{bin_ser::BeSer, id::TimelineId, lsn::Lsn};
      15              : 
      16              : /// Use special format number to enable backward compatibility.
      17              : const METADATA_FORMAT_VERSION: u16 = 4;
      18              : 
      19              : /// Previous supported format versions.
      20              : const METADATA_OLD_FORMAT_VERSION: u16 = 3;
      21              : 
      22              : /// We assume that a write of up to METADATA_MAX_SIZE bytes is atomic.
      23              : ///
      24              : /// This is the same assumption that PostgreSQL makes with the control file,
      25              : /// see PG_CONTROL_MAX_SAFE_SIZE
      26              : const METADATA_MAX_SIZE: usize = 512;
      27              : 
      28              : /// Metadata stored on disk for each timeline
      29              : ///
      30              : /// The fields correspond to the values we hold in memory, in Timeline.
      31              : #[derive(Debug, Clone, PartialEq, Eq)]
      32              : pub struct TimelineMetadata {
      33              :     hdr: TimelineMetadataHeader,
      34              :     body: TimelineMetadataBodyV2,
      35              : }
      36              : 
      37           64 : #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
      38              : struct TimelineMetadataHeader {
      39              :     checksum: u32,       // CRC of serialized metadata body
      40              :     size: u16,           // size of serialized metadata
      41              :     format_version: u16, // metadata format version (used for compatibility checks)
      42              : }
      43              : const METADATA_HDR_SIZE: usize = std::mem::size_of::<TimelineMetadataHeader>();
      44              : 
      45           60 : #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
      46              : struct TimelineMetadataBodyV2 {
      47              :     disk_consistent_lsn: Lsn,
      48              :     // This is only set if we know it. We track it in memory when the page
      49              :     // server is running, but we only track the value corresponding to
      50              :     // 'last_record_lsn', not 'disk_consistent_lsn' which can lag behind by a
      51              :     // lot. We only store it in the metadata file when we flush *all* the
      52              :     // in-memory data so that 'last_record_lsn' is the same as
      53              :     // 'disk_consistent_lsn'.  That's OK, because after page server restart, as
      54              :     // soon as we reprocess at least one record, we will have a valid
      55              :     // 'prev_record_lsn' value in memory again. This is only really needed when
      56              :     // doing a clean shutdown, so that there is no more WAL beyond
      57              :     // 'disk_consistent_lsn'
      58              :     prev_record_lsn: Option<Lsn>,
      59              :     ancestor_timeline: Option<TimelineId>,
      60              :     ancestor_lsn: Lsn,
      61              :     latest_gc_cutoff_lsn: Lsn,
      62              :     initdb_lsn: Lsn,
      63              :     pg_version: u32,
      64              : }
      65              : 
      66            2 : #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
      67              : struct TimelineMetadataBodyV1 {
      68              :     disk_consistent_lsn: Lsn,
      69              :     // This is only set if we know it. We track it in memory when the page
      70              :     // server is running, but we only track the value corresponding to
      71              :     // 'last_record_lsn', not 'disk_consistent_lsn' which can lag behind by a
      72              :     // lot. We only store it in the metadata file when we flush *all* the
      73              :     // in-memory data so that 'last_record_lsn' is the same as
      74              :     // 'disk_consistent_lsn'.  That's OK, because after page server restart, as
      75              :     // soon as we reprocess at least one record, we will have a valid
      76              :     // 'prev_record_lsn' value in memory again. This is only really needed when
      77              :     // doing a clean shutdown, so that there is no more WAL beyond
      78              :     // 'disk_consistent_lsn'
      79              :     prev_record_lsn: Option<Lsn>,
      80              :     ancestor_timeline: Option<TimelineId>,
      81              :     ancestor_lsn: Lsn,
      82              :     latest_gc_cutoff_lsn: Lsn,
      83              :     initdb_lsn: Lsn,
      84              : }
      85              : 
      86              : impl TimelineMetadata {
      87          350 :     pub fn new(
      88          350 :         disk_consistent_lsn: Lsn,
      89          350 :         prev_record_lsn: Option<Lsn>,
      90          350 :         ancestor_timeline: Option<TimelineId>,
      91          350 :         ancestor_lsn: Lsn,
      92          350 :         latest_gc_cutoff_lsn: Lsn,
      93          350 :         initdb_lsn: Lsn,
      94          350 :         pg_version: u32,
      95          350 :     ) -> Self {
      96          350 :         Self {
      97          350 :             hdr: TimelineMetadataHeader {
      98          350 :                 checksum: 0,
      99          350 :                 size: 0,
     100          350 :                 format_version: METADATA_FORMAT_VERSION,
     101          350 :             },
     102          350 :             body: TimelineMetadataBodyV2 {
     103          350 :                 disk_consistent_lsn,
     104          350 :                 prev_record_lsn,
     105          350 :                 ancestor_timeline,
     106          350 :                 ancestor_lsn,
     107          350 :                 latest_gc_cutoff_lsn,
     108          350 :                 initdb_lsn,
     109          350 :                 pg_version,
     110          350 :             },
     111          350 :         }
     112          350 :     }
     113              : 
     114            2 :     fn upgrade_timeline_metadata(metadata_bytes: &[u8]) -> anyhow::Result<Self> {
     115            2 :         let mut hdr = TimelineMetadataHeader::des(&metadata_bytes[0..METADATA_HDR_SIZE])?;
     116              : 
     117              :         // backward compatible only up to this version
     118            2 :         ensure!(
     119            2 :             hdr.format_version == METADATA_OLD_FORMAT_VERSION,
     120            0 :             "unsupported metadata format version {}",
     121              :             hdr.format_version
     122              :         );
     123              : 
     124            2 :         let metadata_size = hdr.size as usize;
     125              : 
     126            2 :         let body: TimelineMetadataBodyV1 =
     127            2 :             TimelineMetadataBodyV1::des(&metadata_bytes[METADATA_HDR_SIZE..metadata_size])?;
     128              : 
     129            2 :         let body = TimelineMetadataBodyV2 {
     130            2 :             disk_consistent_lsn: body.disk_consistent_lsn,
     131            2 :             prev_record_lsn: body.prev_record_lsn,
     132            2 :             ancestor_timeline: body.ancestor_timeline,
     133            2 :             ancestor_lsn: body.ancestor_lsn,
     134            2 :             latest_gc_cutoff_lsn: body.latest_gc_cutoff_lsn,
     135            2 :             initdb_lsn: body.initdb_lsn,
     136            2 :             pg_version: 14, // All timelines created before this version had pg_version 14
     137            2 :         };
     138            2 : 
     139            2 :         hdr.format_version = METADATA_FORMAT_VERSION;
     140            2 : 
     141            2 :         Ok(Self { hdr, body })
     142            2 :     }
     143              : 
     144           62 :     pub fn from_bytes(metadata_bytes: &[u8]) -> anyhow::Result<Self> {
     145           62 :         ensure!(
     146           62 :             metadata_bytes.len() == METADATA_MAX_SIZE,
     147            0 :             "metadata bytes size is wrong"
     148              :         );
     149           62 :         let hdr = TimelineMetadataHeader::des(&metadata_bytes[0..METADATA_HDR_SIZE])?;
     150              : 
     151           62 :         let metadata_size = hdr.size as usize;
     152           62 :         ensure!(
     153           62 :             metadata_size <= METADATA_MAX_SIZE,
     154            0 :             "corrupted metadata file"
     155              :         );
     156           62 :         let calculated_checksum = crc32c::crc32c(&metadata_bytes[METADATA_HDR_SIZE..metadata_size]);
     157           62 :         ensure!(
     158           62 :             hdr.checksum == calculated_checksum,
     159            0 :             "metadata checksum mismatch"
     160              :         );
     161              : 
     162           62 :         if hdr.format_version != METADATA_FORMAT_VERSION {
     163              :             // If metadata has the old format,
     164              :             // upgrade it and return the result
     165            2 :             TimelineMetadata::upgrade_timeline_metadata(metadata_bytes)
     166              :         } else {
     167           60 :             let body =
     168           60 :                 TimelineMetadataBodyV2::des(&metadata_bytes[METADATA_HDR_SIZE..metadata_size])?;
     169           60 :             ensure!(
     170           60 :                 body.disk_consistent_lsn.is_aligned(),
     171            0 :                 "disk_consistent_lsn is not aligned"
     172              :             );
     173           60 :             Ok(TimelineMetadata { hdr, body })
     174              :         }
     175           62 :     }
     176              : 
     177         1188 :     pub fn to_bytes(&self) -> Result<Vec<u8>, SerializeError> {
     178         1188 :         let body_bytes = self.body.ser()?;
     179         1188 :         let metadata_size = METADATA_HDR_SIZE + body_bytes.len();
     180         1188 :         let hdr = TimelineMetadataHeader {
     181         1188 :             size: metadata_size as u16,
     182         1188 :             format_version: METADATA_FORMAT_VERSION,
     183         1188 :             checksum: crc32c::crc32c(&body_bytes),
     184         1188 :         };
     185         1188 :         let hdr_bytes = hdr.ser()?;
     186         1188 :         let mut metadata_bytes = vec![0u8; METADATA_MAX_SIZE];
     187         1188 :         metadata_bytes[0..METADATA_HDR_SIZE].copy_from_slice(&hdr_bytes);
     188         1188 :         metadata_bytes[METADATA_HDR_SIZE..metadata_size].copy_from_slice(&body_bytes);
     189         1188 :         Ok(metadata_bytes)
     190         1188 :     }
     191              : 
     192              :     /// [`Lsn`] that corresponds to the corresponding timeline directory
     193              :     /// contents, stored locally in the pageserver workdir.
     194         2722 :     pub fn disk_consistent_lsn(&self) -> Lsn {
     195         2722 :         self.body.disk_consistent_lsn
     196         2722 :     }
     197              : 
     198          334 :     pub fn prev_record_lsn(&self) -> Option<Lsn> {
     199          334 :         self.body.prev_record_lsn
     200          334 :     }
     201              : 
     202          346 :     pub fn ancestor_timeline(&self) -> Option<TimelineId> {
     203          346 :         self.body.ancestor_timeline
     204          346 :     }
     205              : 
     206          334 :     pub fn ancestor_lsn(&self) -> Lsn {
     207          334 :         self.body.ancestor_lsn
     208          334 :     }
     209              : 
     210              :     /// When reparenting, the `ancestor_lsn` does not change.
     211            0 :     pub fn reparent(&mut self, timeline: &TimelineId) {
     212            0 :         assert!(self.body.ancestor_timeline.is_some());
     213              :         // no assertion for redoing this: it's fine, we may have to repeat this multiple times over
     214            0 :         self.body.ancestor_timeline = Some(*timeline);
     215            0 :     }
     216              : 
     217            0 :     pub fn detach_from_ancestor(&mut self, timeline: &TimelineId, ancestor_lsn: &Lsn) {
     218            0 :         if let Some(ancestor) = self.body.ancestor_timeline {
     219            0 :             assert_eq!(ancestor, *timeline);
     220            0 :         }
     221            0 :         if self.body.ancestor_lsn != Lsn(0) {
     222            0 :             assert_eq!(self.body.ancestor_lsn, *ancestor_lsn);
     223            0 :         }
     224            0 :         self.body.ancestor_timeline = None;
     225            0 :         self.body.ancestor_lsn = Lsn(0);
     226            0 :     }
     227              : 
     228          334 :     pub fn latest_gc_cutoff_lsn(&self) -> Lsn {
     229          334 :         self.body.latest_gc_cutoff_lsn
     230          334 :     }
     231              : 
     232          334 :     pub fn initdb_lsn(&self) -> Lsn {
     233          334 :         self.body.initdb_lsn
     234          334 :     }
     235              : 
     236          334 :     pub fn pg_version(&self) -> u32 {
     237          334 :         self.body.pg_version
     238          334 :     }
     239              : 
     240              :     // Checksums make it awkward to build a valid instance by hand.  This helper
     241              :     // provides a TimelineMetadata with a valid checksum in its header.
     242              :     #[cfg(test)]
     243           12 :     pub fn example() -> Self {
     244           12 :         let instance = Self::new(
     245           12 :             "0/16960E8".parse::<Lsn>().unwrap(),
     246           12 :             None,
     247           12 :             None,
     248           12 :             Lsn::from_hex("00000000").unwrap(),
     249           12 :             Lsn::from_hex("00000000").unwrap(),
     250           12 :             Lsn::from_hex("00000000").unwrap(),
     251           12 :             0,
     252           12 :         );
     253           12 :         let bytes = instance.to_bytes().unwrap();
     254           12 :         Self::from_bytes(&bytes).unwrap()
     255           12 :     }
     256              : 
     257          928 :     pub(crate) fn apply(&mut self, update: &MetadataUpdate) {
     258          928 :         self.body.disk_consistent_lsn = update.disk_consistent_lsn;
     259          928 :         self.body.prev_record_lsn = update.prev_record_lsn;
     260          928 :         self.body.latest_gc_cutoff_lsn = update.latest_gc_cutoff_lsn;
     261          928 :     }
     262              : }
     263              : 
     264              : impl<'de> Deserialize<'de> for TimelineMetadata {
     265           34 :     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     266           34 :     where
     267           34 :         D: serde::Deserializer<'de>,
     268           34 :     {
     269           34 :         let bytes = Vec::<u8>::deserialize(deserializer)?;
     270           34 :         Self::from_bytes(bytes.as_slice()).map_err(|e| D::Error::custom(format!("{e}")))
     271           34 :     }
     272              : }
     273              : 
     274              : impl Serialize for TimelineMetadata {
     275         1170 :     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     276         1170 :     where
     277         1170 :         S: Serializer,
     278         1170 :     {
     279         1170 :         let bytes = self
     280         1170 :             .to_bytes()
     281         1170 :             .map_err(|e| serde::ser::Error::custom(format!("{e}")))?;
     282         1170 :         bytes.serialize(serializer)
     283         1170 :     }
     284              : }
     285              : 
     286              : /// Parts of the metadata which are regularly modified.
     287              : pub(crate) struct MetadataUpdate {
     288              :     disk_consistent_lsn: Lsn,
     289              :     prev_record_lsn: Option<Lsn>,
     290              :     latest_gc_cutoff_lsn: Lsn,
     291              : }
     292              : 
     293              : impl MetadataUpdate {
     294          928 :     pub(crate) fn new(
     295          928 :         disk_consistent_lsn: Lsn,
     296          928 :         prev_record_lsn: Option<Lsn>,
     297          928 :         latest_gc_cutoff_lsn: Lsn,
     298          928 :     ) -> Self {
     299          928 :         Self {
     300          928 :             disk_consistent_lsn,
     301          928 :             prev_record_lsn,
     302          928 :             latest_gc_cutoff_lsn,
     303          928 :         }
     304          928 :     }
     305              : }
     306              : 
     307              : #[cfg(test)]
     308              : mod tests {
     309              :     use super::*;
     310              :     use crate::tenant::harness::TIMELINE_ID;
     311              : 
     312              :     #[test]
     313            2 :     fn metadata_serializes_correctly() {
     314            2 :         let original_metadata = TimelineMetadata::new(
     315            2 :             Lsn(0x200),
     316            2 :             Some(Lsn(0x100)),
     317            2 :             Some(TIMELINE_ID),
     318            2 :             Lsn(0),
     319            2 :             Lsn(0),
     320            2 :             Lsn(0),
     321            2 :             // Any version will do here, so use the default
     322            2 :             crate::DEFAULT_PG_VERSION,
     323            2 :         );
     324            2 : 
     325            2 :         let metadata_bytes = original_metadata
     326            2 :             .to_bytes()
     327            2 :             .expect("Should serialize correct metadata to bytes");
     328            2 : 
     329            2 :         let deserialized_metadata = TimelineMetadata::from_bytes(&metadata_bytes)
     330            2 :             .expect("Should deserialize its own bytes");
     331            2 : 
     332            2 :         assert_eq!(
     333              :             deserialized_metadata.body, original_metadata.body,
     334            0 :             "Metadata that was serialized to bytes and deserialized back should not change"
     335              :         );
     336            2 :     }
     337              : 
     338              :     // Generate old version metadata and read it with current code.
     339              :     // Ensure that it is upgraded correctly
     340              :     #[test]
     341            2 :     fn test_metadata_upgrade() {
     342            2 :         #[derive(Debug, Clone, PartialEq, Eq)]
     343            2 :         struct TimelineMetadataV1 {
     344            2 :             hdr: TimelineMetadataHeader,
     345            2 :             body: TimelineMetadataBodyV1,
     346            2 :         }
     347            2 : 
     348            2 :         let metadata_v1 = TimelineMetadataV1 {
     349            2 :             hdr: TimelineMetadataHeader {
     350            2 :                 checksum: 0,
     351            2 :                 size: 0,
     352            2 :                 format_version: METADATA_OLD_FORMAT_VERSION,
     353            2 :             },
     354            2 :             body: TimelineMetadataBodyV1 {
     355            2 :                 disk_consistent_lsn: Lsn(0x200),
     356            2 :                 prev_record_lsn: Some(Lsn(0x100)),
     357            2 :                 ancestor_timeline: Some(TIMELINE_ID),
     358            2 :                 ancestor_lsn: Lsn(0),
     359            2 :                 latest_gc_cutoff_lsn: Lsn(0),
     360            2 :                 initdb_lsn: Lsn(0),
     361            2 :             },
     362            2 :         };
     363            2 : 
     364            2 :         impl TimelineMetadataV1 {
     365            2 :             pub fn to_bytes(&self) -> anyhow::Result<Vec<u8>> {
     366            2 :                 let body_bytes = self.body.ser()?;
     367            2 :                 let metadata_size = METADATA_HDR_SIZE + body_bytes.len();
     368            2 :                 let hdr = TimelineMetadataHeader {
     369            2 :                     size: metadata_size as u16,
     370            2 :                     format_version: METADATA_OLD_FORMAT_VERSION,
     371            2 :                     checksum: crc32c::crc32c(&body_bytes),
     372            2 :                 };
     373            2 :                 let hdr_bytes = hdr.ser()?;
     374            2 :                 let mut metadata_bytes = vec![0u8; METADATA_MAX_SIZE];
     375            2 :                 metadata_bytes[0..METADATA_HDR_SIZE].copy_from_slice(&hdr_bytes);
     376            2 :                 metadata_bytes[METADATA_HDR_SIZE..metadata_size].copy_from_slice(&body_bytes);
     377            2 :                 Ok(metadata_bytes)
     378            2 :             }
     379            2 :         }
     380            2 : 
     381            2 :         let metadata_bytes = metadata_v1
     382            2 :             .to_bytes()
     383            2 :             .expect("Should serialize correct metadata to bytes");
     384            2 : 
     385            2 :         // This should deserialize to the latest version format
     386            2 :         let deserialized_metadata = TimelineMetadata::from_bytes(&metadata_bytes)
     387            2 :             .expect("Should deserialize its own bytes");
     388            2 : 
     389            2 :         let expected_metadata = TimelineMetadata::new(
     390            2 :             Lsn(0x200),
     391            2 :             Some(Lsn(0x100)),
     392            2 :             Some(TIMELINE_ID),
     393            2 :             Lsn(0),
     394            2 :             Lsn(0),
     395            2 :             Lsn(0),
     396            2 :             14, // All timelines created before this version had pg_version 14
     397            2 :         );
     398            2 : 
     399            2 :         assert_eq!(
     400              :             deserialized_metadata.body, expected_metadata.body,
     401            0 :             "Metadata of the old version {} should be upgraded to the latest version {}",
     402              :             METADATA_OLD_FORMAT_VERSION, METADATA_FORMAT_VERSION
     403              :         );
     404            2 :     }
     405              : 
     406              :     #[test]
     407            2 :     fn test_metadata_bincode_serde() {
     408            2 :         let original_metadata = TimelineMetadata::new(
     409            2 :             Lsn(0x200),
     410            2 :             Some(Lsn(0x100)),
     411            2 :             Some(TIMELINE_ID),
     412            2 :             Lsn(0),
     413            2 :             Lsn(0),
     414            2 :             Lsn(0),
     415            2 :             // Any version will do here, so use the default
     416            2 :             crate::DEFAULT_PG_VERSION,
     417            2 :         );
     418            2 :         let metadata_bytes = original_metadata
     419            2 :             .to_bytes()
     420            2 :             .expect("Cannot create bytes array from metadata");
     421            2 : 
     422            2 :         let metadata_bincode_be_bytes = original_metadata
     423            2 :             .ser()
     424            2 :             .expect("Cannot serialize the metadata");
     425            2 : 
     426            2 :         // 8 bytes for the length of the vector
     427            2 :         assert_eq!(metadata_bincode_be_bytes.len(), 8 + metadata_bytes.len());
     428              : 
     429            2 :         let expected_bincode_bytes = {
     430            2 :             let mut temp = vec![];
     431            2 :             let len_bytes = metadata_bytes.len().to_be_bytes();
     432            2 :             temp.extend_from_slice(&len_bytes);
     433            2 :             temp.extend_from_slice(&metadata_bytes);
     434            2 :             temp
     435            2 :         };
     436            2 :         assert_eq!(metadata_bincode_be_bytes, expected_bincode_bytes);
     437              : 
     438            2 :         let deserialized_metadata = TimelineMetadata::des(&metadata_bincode_be_bytes).unwrap();
     439            2 :         // Deserialized metadata has the metadata header, which is different from the serialized one.
     440            2 :         //   Reference: TimelineMetaData::to_bytes()
     441            2 :         let expected_metadata = {
     442            2 :             let mut temp_metadata = original_metadata;
     443            2 :             let body_bytes = temp_metadata
     444            2 :                 .body
     445            2 :                 .ser()
     446            2 :                 .expect("Cannot serialize the metadata body");
     447            2 :             let metadata_size = METADATA_HDR_SIZE + body_bytes.len();
     448            2 :             let hdr = TimelineMetadataHeader {
     449            2 :                 size: metadata_size as u16,
     450            2 :                 format_version: METADATA_FORMAT_VERSION,
     451            2 :                 checksum: crc32c::crc32c(&body_bytes),
     452            2 :             };
     453            2 :             temp_metadata.hdr = hdr;
     454            2 :             temp_metadata
     455            2 :         };
     456            2 :         assert_eq!(deserialized_metadata, expected_metadata);
     457            2 :     }
     458              : 
     459              :     #[test]
     460            2 :     fn test_metadata_bincode_serde_ensure_roundtrip() {
     461            2 :         let original_metadata = TimelineMetadata::new(
     462            2 :             Lsn(0x200),
     463            2 :             Some(Lsn(0x100)),
     464            2 :             Some(TIMELINE_ID),
     465            2 :             Lsn(0),
     466            2 :             Lsn(0),
     467            2 :             Lsn(0),
     468            2 :             // Any version will do here, so use the default
     469            2 :             crate::DEFAULT_PG_VERSION,
     470            2 :         );
     471            2 :         let expected_bytes = vec![
     472            2 :             /* bincode length encoding bytes */
     473            2 :             0, 0, 0, 0, 0, 0, 2, 0, // 8 bytes for the length of the serialized vector
     474            2 :             /* TimelineMetadataHeader */
     475            2 :             4, 37, 101, 34, 0, 70, 0, 4, // checksum, size, format_version (4 + 2 + 2)
     476            2 :             /* TimelineMetadataBodyV2 */
     477            2 :             0, 0, 0, 0, 0, 0, 2, 0, // disk_consistent_lsn (8 bytes)
     478            2 :             1, 0, 0, 0, 0, 0, 0, 1, 0, // prev_record_lsn (9 bytes)
     479            2 :             1, 17, 34, 51, 68, 85, 102, 119, 136, 17, 34, 51, 68, 85, 102, 119,
     480            2 :             136, // ancestor_timeline (17 bytes)
     481            2 :             0, 0, 0, 0, 0, 0, 0, 0, // ancestor_lsn (8 bytes)
     482            2 :             0, 0, 0, 0, 0, 0, 0, 0, // latest_gc_cutoff_lsn (8 bytes)
     483            2 :             0, 0, 0, 0, 0, 0, 0, 0, // initdb_lsn (8 bytes)
     484            2 :             0, 0, 0, 15, // pg_version (4 bytes)
     485            2 :             /* padding bytes */
     486            2 :             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     487            2 :             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     488            2 :             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     489            2 :             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     490            2 :             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     491            2 :             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     492            2 :             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     493            2 :             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     494            2 :             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     495            2 :             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     496            2 :             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     497            2 :             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     498            2 :             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     499            2 :             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     500            2 :             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     501            2 :             0, 0, 0, 0, 0, 0, 0,
     502            2 :         ];
     503            2 :         let metadata_ser_bytes = original_metadata.ser().unwrap();
     504            2 :         assert_eq!(metadata_ser_bytes, expected_bytes);
     505              : 
     506            2 :         let expected_metadata = {
     507            2 :             let mut temp_metadata = original_metadata;
     508            2 :             let body_bytes = temp_metadata
     509            2 :                 .body
     510            2 :                 .ser()
     511            2 :                 .expect("Cannot serialize the metadata body");
     512            2 :             let metadata_size = METADATA_HDR_SIZE + body_bytes.len();
     513            2 :             let hdr = TimelineMetadataHeader {
     514            2 :                 size: metadata_size as u16,
     515            2 :                 format_version: METADATA_FORMAT_VERSION,
     516            2 :                 checksum: crc32c::crc32c(&body_bytes),
     517            2 :             };
     518            2 :             temp_metadata.hdr = hdr;
     519            2 :             temp_metadata
     520            2 :         };
     521            2 :         let des_metadata = TimelineMetadata::des(&metadata_ser_bytes).unwrap();
     522            2 :         assert_eq!(des_metadata, expected_metadata);
     523            2 :     }
     524              : }
        

Generated by: LCOV version 2.1-beta