LCOV - code coverage report
Current view: top level - safekeeper/src - control_file_upgrade.rs (source / functions) Coverage Total Hit
Test: c639aa5f7ab62b43d647b10f40d15a15686ce8a9.info Lines: 65.0 % 354 230
Test Date: 2024-02-12 20:26:03 Functions: 35.9 % 153 55

            Line data    Source code
       1              : //! Code to deal with safekeeper control file upgrades
       2              : use crate::{
       3              :     safekeeper::{AcceptorState, PgUuid, ServerInfo, Term, TermHistory, TermLsn},
       4              :     state::{PersistedPeers, TimelinePersistentState},
       5              : };
       6              : use anyhow::{bail, Result};
       7              : use pq_proto::SystemId;
       8              : use serde::{Deserialize, Serialize};
       9              : use tracing::*;
      10              : use utils::{
      11              :     bin_ser::LeSer,
      12              :     id::{TenantId, TimelineId},
      13              :     lsn::Lsn,
      14              : };
      15              : 
      16              : /// Persistent consensus state of the acceptor.
      17            4 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
      18              : struct AcceptorStateV1 {
      19              :     /// acceptor's last term it voted for (advanced in 1 phase)
      20              :     term: Term,
      21              :     /// acceptor's epoch (advanced, i.e. bumped to 'term' when VCL is reached).
      22              :     epoch: Term,
      23              : }
      24              : 
      25            4 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
      26              : struct SafeKeeperStateV1 {
      27              :     /// persistent acceptor state
      28              :     acceptor_state: AcceptorStateV1,
      29              :     /// information about server
      30              :     server: ServerInfoV2,
      31              :     /// Unique id of the last *elected* proposer we dealt with. Not needed
      32              :     /// for correctness, exists for monitoring purposes.
      33              :     proposer_uuid: PgUuid,
      34              :     /// part of WAL acknowledged by quorum and available locally
      35              :     commit_lsn: Lsn,
      36              :     /// minimal LSN which may be needed for recovery of some safekeeper (end_lsn
      37              :     /// of last record streamed to everyone)
      38              :     truncate_lsn: Lsn,
      39              :     // Safekeeper starts receiving WAL from this LSN, zeros before it ought to
      40              :     // be skipped during decoding.
      41              :     wal_start_lsn: Lsn,
      42              : }
      43              : 
      44            8 : #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
      45              : pub struct ServerInfoV2 {
      46              :     /// Postgres server version
      47              :     pub pg_version: u32,
      48              :     pub system_id: SystemId,
      49              :     pub tenant_id: TenantId,
      50              :     pub timeline_id: TimelineId,
      51              :     pub wal_seg_size: u32,
      52              : }
      53              : 
      54            4 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
      55              : pub struct SafeKeeperStateV2 {
      56              :     /// persistent acceptor state
      57              :     pub acceptor_state: AcceptorState,
      58              :     /// information about server
      59              :     pub server: ServerInfoV2,
      60              :     /// Unique id of the last *elected* proposer we dealt with. Not needed
      61              :     /// for correctness, exists for monitoring purposes.
      62              :     pub proposer_uuid: PgUuid,
      63              :     /// part of WAL acknowledged by quorum and available locally
      64              :     pub commit_lsn: Lsn,
      65              :     /// minimal LSN which may be needed for recovery of some safekeeper (end_lsn
      66              :     /// of last record streamed to everyone)
      67              :     pub truncate_lsn: Lsn,
      68              :     // Safekeeper starts receiving WAL from this LSN, zeros before it ought to
      69              :     // be skipped during decoding.
      70              :     pub wal_start_lsn: Lsn,
      71              : }
      72              : 
      73            6 : #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
      74              : pub struct ServerInfoV3 {
      75              :     /// Postgres server version
      76              :     pub pg_version: u32,
      77              :     pub system_id: SystemId,
      78              :     #[serde(with = "hex")]
      79              :     pub tenant_id: TenantId,
      80              :     #[serde(with = "hex")]
      81              :     pub timeline_id: TimelineId,
      82              :     pub wal_seg_size: u32,
      83              : }
      84              : 
      85            4 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
      86              : pub struct SafeKeeperStateV3 {
      87              :     /// persistent acceptor state
      88              :     pub acceptor_state: AcceptorState,
      89              :     /// information about server
      90              :     pub server: ServerInfoV3,
      91              :     /// Unique id of the last *elected* proposer we dealt with. Not needed
      92              :     /// for correctness, exists for monitoring purposes.
      93              :     #[serde(with = "hex")]
      94              :     pub proposer_uuid: PgUuid,
      95              :     /// part of WAL acknowledged by quorum and available locally
      96              :     pub commit_lsn: Lsn,
      97              :     /// minimal LSN which may be needed for recovery of some safekeeper (end_lsn
      98              :     /// of last record streamed to everyone)
      99              :     pub truncate_lsn: Lsn,
     100              :     // Safekeeper starts receiving WAL from this LSN, zeros before it ought to
     101              :     // be skipped during decoding.
     102              :     pub wal_start_lsn: Lsn,
     103              : }
     104              : 
     105            8 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
     106              : pub struct SafeKeeperStateV4 {
     107              :     #[serde(with = "hex")]
     108              :     pub tenant_id: TenantId,
     109              :     #[serde(with = "hex")]
     110              :     pub timeline_id: TimelineId,
     111              :     /// persistent acceptor state
     112              :     pub acceptor_state: AcceptorState,
     113              :     /// information about server
     114              :     pub server: ServerInfo,
     115              :     /// Unique id of the last *elected* proposer we dealt with. Not needed
     116              :     /// for correctness, exists for monitoring purposes.
     117              :     #[serde(with = "hex")]
     118              :     pub proposer_uuid: PgUuid,
     119              :     /// Part of WAL acknowledged by quorum and available locally. Always points
     120              :     /// to record boundary.
     121              :     pub commit_lsn: Lsn,
     122              :     /// First LSN not yet offloaded to s3. Useful to persist to avoid finding
     123              :     /// out offloading progress on boot.
     124              :     pub s3_wal_lsn: Lsn,
     125              :     /// Minimal LSN which may be needed for recovery of some safekeeper (end_lsn
     126              :     /// of last record streamed to everyone). Persisting it helps skipping
     127              :     /// recovery in walproposer, generally we compute it from peers. In
     128              :     /// walproposer proto called 'truncate_lsn'.
     129              :     pub peer_horizon_lsn: Lsn,
     130              :     /// LSN of the oldest known checkpoint made by pageserver and successfully
     131              :     /// pushed to s3. We don't remove WAL beyond it. Persisted only for
     132              :     /// informational purposes, we receive it from pageserver (or broker).
     133              :     pub remote_consistent_lsn: Lsn,
     134              :     // Peers and their state as we remember it. Knowing peers themselves is
     135              :     // fundamental; but state is saved here only for informational purposes and
     136              :     // obviously can be stale. (Currently not saved at all, but let's provision
     137              :     // place to have less file version upgrades).
     138              :     pub peers: PersistedPeers,
     139              : }
     140              : 
     141            0 : pub fn upgrade_control_file(buf: &[u8], version: u32) -> Result<TimelinePersistentState> {
     142            0 :     // migrate to storing full term history
     143            0 :     if version == 1 {
     144            0 :         info!("reading safekeeper control file version {}", version);
     145            0 :         let oldstate = SafeKeeperStateV1::des(&buf[..buf.len()])?;
     146            0 :         let ac = AcceptorState {
     147            0 :             term: oldstate.acceptor_state.term,
     148            0 :             term_history: TermHistory(vec![TermLsn {
     149            0 :                 term: oldstate.acceptor_state.epoch,
     150            0 :                 lsn: Lsn(0),
     151            0 :             }]),
     152            0 :         };
     153            0 :         return Ok(TimelinePersistentState {
     154            0 :             tenant_id: oldstate.server.tenant_id,
     155            0 :             timeline_id: oldstate.server.timeline_id,
     156            0 :             acceptor_state: ac,
     157            0 :             server: ServerInfo {
     158            0 :                 pg_version: oldstate.server.pg_version,
     159            0 :                 system_id: oldstate.server.system_id,
     160            0 :                 wal_seg_size: oldstate.server.wal_seg_size,
     161            0 :             },
     162            0 :             proposer_uuid: oldstate.proposer_uuid,
     163            0 :             timeline_start_lsn: Lsn(0),
     164            0 :             local_start_lsn: Lsn(0),
     165            0 :             commit_lsn: oldstate.commit_lsn,
     166            0 :             backup_lsn: Lsn(0),
     167            0 :             peer_horizon_lsn: oldstate.truncate_lsn,
     168            0 :             remote_consistent_lsn: Lsn(0),
     169            0 :             peers: PersistedPeers(vec![]),
     170            0 :         });
     171              :     // migrate to hexing some ids
     172            0 :     } else if version == 2 {
     173            0 :         info!("reading safekeeper control file version {}", version);
     174            0 :         let oldstate = SafeKeeperStateV2::des(&buf[..buf.len()])?;
     175            0 :         let server = ServerInfo {
     176            0 :             pg_version: oldstate.server.pg_version,
     177            0 :             system_id: oldstate.server.system_id,
     178            0 :             wal_seg_size: oldstate.server.wal_seg_size,
     179            0 :         };
     180            0 :         return Ok(TimelinePersistentState {
     181            0 :             tenant_id: oldstate.server.tenant_id,
     182            0 :             timeline_id: oldstate.server.timeline_id,
     183            0 :             acceptor_state: oldstate.acceptor_state,
     184            0 :             server,
     185            0 :             proposer_uuid: oldstate.proposer_uuid,
     186            0 :             timeline_start_lsn: Lsn(0),
     187            0 :             local_start_lsn: Lsn(0),
     188            0 :             commit_lsn: oldstate.commit_lsn,
     189            0 :             backup_lsn: Lsn(0),
     190            0 :             peer_horizon_lsn: oldstate.truncate_lsn,
     191            0 :             remote_consistent_lsn: Lsn(0),
     192            0 :             peers: PersistedPeers(vec![]),
     193            0 :         });
     194              :     // migrate to moving tenant_id/timeline_id to the top and adding some lsns
     195            0 :     } else if version == 3 {
     196            0 :         info!("reading safekeeper control file version {version}");
     197            0 :         let oldstate = SafeKeeperStateV3::des(&buf[..buf.len()])?;
     198            0 :         let server = ServerInfo {
     199            0 :             pg_version: oldstate.server.pg_version,
     200            0 :             system_id: oldstate.server.system_id,
     201            0 :             wal_seg_size: oldstate.server.wal_seg_size,
     202            0 :         };
     203            0 :         return Ok(TimelinePersistentState {
     204            0 :             tenant_id: oldstate.server.tenant_id,
     205            0 :             timeline_id: oldstate.server.timeline_id,
     206            0 :             acceptor_state: oldstate.acceptor_state,
     207            0 :             server,
     208            0 :             proposer_uuid: oldstate.proposer_uuid,
     209            0 :             timeline_start_lsn: Lsn(0),
     210            0 :             local_start_lsn: Lsn(0),
     211            0 :             commit_lsn: oldstate.commit_lsn,
     212            0 :             backup_lsn: Lsn(0),
     213            0 :             peer_horizon_lsn: oldstate.truncate_lsn,
     214            0 :             remote_consistent_lsn: Lsn(0),
     215            0 :             peers: PersistedPeers(vec![]),
     216            0 :         });
     217              :     // migrate to having timeline_start_lsn
     218            0 :     } else if version == 4 {
     219            0 :         info!("reading safekeeper control file version {}", version);
     220            0 :         let oldstate = SafeKeeperStateV4::des(&buf[..buf.len()])?;
     221            0 :         let server = ServerInfo {
     222            0 :             pg_version: oldstate.server.pg_version,
     223            0 :             system_id: oldstate.server.system_id,
     224            0 :             wal_seg_size: oldstate.server.wal_seg_size,
     225            0 :         };
     226            0 :         return Ok(TimelinePersistentState {
     227            0 :             tenant_id: oldstate.tenant_id,
     228            0 :             timeline_id: oldstate.timeline_id,
     229            0 :             acceptor_state: oldstate.acceptor_state,
     230            0 :             server,
     231            0 :             proposer_uuid: oldstate.proposer_uuid,
     232            0 :             timeline_start_lsn: Lsn(0),
     233            0 :             local_start_lsn: Lsn(0),
     234            0 :             commit_lsn: oldstate.commit_lsn,
     235            0 :             backup_lsn: Lsn::INVALID,
     236            0 :             peer_horizon_lsn: oldstate.peer_horizon_lsn,
     237            0 :             remote_consistent_lsn: Lsn(0),
     238            0 :             peers: PersistedPeers(vec![]),
     239            0 :         });
     240            0 :     } else if version == 5 {
     241            0 :         info!("reading safekeeper control file version {}", version);
     242            0 :         let mut oldstate = TimelinePersistentState::des(&buf[..buf.len()])?;
     243            0 :         if oldstate.timeline_start_lsn != Lsn(0) {
     244            0 :             return Ok(oldstate);
     245            0 :         }
     246            0 : 
     247            0 :         // set special timeline_start_lsn because we don't know the real one
     248            0 :         info!("setting timeline_start_lsn and local_start_lsn to Lsn(1)");
     249            0 :         oldstate.timeline_start_lsn = Lsn(1);
     250            0 :         oldstate.local_start_lsn = Lsn(1);
     251            0 : 
     252            0 :         return Ok(oldstate);
     253            0 :     } else if version == 6 {
     254            0 :         info!("reading safekeeper control file version {}", version);
     255            0 :         let mut oldstate = TimelinePersistentState::des(&buf[..buf.len()])?;
     256            0 :         if oldstate.server.pg_version != 0 {
     257            0 :             return Ok(oldstate);
     258            0 :         }
     259            0 : 
     260            0 :         // set pg_version to the default v14
     261            0 :         info!("setting pg_version to 140005");
     262            0 :         oldstate.server.pg_version = 140005;
     263            0 : 
     264            0 :         return Ok(oldstate);
     265            0 :     }
     266            0 :     bail!("unsupported safekeeper control file version {}", version)
     267            0 : }
     268              : 
     269              : #[cfg(test)]
     270              : mod tests {
     271              :     use std::str::FromStr;
     272              : 
     273              :     use utils::{id::NodeId, Hex};
     274              : 
     275              :     use crate::safekeeper::PersistedPeerInfo;
     276              : 
     277              :     use super::*;
     278              : 
     279            2 :     #[test]
     280            2 :     fn roundtrip_v1() {
     281            2 :         let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
     282            2 :         let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
     283            2 :         let state = SafeKeeperStateV1 {
     284            2 :             acceptor_state: AcceptorStateV1 {
     285            2 :                 term: 42,
     286            2 :                 epoch: 43,
     287            2 :             },
     288            2 :             server: ServerInfoV2 {
     289            2 :                 pg_version: 14,
     290            2 :                 system_id: 0x1234567887654321,
     291            2 :                 tenant_id,
     292            2 :                 timeline_id,
     293            2 :                 wal_seg_size: 0x12345678,
     294            2 :             },
     295            2 :             proposer_uuid: {
     296            2 :                 let mut arr = timeline_id.as_arr();
     297            2 :                 arr.reverse();
     298            2 :                 arr
     299            2 :             },
     300            2 :             commit_lsn: Lsn(1234567800),
     301            2 :             truncate_lsn: Lsn(123456780),
     302            2 :             wal_start_lsn: Lsn(1234567800 - 8),
     303            2 :         };
     304            2 : 
     305            2 :         let ser = state.ser().unwrap();
     306            2 :         #[rustfmt::skip]
     307            2 :         let expected = [
     308            2 :             // term
     309            2 :             0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     310            2 :             // epoch
     311            2 :             0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     312            2 :             // pg_version
     313            2 :             0x0e, 0x00, 0x00, 0x00,
     314            2 :             // system_id
     315            2 :             0x21, 0x43, 0x65, 0x87, 0x78, 0x56, 0x34, 0x12,
     316            2 :             // tenant_id
     317            2 :             0xcf, 0x04, 0x80, 0x92, 0x97, 0x07, 0xee, 0x75, 0x37, 0x23, 0x37, 0xef, 0xaa, 0x5e, 0xcf, 0x96,
     318            2 :             // timeline_id
     319            2 :             0x11, 0x2d, 0xed, 0x66, 0x42, 0x2a, 0xa5, 0xe9, 0x53, 0xe5, 0x44, 0x0f, 0xa5, 0x42, 0x7a, 0xc4,
     320            2 :             // wal_seg_size
     321            2 :             0x78, 0x56, 0x34, 0x12,
     322            2 :             // proposer_uuid
     323            2 :             0xc4, 0x7a, 0x42, 0xa5, 0x0f, 0x44, 0xe5, 0x53, 0xe9, 0xa5, 0x2a, 0x42, 0x66, 0xed, 0x2d, 0x11,
     324            2 :             // commit_lsn
     325            2 :             0x78, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
     326            2 :             // truncate_lsn
     327            2 :             0x0c, 0xcd, 0x5b, 0x07, 0x00, 0x00, 0x00, 0x00,
     328            2 :             // wal_start_lsn
     329            2 :             0x70, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
     330            2 :         ];
     331            2 : 
     332            2 :         assert_eq!(Hex(&ser), Hex(&expected));
     333              : 
     334            2 :         let deser = SafeKeeperStateV1::des(&ser).unwrap();
     335            2 : 
     336            2 :         assert_eq!(state, deser);
     337            2 :     }
     338              : 
     339            2 :     #[test]
     340            2 :     fn roundtrip_v2() {
     341            2 :         let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
     342            2 :         let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
     343            2 :         let state = SafeKeeperStateV2 {
     344            2 :             acceptor_state: AcceptorState {
     345            2 :                 term: 42,
     346            2 :                 term_history: TermHistory(vec![TermLsn {
     347            2 :                     lsn: Lsn(0x1),
     348            2 :                     term: 41,
     349            2 :                 }]),
     350            2 :             },
     351            2 :             server: ServerInfoV2 {
     352            2 :                 pg_version: 14,
     353            2 :                 system_id: 0x1234567887654321,
     354            2 :                 tenant_id,
     355            2 :                 timeline_id,
     356            2 :                 wal_seg_size: 0x12345678,
     357            2 :             },
     358            2 :             proposer_uuid: {
     359            2 :                 let mut arr = timeline_id.as_arr();
     360            2 :                 arr.reverse();
     361            2 :                 arr
     362            2 :             },
     363            2 :             commit_lsn: Lsn(1234567800),
     364            2 :             truncate_lsn: Lsn(123456780),
     365            2 :             wal_start_lsn: Lsn(1234567800 - 8),
     366            2 :         };
     367            2 : 
     368            2 :         let ser = state.ser().unwrap();
     369            2 :         let expected = [
     370            2 :             0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
     371            2 :             0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
     372            2 :             0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x21, 0x43, 0x65, 0x87, 0x78, 0x56,
     373            2 :             0x34, 0x12, 0xcf, 0x04, 0x80, 0x92, 0x97, 0x07, 0xee, 0x75, 0x37, 0x23, 0x37, 0xef,
     374            2 :             0xaa, 0x5e, 0xcf, 0x96, 0x11, 0x2d, 0xed, 0x66, 0x42, 0x2a, 0xa5, 0xe9, 0x53, 0xe5,
     375            2 :             0x44, 0x0f, 0xa5, 0x42, 0x7a, 0xc4, 0x78, 0x56, 0x34, 0x12, 0xc4, 0x7a, 0x42, 0xa5,
     376            2 :             0x0f, 0x44, 0xe5, 0x53, 0xe9, 0xa5, 0x2a, 0x42, 0x66, 0xed, 0x2d, 0x11, 0x78, 0x02,
     377            2 :             0x96, 0x49, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xcd, 0x5b, 0x07, 0x00, 0x00, 0x00, 0x00,
     378            2 :             0x70, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
     379            2 :         ];
     380            2 : 
     381            2 :         assert_eq!(Hex(&ser), Hex(&expected));
     382              : 
     383            2 :         let deser = SafeKeeperStateV2::des(&ser).unwrap();
     384            2 : 
     385            2 :         assert_eq!(state, deser);
     386            2 :     }
     387              : 
     388            2 :     #[test]
     389            2 :     fn roundtrip_v3() {
     390            2 :         let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
     391            2 :         let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
     392            2 :         let state = SafeKeeperStateV3 {
     393            2 :             acceptor_state: AcceptorState {
     394            2 :                 term: 42,
     395            2 :                 term_history: TermHistory(vec![TermLsn {
     396            2 :                     lsn: Lsn(0x1),
     397            2 :                     term: 41,
     398            2 :                 }]),
     399            2 :             },
     400            2 :             server: ServerInfoV3 {
     401            2 :                 pg_version: 14,
     402            2 :                 system_id: 0x1234567887654321,
     403            2 :                 tenant_id,
     404            2 :                 timeline_id,
     405            2 :                 wal_seg_size: 0x12345678,
     406            2 :             },
     407            2 :             proposer_uuid: {
     408            2 :                 let mut arr = timeline_id.as_arr();
     409            2 :                 arr.reverse();
     410            2 :                 arr
     411            2 :             },
     412            2 :             commit_lsn: Lsn(1234567800),
     413            2 :             truncate_lsn: Lsn(123456780),
     414            2 :             wal_start_lsn: Lsn(1234567800 - 8),
     415            2 :         };
     416            2 : 
     417            2 :         let ser = state.ser().unwrap();
     418            2 :         let expected = [
     419            2 :             0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
     420            2 :             0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
     421            2 :             0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x21, 0x43, 0x65, 0x87, 0x78, 0x56,
     422            2 :             0x34, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x66, 0x30, 0x34,
     423            2 :             0x38, 0x30, 0x39, 0x32, 0x39, 0x37, 0x30, 0x37, 0x65, 0x65, 0x37, 0x35, 0x33, 0x37,
     424            2 :             0x32, 0x33, 0x33, 0x37, 0x65, 0x66, 0x61, 0x61, 0x35, 0x65, 0x63, 0x66, 0x39, 0x36,
     425            2 :             0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31, 0x32, 0x64, 0x65, 0x64,
     426            2 :             0x36, 0x36, 0x34, 0x32, 0x32, 0x61, 0x61, 0x35, 0x65, 0x39, 0x35, 0x33, 0x65, 0x35,
     427            2 :             0x34, 0x34, 0x30, 0x66, 0x61, 0x35, 0x34, 0x32, 0x37, 0x61, 0x63, 0x34, 0x78, 0x56,
     428            2 :             0x34, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x34, 0x37, 0x61,
     429            2 :             0x34, 0x32, 0x61, 0x35, 0x30, 0x66, 0x34, 0x34, 0x65, 0x35, 0x35, 0x33, 0x65, 0x39,
     430            2 :             0x61, 0x35, 0x32, 0x61, 0x34, 0x32, 0x36, 0x36, 0x65, 0x64, 0x32, 0x64, 0x31, 0x31,
     431            2 :             0x78, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xcd, 0x5b, 0x07, 0x00, 0x00,
     432            2 :             0x00, 0x00, 0x70, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
     433            2 :         ];
     434            2 : 
     435            2 :         assert_eq!(Hex(&ser), Hex(&expected));
     436              : 
     437            2 :         let deser = SafeKeeperStateV3::des(&ser).unwrap();
     438            2 : 
     439            2 :         assert_eq!(state, deser);
     440            2 :     }
     441              : 
     442            2 :     #[test]
     443            2 :     fn roundtrip_v4() {
     444            2 :         let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
     445            2 :         let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
     446            2 :         let state = SafeKeeperStateV4 {
     447            2 :             tenant_id,
     448            2 :             timeline_id,
     449            2 :             acceptor_state: AcceptorState {
     450            2 :                 term: 42,
     451            2 :                 term_history: TermHistory(vec![TermLsn {
     452            2 :                     lsn: Lsn(0x1),
     453            2 :                     term: 41,
     454            2 :                 }]),
     455            2 :             },
     456            2 :             server: ServerInfo {
     457            2 :                 pg_version: 14,
     458            2 :                 system_id: 0x1234567887654321,
     459            2 :                 wal_seg_size: 0x12345678,
     460            2 :             },
     461            2 :             proposer_uuid: {
     462            2 :                 let mut arr = timeline_id.as_arr();
     463            2 :                 arr.reverse();
     464            2 :                 arr
     465            2 :             },
     466            2 :             peers: PersistedPeers(vec![(
     467            2 :                 NodeId(1),
     468            2 :                 PersistedPeerInfo {
     469            2 :                     backup_lsn: Lsn(1234567000),
     470            2 :                     term: 42,
     471            2 :                     flush_lsn: Lsn(1234567800 - 8),
     472            2 :                     commit_lsn: Lsn(1234567600),
     473            2 :                 },
     474            2 :             )]),
     475            2 :             commit_lsn: Lsn(1234567800),
     476            2 :             s3_wal_lsn: Lsn(1234567300),
     477            2 :             peer_horizon_lsn: Lsn(9999999),
     478            2 :             remote_consistent_lsn: Lsn(1234560000),
     479            2 :         };
     480            2 : 
     481            2 :         let ser = state.ser().unwrap();
     482            2 :         let expected = [
     483            2 :             0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x66, 0x30, 0x34, 0x38, 0x30,
     484            2 :             0x39, 0x32, 0x39, 0x37, 0x30, 0x37, 0x65, 0x65, 0x37, 0x35, 0x33, 0x37, 0x32, 0x33,
     485            2 :             0x33, 0x37, 0x65, 0x66, 0x61, 0x61, 0x35, 0x65, 0x63, 0x66, 0x39, 0x36, 0x20, 0x00,
     486            2 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31, 0x32, 0x64, 0x65, 0x64, 0x36, 0x36,
     487            2 :             0x34, 0x32, 0x32, 0x61, 0x61, 0x35, 0x65, 0x39, 0x35, 0x33, 0x65, 0x35, 0x34, 0x34,
     488            2 :             0x30, 0x66, 0x61, 0x35, 0x34, 0x32, 0x37, 0x61, 0x63, 0x34, 0x2a, 0x00, 0x00, 0x00,
     489            2 :             0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00,
     490            2 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     491            2 :             0x0e, 0x00, 0x00, 0x00, 0x21, 0x43, 0x65, 0x87, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56,
     492            2 :             0x34, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x34, 0x37, 0x61,
     493            2 :             0x34, 0x32, 0x61, 0x35, 0x30, 0x66, 0x34, 0x34, 0x65, 0x35, 0x35, 0x33, 0x65, 0x39,
     494            2 :             0x61, 0x35, 0x32, 0x61, 0x34, 0x32, 0x36, 0x36, 0x65, 0x64, 0x32, 0x64, 0x31, 0x31,
     495            2 :             0x78, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x96, 0x49, 0x00, 0x00,
     496            2 :             0x00, 0x00, 0x7f, 0x96, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x95, 0x49,
     497            2 :             0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
     498            2 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0xff, 0x95, 0x49, 0x00, 0x00, 0x00, 0x00,
     499            2 :             0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x02, 0x96, 0x49, 0x00, 0x00,
     500            2 :             0x00, 0x00, 0xb0, 0x01, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
     501            2 :         ];
     502            2 : 
     503            2 :         assert_eq!(Hex(&ser), Hex(&expected));
     504              : 
     505            2 :         let deser = SafeKeeperStateV4::des(&ser).unwrap();
     506            2 : 
     507            2 :         assert_eq!(state, deser);
     508            2 :     }
     509              : }
        

Generated by: LCOV version 2.1-beta