LCOV - code coverage report
Current view: top level - safekeeper/src - control_file_upgrade.rs (source / functions) Coverage Total Hit
Test: b4ae4c4857f9ef3e144e982a35ee23bc84c71983.info Lines: 65.2 % 419 273
Test Date: 2024-10-22 22:13:45 Functions: 29.6 % 115 34

            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::{EvictionState, 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            1 : #[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            1 : #[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            2 : #[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            1 : #[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            4 : #[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            2 : #[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            6 : #[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              : /// Persistent information stored on safekeeper node about timeline.
     187              : /// On disk data is prefixed by magic and format version and followed by checksum.
     188            8 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
     189              : pub struct SafeKeeperStateV8 {
     190              :     #[serde(with = "hex")]
     191              :     pub tenant_id: TenantId,
     192              :     #[serde(with = "hex")]
     193              :     pub timeline_id: TimelineId,
     194              :     /// persistent acceptor state
     195              :     pub acceptor_state: AcceptorState,
     196              :     /// information about server
     197              :     pub server: ServerInfo,
     198              :     /// Unique id of the last *elected* proposer we dealt with. Not needed
     199              :     /// for correctness, exists for monitoring purposes.
     200              :     #[serde(with = "hex")]
     201              :     pub proposer_uuid: PgUuid,
     202              :     /// Since which LSN this timeline generally starts. Safekeeper might have
     203              :     /// joined later.
     204              :     pub timeline_start_lsn: Lsn,
     205              :     /// Since which LSN safekeeper has (had) WAL for this timeline.
     206              :     /// All WAL segments next to one containing local_start_lsn are
     207              :     /// filled with data from the beginning.
     208              :     pub local_start_lsn: Lsn,
     209              :     /// Part of WAL acknowledged by quorum *and available locally*. Always points
     210              :     /// to record boundary.
     211              :     pub commit_lsn: Lsn,
     212              :     /// LSN that points to the end of the last backed up segment. Useful to
     213              :     /// persist to avoid finding out offloading progress on boot.
     214              :     pub backup_lsn: Lsn,
     215              :     /// Minimal LSN which may be needed for recovery of some safekeeper (end_lsn
     216              :     /// of last record streamed to everyone). Persisting it helps skipping
     217              :     /// recovery in walproposer, generally we compute it from peers. In
     218              :     /// walproposer proto called 'truncate_lsn'. Updates are currently drived
     219              :     /// only by walproposer.
     220              :     pub peer_horizon_lsn: Lsn,
     221              :     /// LSN of the oldest known checkpoint made by pageserver and successfully
     222              :     /// pushed to s3. We don't remove WAL beyond it. Persisted only for
     223              :     /// informational purposes, we receive it from pageserver (or broker).
     224              :     pub remote_consistent_lsn: Lsn,
     225              :     /// Peers and their state as we remember it. Knowing peers themselves is
     226              :     /// fundamental; but state is saved here only for informational purposes and
     227              :     /// obviously can be stale. (Currently not saved at all, but let's provision
     228              :     /// place to have less file version upgrades).
     229              :     pub peers: PersistedPeers,
     230              :     /// Holds names of partial segments uploaded to remote storage. Used to
     231              :     /// clean up old objects without leaving garbage in remote storage.
     232              :     pub partial_backup: wal_backup_partial::State,
     233              : }
     234              : 
     235            2 : pub fn upgrade_control_file(buf: &[u8], version: u32) -> Result<TimelinePersistentState> {
     236            2 :     // migrate to storing full term history
     237            2 :     if version == 1 {
     238            0 :         info!("reading safekeeper control file version {}", version);
     239            0 :         let oldstate = SafeKeeperStateV1::des(&buf[..buf.len()])?;
     240            0 :         let ac = AcceptorState {
     241            0 :             term: oldstate.acceptor_state.term,
     242            0 :             term_history: TermHistory(vec![TermLsn {
     243            0 :                 term: oldstate.acceptor_state.epoch,
     244            0 :                 lsn: Lsn(0),
     245            0 :             }]),
     246            0 :         };
     247            0 :         return Ok(TimelinePersistentState {
     248            0 :             tenant_id: oldstate.server.tenant_id,
     249            0 :             timeline_id: oldstate.server.timeline_id,
     250            0 :             acceptor_state: ac,
     251            0 :             server: ServerInfo {
     252            0 :                 pg_version: oldstate.server.pg_version,
     253            0 :                 system_id: oldstate.server.system_id,
     254            0 :                 wal_seg_size: oldstate.server.wal_seg_size,
     255            0 :             },
     256            0 :             proposer_uuid: oldstate.proposer_uuid,
     257            0 :             timeline_start_lsn: Lsn(0),
     258            0 :             local_start_lsn: Lsn(0),
     259            0 :             commit_lsn: oldstate.commit_lsn,
     260            0 :             backup_lsn: Lsn(0),
     261            0 :             peer_horizon_lsn: oldstate.truncate_lsn,
     262            0 :             remote_consistent_lsn: Lsn(0),
     263            0 :             peers: PersistedPeers(vec![]),
     264            0 :             partial_backup: wal_backup_partial::State::default(),
     265            0 :             eviction_state: EvictionState::Present,
     266            0 :         });
     267              :     // migrate to hexing some ids
     268            2 :     } else if version == 2 {
     269            0 :         info!("reading safekeeper control file version {}", version);
     270            0 :         let oldstate = SafeKeeperStateV2::des(&buf[..buf.len()])?;
     271            0 :         let server = ServerInfo {
     272            0 :             pg_version: oldstate.server.pg_version,
     273            0 :             system_id: oldstate.server.system_id,
     274            0 :             wal_seg_size: oldstate.server.wal_seg_size,
     275            0 :         };
     276            0 :         return Ok(TimelinePersistentState {
     277            0 :             tenant_id: oldstate.server.tenant_id,
     278            0 :             timeline_id: oldstate.server.timeline_id,
     279            0 :             acceptor_state: oldstate.acceptor_state,
     280            0 :             server,
     281            0 :             proposer_uuid: oldstate.proposer_uuid,
     282            0 :             timeline_start_lsn: Lsn(0),
     283            0 :             local_start_lsn: Lsn(0),
     284            0 :             commit_lsn: oldstate.commit_lsn,
     285            0 :             backup_lsn: Lsn(0),
     286            0 :             peer_horizon_lsn: oldstate.truncate_lsn,
     287            0 :             remote_consistent_lsn: Lsn(0),
     288            0 :             peers: PersistedPeers(vec![]),
     289            0 :             partial_backup: wal_backup_partial::State::default(),
     290            0 :             eviction_state: EvictionState::Present,
     291            0 :         });
     292              :     // migrate to moving tenant_id/timeline_id to the top and adding some lsns
     293            2 :     } else if version == 3 {
     294            0 :         info!("reading safekeeper control file version {version}");
     295            0 :         let oldstate = SafeKeeperStateV3::des(&buf[..buf.len()])?;
     296            0 :         let server = ServerInfo {
     297            0 :             pg_version: oldstate.server.pg_version,
     298            0 :             system_id: oldstate.server.system_id,
     299            0 :             wal_seg_size: oldstate.server.wal_seg_size,
     300            0 :         };
     301            0 :         return Ok(TimelinePersistentState {
     302            0 :             tenant_id: oldstate.server.tenant_id,
     303            0 :             timeline_id: oldstate.server.timeline_id,
     304            0 :             acceptor_state: oldstate.acceptor_state,
     305            0 :             server,
     306            0 :             proposer_uuid: oldstate.proposer_uuid,
     307            0 :             timeline_start_lsn: Lsn(0),
     308            0 :             local_start_lsn: Lsn(0),
     309            0 :             commit_lsn: oldstate.commit_lsn,
     310            0 :             backup_lsn: Lsn(0),
     311            0 :             peer_horizon_lsn: oldstate.truncate_lsn,
     312            0 :             remote_consistent_lsn: Lsn(0),
     313            0 :             peers: PersistedPeers(vec![]),
     314            0 :             partial_backup: wal_backup_partial::State::default(),
     315            0 :             eviction_state: EvictionState::Present,
     316            0 :         });
     317              :     // migrate to having timeline_start_lsn
     318            2 :     } else if version == 4 {
     319            0 :         info!("reading safekeeper control file version {}", version);
     320            0 :         let oldstate = SafeKeeperStateV4::des(&buf[..buf.len()])?;
     321            0 :         let server = ServerInfo {
     322            0 :             pg_version: oldstate.server.pg_version,
     323            0 :             system_id: oldstate.server.system_id,
     324            0 :             wal_seg_size: oldstate.server.wal_seg_size,
     325            0 :         };
     326            0 :         return Ok(TimelinePersistentState {
     327            0 :             tenant_id: oldstate.tenant_id,
     328            0 :             timeline_id: oldstate.timeline_id,
     329            0 :             acceptor_state: oldstate.acceptor_state,
     330            0 :             server,
     331            0 :             proposer_uuid: oldstate.proposer_uuid,
     332            0 :             timeline_start_lsn: Lsn(0),
     333            0 :             local_start_lsn: Lsn(0),
     334            0 :             commit_lsn: oldstate.commit_lsn,
     335            0 :             backup_lsn: Lsn::INVALID,
     336            0 :             peer_horizon_lsn: oldstate.peer_horizon_lsn,
     337            0 :             remote_consistent_lsn: Lsn(0),
     338            0 :             peers: PersistedPeers(vec![]),
     339            0 :             partial_backup: wal_backup_partial::State::default(),
     340            0 :             eviction_state: EvictionState::Present,
     341            0 :         });
     342            2 :     } else if version == 5 {
     343            0 :         info!("reading safekeeper control file version {}", version);
     344            0 :         let mut oldstate = TimelinePersistentState::des(&buf[..buf.len()])?;
     345            0 :         if oldstate.timeline_start_lsn != Lsn(0) {
     346            0 :             return Ok(oldstate);
     347            0 :         }
     348            0 : 
     349            0 :         // set special timeline_start_lsn because we don't know the real one
     350            0 :         info!("setting timeline_start_lsn and local_start_lsn to Lsn(1)");
     351            0 :         oldstate.timeline_start_lsn = Lsn(1);
     352            0 :         oldstate.local_start_lsn = Lsn(1);
     353            0 : 
     354            0 :         return Ok(oldstate);
     355            2 :     } else if version == 6 {
     356            0 :         info!("reading safekeeper control file version {}", version);
     357            0 :         let mut oldstate = TimelinePersistentState::des(&buf[..buf.len()])?;
     358            0 :         if oldstate.server.pg_version != 0 {
     359            0 :             return Ok(oldstate);
     360            0 :         }
     361            0 : 
     362            0 :         // set pg_version to the default v14
     363            0 :         info!("setting pg_version to 140005");
     364            0 :         oldstate.server.pg_version = 140005;
     365            0 : 
     366            0 :         return Ok(oldstate);
     367            2 :     } else if version == 7 {
     368            0 :         info!("reading safekeeper control file version {}", version);
     369            0 :         let oldstate = SafeKeeperStateV7::des(&buf[..buf.len()])?;
     370              : 
     371            0 :         return Ok(TimelinePersistentState {
     372            0 :             tenant_id: oldstate.tenant_id,
     373            0 :             timeline_id: oldstate.timeline_id,
     374            0 :             acceptor_state: oldstate.acceptor_state,
     375            0 :             server: oldstate.server,
     376            0 :             proposer_uuid: oldstate.proposer_uuid,
     377            0 :             timeline_start_lsn: oldstate.timeline_start_lsn,
     378            0 :             local_start_lsn: oldstate.local_start_lsn,
     379            0 :             commit_lsn: oldstate.commit_lsn,
     380            0 :             backup_lsn: oldstate.backup_lsn,
     381            0 :             peer_horizon_lsn: oldstate.peer_horizon_lsn,
     382            0 :             remote_consistent_lsn: oldstate.remote_consistent_lsn,
     383            0 :             peers: oldstate.peers,
     384            0 :             partial_backup: wal_backup_partial::State::default(),
     385            0 :             eviction_state: EvictionState::Present,
     386            0 :         });
     387            2 :     } else if version == 8 {
     388            2 :         let oldstate = SafeKeeperStateV8::des(&buf[..buf.len()])?;
     389              : 
     390            2 :         return Ok(TimelinePersistentState {
     391            2 :             tenant_id: oldstate.tenant_id,
     392            2 :             timeline_id: oldstate.timeline_id,
     393            2 :             acceptor_state: oldstate.acceptor_state,
     394            2 :             server: oldstate.server,
     395            2 :             proposer_uuid: oldstate.proposer_uuid,
     396            2 :             timeline_start_lsn: oldstate.timeline_start_lsn,
     397            2 :             local_start_lsn: oldstate.local_start_lsn,
     398            2 :             commit_lsn: oldstate.commit_lsn,
     399            2 :             backup_lsn: oldstate.backup_lsn,
     400            2 :             peer_horizon_lsn: oldstate.peer_horizon_lsn,
     401            2 :             remote_consistent_lsn: oldstate.remote_consistent_lsn,
     402            2 :             peers: oldstate.peers,
     403            2 :             partial_backup: oldstate.partial_backup,
     404            2 :             eviction_state: EvictionState::Present,
     405            2 :         });
     406            0 :     }
     407            0 : 
     408            0 :     // TODO: persist the file back to the disk after upgrade
     409            0 :     // TODO: think about backward compatibility and rollbacks
     410            0 : 
     411            0 :     bail!("unsupported safekeeper control file version {}", version)
     412            2 : }
     413              : 
     414            2 : pub fn downgrade_v9_to_v8(state: &TimelinePersistentState) -> SafeKeeperStateV8 {
     415            2 :     assert!(state.eviction_state == EvictionState::Present);
     416            2 :     SafeKeeperStateV8 {
     417            2 :         tenant_id: state.tenant_id,
     418            2 :         timeline_id: state.timeline_id,
     419            2 :         acceptor_state: state.acceptor_state.clone(),
     420            2 :         server: state.server.clone(),
     421            2 :         proposer_uuid: state.proposer_uuid,
     422            2 :         timeline_start_lsn: state.timeline_start_lsn,
     423            2 :         local_start_lsn: state.local_start_lsn,
     424            2 :         commit_lsn: state.commit_lsn,
     425            2 :         backup_lsn: state.backup_lsn,
     426            2 :         peer_horizon_lsn: state.peer_horizon_lsn,
     427            2 :         remote_consistent_lsn: state.remote_consistent_lsn,
     428            2 :         peers: state.peers.clone(),
     429            2 :         partial_backup: state.partial_backup.clone(),
     430            2 :     }
     431            2 : }
     432              : 
     433              : #[cfg(test)]
     434              : mod tests {
     435              :     use std::str::FromStr;
     436              : 
     437              :     use utils::{id::NodeId, Hex};
     438              : 
     439              :     use crate::safekeeper::PersistedPeerInfo;
     440              : 
     441              :     use super::*;
     442              : 
     443              :     #[test]
     444            1 :     fn roundtrip_v1() {
     445            1 :         let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
     446            1 :         let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
     447            1 :         let state = SafeKeeperStateV1 {
     448            1 :             acceptor_state: AcceptorStateV1 {
     449            1 :                 term: 42,
     450            1 :                 epoch: 43,
     451            1 :             },
     452            1 :             server: ServerInfoV2 {
     453            1 :                 pg_version: 14,
     454            1 :                 system_id: 0x1234567887654321,
     455            1 :                 tenant_id,
     456            1 :                 timeline_id,
     457            1 :                 wal_seg_size: 0x12345678,
     458            1 :             },
     459            1 :             proposer_uuid: {
     460            1 :                 let mut arr = timeline_id.as_arr();
     461            1 :                 arr.reverse();
     462            1 :                 arr
     463            1 :             },
     464            1 :             commit_lsn: Lsn(1234567800),
     465            1 :             truncate_lsn: Lsn(123456780),
     466            1 :             wal_start_lsn: Lsn(1234567800 - 8),
     467            1 :         };
     468            1 : 
     469            1 :         let ser = state.ser().unwrap();
     470            1 :         #[rustfmt::skip]
     471            1 :         let expected = [
     472            1 :             // term
     473            1 :             0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     474            1 :             // epoch
     475            1 :             0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     476            1 :             // pg_version
     477            1 :             0x0e, 0x00, 0x00, 0x00,
     478            1 :             // system_id
     479            1 :             0x21, 0x43, 0x65, 0x87, 0x78, 0x56, 0x34, 0x12,
     480            1 :             // tenant_id
     481            1 :             0xcf, 0x04, 0x80, 0x92, 0x97, 0x07, 0xee, 0x75, 0x37, 0x23, 0x37, 0xef, 0xaa, 0x5e, 0xcf, 0x96,
     482            1 :             // timeline_id
     483            1 :             0x11, 0x2d, 0xed, 0x66, 0x42, 0x2a, 0xa5, 0xe9, 0x53, 0xe5, 0x44, 0x0f, 0xa5, 0x42, 0x7a, 0xc4,
     484            1 :             // wal_seg_size
     485            1 :             0x78, 0x56, 0x34, 0x12,
     486            1 :             // proposer_uuid
     487            1 :             0xc4, 0x7a, 0x42, 0xa5, 0x0f, 0x44, 0xe5, 0x53, 0xe9, 0xa5, 0x2a, 0x42, 0x66, 0xed, 0x2d, 0x11,
     488            1 :             // commit_lsn
     489            1 :             0x78, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
     490            1 :             // truncate_lsn
     491            1 :             0x0c, 0xcd, 0x5b, 0x07, 0x00, 0x00, 0x00, 0x00,
     492            1 :             // wal_start_lsn
     493            1 :             0x70, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
     494            1 :         ];
     495            1 : 
     496            1 :         assert_eq!(Hex(&ser), Hex(&expected));
     497              : 
     498            1 :         let deser = SafeKeeperStateV1::des(&ser).unwrap();
     499            1 : 
     500            1 :         assert_eq!(state, deser);
     501            1 :     }
     502              : 
     503              :     #[test]
     504            1 :     fn roundtrip_v2() {
     505            1 :         let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
     506            1 :         let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
     507            1 :         let state = SafeKeeperStateV2 {
     508            1 :             acceptor_state: AcceptorState {
     509            1 :                 term: 42,
     510            1 :                 term_history: TermHistory(vec![TermLsn {
     511            1 :                     lsn: Lsn(0x1),
     512            1 :                     term: 41,
     513            1 :                 }]),
     514            1 :             },
     515            1 :             server: ServerInfoV2 {
     516            1 :                 pg_version: 14,
     517            1 :                 system_id: 0x1234567887654321,
     518            1 :                 tenant_id,
     519            1 :                 timeline_id,
     520            1 :                 wal_seg_size: 0x12345678,
     521            1 :             },
     522            1 :             proposer_uuid: {
     523            1 :                 let mut arr = timeline_id.as_arr();
     524            1 :                 arr.reverse();
     525            1 :                 arr
     526            1 :             },
     527            1 :             commit_lsn: Lsn(1234567800),
     528            1 :             truncate_lsn: Lsn(123456780),
     529            1 :             wal_start_lsn: Lsn(1234567800 - 8),
     530            1 :         };
     531            1 : 
     532            1 :         let ser = state.ser().unwrap();
     533            1 :         let expected = [
     534            1 :             0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
     535            1 :             0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
     536            1 :             0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x21, 0x43, 0x65, 0x87, 0x78, 0x56,
     537            1 :             0x34, 0x12, 0xcf, 0x04, 0x80, 0x92, 0x97, 0x07, 0xee, 0x75, 0x37, 0x23, 0x37, 0xef,
     538            1 :             0xaa, 0x5e, 0xcf, 0x96, 0x11, 0x2d, 0xed, 0x66, 0x42, 0x2a, 0xa5, 0xe9, 0x53, 0xe5,
     539            1 :             0x44, 0x0f, 0xa5, 0x42, 0x7a, 0xc4, 0x78, 0x56, 0x34, 0x12, 0xc4, 0x7a, 0x42, 0xa5,
     540            1 :             0x0f, 0x44, 0xe5, 0x53, 0xe9, 0xa5, 0x2a, 0x42, 0x66, 0xed, 0x2d, 0x11, 0x78, 0x02,
     541            1 :             0x96, 0x49, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xcd, 0x5b, 0x07, 0x00, 0x00, 0x00, 0x00,
     542            1 :             0x70, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
     543            1 :         ];
     544            1 : 
     545            1 :         assert_eq!(Hex(&ser), Hex(&expected));
     546              : 
     547            1 :         let deser = SafeKeeperStateV2::des(&ser).unwrap();
     548            1 : 
     549            1 :         assert_eq!(state, deser);
     550            1 :     }
     551              : 
     552              :     #[test]
     553            1 :     fn roundtrip_v3() {
     554            1 :         let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
     555            1 :         let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
     556            1 :         let state = SafeKeeperStateV3 {
     557            1 :             acceptor_state: AcceptorState {
     558            1 :                 term: 42,
     559            1 :                 term_history: TermHistory(vec![TermLsn {
     560            1 :                     lsn: Lsn(0x1),
     561            1 :                     term: 41,
     562            1 :                 }]),
     563            1 :             },
     564            1 :             server: ServerInfoV3 {
     565            1 :                 pg_version: 14,
     566            1 :                 system_id: 0x1234567887654321,
     567            1 :                 tenant_id,
     568            1 :                 timeline_id,
     569            1 :                 wal_seg_size: 0x12345678,
     570            1 :             },
     571            1 :             proposer_uuid: {
     572            1 :                 let mut arr = timeline_id.as_arr();
     573            1 :                 arr.reverse();
     574            1 :                 arr
     575            1 :             },
     576            1 :             commit_lsn: Lsn(1234567800),
     577            1 :             truncate_lsn: Lsn(123456780),
     578            1 :             wal_start_lsn: Lsn(1234567800 - 8),
     579            1 :         };
     580            1 : 
     581            1 :         let ser = state.ser().unwrap();
     582            1 :         let expected = [
     583            1 :             0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
     584            1 :             0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
     585            1 :             0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x21, 0x43, 0x65, 0x87, 0x78, 0x56,
     586            1 :             0x34, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x66, 0x30, 0x34,
     587            1 :             0x38, 0x30, 0x39, 0x32, 0x39, 0x37, 0x30, 0x37, 0x65, 0x65, 0x37, 0x35, 0x33, 0x37,
     588            1 :             0x32, 0x33, 0x33, 0x37, 0x65, 0x66, 0x61, 0x61, 0x35, 0x65, 0x63, 0x66, 0x39, 0x36,
     589            1 :             0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31, 0x32, 0x64, 0x65, 0x64,
     590            1 :             0x36, 0x36, 0x34, 0x32, 0x32, 0x61, 0x61, 0x35, 0x65, 0x39, 0x35, 0x33, 0x65, 0x35,
     591            1 :             0x34, 0x34, 0x30, 0x66, 0x61, 0x35, 0x34, 0x32, 0x37, 0x61, 0x63, 0x34, 0x78, 0x56,
     592            1 :             0x34, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x34, 0x37, 0x61,
     593            1 :             0x34, 0x32, 0x61, 0x35, 0x30, 0x66, 0x34, 0x34, 0x65, 0x35, 0x35, 0x33, 0x65, 0x39,
     594            1 :             0x61, 0x35, 0x32, 0x61, 0x34, 0x32, 0x36, 0x36, 0x65, 0x64, 0x32, 0x64, 0x31, 0x31,
     595            1 :             0x78, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xcd, 0x5b, 0x07, 0x00, 0x00,
     596            1 :             0x00, 0x00, 0x70, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
     597            1 :         ];
     598            1 : 
     599            1 :         assert_eq!(Hex(&ser), Hex(&expected));
     600              : 
     601            1 :         let deser = SafeKeeperStateV3::des(&ser).unwrap();
     602            1 : 
     603            1 :         assert_eq!(state, deser);
     604            1 :     }
     605              : 
     606              :     #[test]
     607            1 :     fn roundtrip_v4() {
     608            1 :         let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
     609            1 :         let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
     610            1 :         let state = SafeKeeperStateV4 {
     611            1 :             tenant_id,
     612            1 :             timeline_id,
     613            1 :             acceptor_state: AcceptorState {
     614            1 :                 term: 42,
     615            1 :                 term_history: TermHistory(vec![TermLsn {
     616            1 :                     lsn: Lsn(0x1),
     617            1 :                     term: 41,
     618            1 :                 }]),
     619            1 :             },
     620            1 :             server: ServerInfo {
     621            1 :                 pg_version: 14,
     622            1 :                 system_id: 0x1234567887654321,
     623            1 :                 wal_seg_size: 0x12345678,
     624            1 :             },
     625            1 :             proposer_uuid: {
     626            1 :                 let mut arr = timeline_id.as_arr();
     627            1 :                 arr.reverse();
     628            1 :                 arr
     629            1 :             },
     630            1 :             peers: PersistedPeers(vec![(
     631            1 :                 NodeId(1),
     632            1 :                 PersistedPeerInfo {
     633            1 :                     backup_lsn: Lsn(1234567000),
     634            1 :                     term: 42,
     635            1 :                     flush_lsn: Lsn(1234567800 - 8),
     636            1 :                     commit_lsn: Lsn(1234567600),
     637            1 :                 },
     638            1 :             )]),
     639            1 :             commit_lsn: Lsn(1234567800),
     640            1 :             s3_wal_lsn: Lsn(1234567300),
     641            1 :             peer_horizon_lsn: Lsn(9999999),
     642            1 :             remote_consistent_lsn: Lsn(1234560000),
     643            1 :         };
     644            1 : 
     645            1 :         let ser = state.ser().unwrap();
     646            1 :         let expected = [
     647            1 :             0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x66, 0x30, 0x34, 0x38, 0x30,
     648            1 :             0x39, 0x32, 0x39, 0x37, 0x30, 0x37, 0x65, 0x65, 0x37, 0x35, 0x33, 0x37, 0x32, 0x33,
     649            1 :             0x33, 0x37, 0x65, 0x66, 0x61, 0x61, 0x35, 0x65, 0x63, 0x66, 0x39, 0x36, 0x20, 0x00,
     650            1 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31, 0x32, 0x64, 0x65, 0x64, 0x36, 0x36,
     651            1 :             0x34, 0x32, 0x32, 0x61, 0x61, 0x35, 0x65, 0x39, 0x35, 0x33, 0x65, 0x35, 0x34, 0x34,
     652            1 :             0x30, 0x66, 0x61, 0x35, 0x34, 0x32, 0x37, 0x61, 0x63, 0x34, 0x2a, 0x00, 0x00, 0x00,
     653            1 :             0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00,
     654            1 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     655            1 :             0x0e, 0x00, 0x00, 0x00, 0x21, 0x43, 0x65, 0x87, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56,
     656            1 :             0x34, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x34, 0x37, 0x61,
     657            1 :             0x34, 0x32, 0x61, 0x35, 0x30, 0x66, 0x34, 0x34, 0x65, 0x35, 0x35, 0x33, 0x65, 0x39,
     658            1 :             0x61, 0x35, 0x32, 0x61, 0x34, 0x32, 0x36, 0x36, 0x65, 0x64, 0x32, 0x64, 0x31, 0x31,
     659            1 :             0x78, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x96, 0x49, 0x00, 0x00,
     660            1 :             0x00, 0x00, 0x7f, 0x96, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x95, 0x49,
     661            1 :             0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
     662            1 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0xff, 0x95, 0x49, 0x00, 0x00, 0x00, 0x00,
     663            1 :             0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x02, 0x96, 0x49, 0x00, 0x00,
     664            1 :             0x00, 0x00, 0xb0, 0x01, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
     665            1 :         ];
     666            1 : 
     667            1 :         assert_eq!(Hex(&ser), Hex(&expected));
     668              : 
     669            1 :         let deser = SafeKeeperStateV4::des(&ser).unwrap();
     670            1 : 
     671            1 :         assert_eq!(state, deser);
     672            1 :     }
     673              : }
        

Generated by: LCOV version 2.1-beta