LCOV - code coverage report
Current view: top level - safekeeper/src - control_file_upgrade.rs (source / functions) Coverage Total Hit
Test: d0b82bdf1b9d596480e63a0c6e41224b64f46ce3.info Lines: 59.9 % 377 226
Test Date: 2024-06-20 08:53:55 Functions: 26.3 % 99 26

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

Generated by: LCOV version 2.1-beta