LCOV - code coverage report
Current view: top level - safekeeper/src - control_file_upgrade.rs (source / functions) Coverage Total Hit
Test: 1e20c4f2b28aa592527961bb32170ebbd2c9172f.info Lines: 52.4 % 429 225
Test Date: 2025-07-16 12:29:03 Functions: 25.0 % 20 5

            Line data    Source code
       1              : //! Code to deal with safekeeper control file upgrades
       2              : use std::vec;
       3              : 
       4              : use anyhow::{Result, bail};
       5              : use postgres_versioninfo::PgVersionId;
       6              : use pq_proto::SystemId;
       7              : use safekeeper_api::membership::{Configuration, INVALID_GENERATION};
       8              : use safekeeper_api::{ServerInfo, Term};
       9              : use serde::{Deserialize, Serialize};
      10              : use tracing::*;
      11              : use utils::bin_ser::LeSer;
      12              : use utils::id::{NodeId, TenantId, TimelineId};
      13              : use utils::lsn::Lsn;
      14              : 
      15              : use crate::safekeeper::{AcceptorState, PgUuid, TermHistory, TermLsn};
      16              : use crate::state::{EvictionState, TimelinePersistentState};
      17              : use crate::wal_backup_partial;
      18              : 
      19              : /// Persistent consensus state of the acceptor.
      20            0 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
      21              : struct AcceptorStateV1 {
      22              :     /// acceptor's last term it voted for (advanced in 1 phase)
      23              :     term: Term,
      24              :     /// acceptor's epoch (advanced, i.e. bumped to 'term' when VCL is reached).
      25              :     epoch: Term,
      26              : }
      27              : 
      28            0 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
      29              : struct SafeKeeperStateV1 {
      30              :     /// persistent acceptor state
      31              :     acceptor_state: AcceptorStateV1,
      32              :     /// information about server
      33              :     server: ServerInfoV2,
      34              :     /// Unique id of the last *elected* proposer we dealt with. Not needed
      35              :     /// for correctness, exists for monitoring purposes.
      36              :     proposer_uuid: PgUuid,
      37              :     /// part of WAL acknowledged by quorum and available locally
      38              :     commit_lsn: Lsn,
      39              :     /// minimal LSN which may be needed for recovery of some safekeeper (end_lsn
      40              :     /// of last record streamed to everyone)
      41              :     truncate_lsn: Lsn,
      42              :     // Safekeeper starts receiving WAL from this LSN, zeros before it ought to
      43              :     // be skipped during decoding.
      44              :     wal_start_lsn: Lsn,
      45              : }
      46              : 
      47            0 : #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
      48              : pub struct ServerInfoV2 {
      49              :     /// Postgres server version
      50              :     pub pg_version: PgVersionId,
      51              :     pub system_id: SystemId,
      52              :     pub tenant_id: TenantId,
      53              :     pub timeline_id: TimelineId,
      54              :     pub wal_seg_size: u32,
      55              : }
      56              : 
      57            0 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
      58              : pub struct SafeKeeperStateV2 {
      59              :     /// persistent acceptor state
      60              :     pub acceptor_state: AcceptorState,
      61              :     /// information about server
      62              :     pub server: ServerInfoV2,
      63              :     /// Unique id of the last *elected* proposer we dealt with. Not needed
      64              :     /// for correctness, exists for monitoring purposes.
      65              :     pub proposer_uuid: PgUuid,
      66              :     /// part of WAL acknowledged by quorum and available locally
      67              :     pub commit_lsn: Lsn,
      68              :     /// minimal LSN which may be needed for recovery of some safekeeper (end_lsn
      69              :     /// of last record streamed to everyone)
      70              :     pub truncate_lsn: Lsn,
      71              :     // Safekeeper starts receiving WAL from this LSN, zeros before it ought to
      72              :     // be skipped during decoding.
      73              :     pub wal_start_lsn: Lsn,
      74              : }
      75              : 
      76              : #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
      77              : pub struct ServerInfoV3 {
      78              :     /// Postgres server version
      79              :     pub pg_version: PgVersionId,
      80              :     pub system_id: SystemId,
      81              :     #[serde(with = "hex")]
      82              :     pub tenant_id: TenantId,
      83              :     #[serde(with = "hex")]
      84              :     pub timeline_id: TimelineId,
      85              :     pub wal_seg_size: u32,
      86              : }
      87              : 
      88              : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
      89              : pub struct SafeKeeperStateV3 {
      90              :     /// persistent acceptor state
      91              :     pub acceptor_state: AcceptorState,
      92              :     /// information about server
      93              :     pub server: ServerInfoV3,
      94              :     /// Unique id of the last *elected* proposer we dealt with. Not needed
      95              :     /// for correctness, exists for monitoring purposes.
      96              :     #[serde(with = "hex")]
      97              :     pub proposer_uuid: PgUuid,
      98              :     /// part of WAL acknowledged by quorum and available locally
      99              :     pub commit_lsn: Lsn,
     100              :     /// minimal LSN which may be needed for recovery of some safekeeper (end_lsn
     101              :     /// of last record streamed to everyone)
     102              :     pub truncate_lsn: Lsn,
     103              :     // Safekeeper starts receiving WAL from this LSN, zeros before it ought to
     104              :     // be skipped during decoding.
     105              :     pub wal_start_lsn: Lsn,
     106              : }
     107              : 
     108              : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
     109              : pub struct SafeKeeperStateV4 {
     110              :     #[serde(with = "hex")]
     111              :     pub tenant_id: TenantId,
     112              :     #[serde(with = "hex")]
     113              :     pub timeline_id: TimelineId,
     114              :     /// persistent acceptor state
     115              :     pub acceptor_state: AcceptorState,
     116              :     /// information about server
     117              :     pub server: ServerInfo,
     118              :     /// Unique id of the last *elected* proposer we dealt with. Not needed
     119              :     /// for correctness, exists for monitoring purposes.
     120              :     #[serde(with = "hex")]
     121              :     pub proposer_uuid: PgUuid,
     122              :     /// Part of WAL acknowledged by quorum and available locally. Always points
     123              :     /// to record boundary.
     124              :     pub commit_lsn: Lsn,
     125              :     /// First LSN not yet offloaded to s3. Useful to persist to avoid finding
     126              :     /// out offloading progress on boot.
     127              :     pub s3_wal_lsn: Lsn,
     128              :     /// Minimal LSN which may be needed for recovery of some safekeeper (end_lsn
     129              :     /// of last record streamed to everyone). Persisting it helps skipping
     130              :     /// recovery in walproposer, generally we compute it from peers. In
     131              :     /// walproposer proto called 'truncate_lsn'.
     132              :     pub peer_horizon_lsn: Lsn,
     133              :     /// LSN of the oldest known checkpoint made by pageserver and successfully
     134              :     /// pushed to s3. We don't remove WAL beyond it. Persisted only for
     135              :     /// informational purposes, we receive it from pageserver (or broker).
     136              :     pub remote_consistent_lsn: Lsn,
     137              :     // Peers and their state as we remember it. Knowing peers themselves is
     138              :     // fundamental; but state is saved here only for informational purposes and
     139              :     // obviously can be stale. (Currently not saved at all, but let's provision
     140              :     // place to have less file version upgrades).
     141              :     pub peers: PersistedPeers,
     142              : }
     143              : 
     144              : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
     145              : pub struct SafeKeeperStateV7 {
     146              :     #[serde(with = "hex")]
     147              :     pub tenant_id: TenantId,
     148              :     #[serde(with = "hex")]
     149              :     pub timeline_id: TimelineId,
     150              :     /// persistent acceptor state
     151              :     pub acceptor_state: AcceptorState,
     152              :     /// information about server
     153              :     pub server: ServerInfo,
     154              :     /// Unique id of the last *elected* proposer we dealt with. Not needed
     155              :     /// for correctness, exists for monitoring purposes.
     156              :     #[serde(with = "hex")]
     157              :     pub proposer_uuid: PgUuid,
     158              :     /// Since which LSN this timeline generally starts. Safekeeper might have
     159              :     /// joined later.
     160              :     pub timeline_start_lsn: Lsn,
     161              :     /// Since which LSN safekeeper has (had) WAL for this timeline.
     162              :     /// All WAL segments next to one containing local_start_lsn are
     163              :     /// filled with data from the beginning.
     164              :     pub local_start_lsn: Lsn,
     165              :     /// Part of WAL acknowledged by quorum *and available locally*. Always points
     166              :     /// to record boundary.
     167              :     pub commit_lsn: Lsn,
     168              :     /// LSN that points to the end of the last backed up segment. Useful to
     169              :     /// persist to avoid finding out offloading progress on boot.
     170              :     pub backup_lsn: Lsn,
     171              :     /// Minimal LSN which may be needed for recovery of some safekeeper (end_lsn
     172              :     /// of last record streamed to everyone). Persisting it helps skipping
     173              :     /// recovery in walproposer, generally we compute it from peers. In
     174              :     /// walproposer proto called 'truncate_lsn'. Updates are currently drived
     175              :     /// only by walproposer.
     176              :     pub peer_horizon_lsn: Lsn,
     177              :     /// LSN of the oldest known checkpoint made by pageserver and successfully
     178              :     /// pushed to s3. We don't remove WAL beyond it. Persisted only for
     179              :     /// informational purposes, we receive it from pageserver (or broker).
     180              :     pub remote_consistent_lsn: Lsn,
     181              :     // Peers and their state as we remember it. Knowing peers themselves is
     182              :     // fundamental; but state is saved here only for informational purposes and
     183              :     // obviously can be stale. (Currently not saved at all, but let's provision
     184              :     // place to have less file version upgrades).
     185              :     pub peers: PersistedPeers,
     186              : }
     187              : 
     188              : /// Persistent information stored on safekeeper node about timeline.
     189              : /// On disk data is prefixed by magic and format version and followed by checksum.
     190              : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
     191              : pub struct SafeKeeperStateV8 {
     192              :     #[serde(with = "hex")]
     193              :     pub tenant_id: TenantId,
     194              :     #[serde(with = "hex")]
     195              :     pub timeline_id: TimelineId,
     196              :     /// persistent acceptor state
     197              :     pub acceptor_state: AcceptorState,
     198              :     /// information about server
     199              :     pub server: ServerInfo,
     200              :     /// Unique id of the last *elected* proposer we dealt with. Not needed
     201              :     /// for correctness, exists for monitoring purposes.
     202              :     #[serde(with = "hex")]
     203              :     pub proposer_uuid: PgUuid,
     204              :     /// Since which LSN this timeline generally starts. Safekeeper might have
     205              :     /// joined later.
     206              :     pub timeline_start_lsn: Lsn,
     207              :     /// Since which LSN safekeeper has (had) WAL for this timeline.
     208              :     /// All WAL segments next to one containing local_start_lsn are
     209              :     /// filled with data from the beginning.
     210              :     pub local_start_lsn: Lsn,
     211              :     /// Part of WAL acknowledged by quorum *and available locally*. Always points
     212              :     /// to record boundary.
     213              :     pub commit_lsn: Lsn,
     214              :     /// LSN that points to the end of the last backed up segment. Useful to
     215              :     /// persist to avoid finding out offloading progress on boot.
     216              :     pub backup_lsn: Lsn,
     217              :     /// Minimal LSN which may be needed for recovery of some safekeeper (end_lsn
     218              :     /// of last record streamed to everyone). Persisting it helps skipping
     219              :     /// recovery in walproposer, generally we compute it from peers. In
     220              :     /// walproposer proto called 'truncate_lsn'. Updates are currently drived
     221              :     /// only by walproposer.
     222              :     pub peer_horizon_lsn: Lsn,
     223              :     /// LSN of the oldest known checkpoint made by pageserver and successfully
     224              :     /// pushed to s3. We don't remove WAL beyond it. Persisted only for
     225              :     /// informational purposes, we receive it from pageserver (or broker).
     226              :     pub remote_consistent_lsn: Lsn,
     227              :     /// Peers and their state as we remember it. Knowing peers themselves is
     228              :     /// fundamental; but state is saved here only for informational purposes and
     229              :     /// obviously can be stale. (Currently not saved at all, but let's provision
     230              :     /// place to have less file version upgrades).
     231              :     pub peers: PersistedPeers,
     232              :     /// Holds names of partial segments uploaded to remote storage. Used to
     233              :     /// clean up old objects without leaving garbage in remote storage.
     234              :     pub partial_backup: wal_backup_partial::State,
     235              : }
     236              : 
     237            0 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
     238              : pub struct PersistedPeers(pub Vec<(NodeId, PersistedPeerInfo)>);
     239              : 
     240            0 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
     241              : pub struct PersistedPeerInfo {
     242              :     /// LSN up to which safekeeper offloaded WAL to s3.
     243              :     pub backup_lsn: Lsn,
     244              :     /// Term of the last entry.
     245              :     pub term: Term,
     246              :     /// LSN of the last record.
     247              :     pub flush_lsn: Lsn,
     248              :     /// Up to which LSN safekeeper regards its WAL as committed.
     249              :     pub commit_lsn: Lsn,
     250              : }
     251              : 
     252              : impl PersistedPeerInfo {
     253            0 :     pub fn new() -> Self {
     254            0 :         Self {
     255            0 :             backup_lsn: Lsn::INVALID,
     256            0 :             term: safekeeper_api::INITIAL_TERM,
     257            0 :             flush_lsn: Lsn(0),
     258            0 :             commit_lsn: Lsn(0),
     259            0 :         }
     260            0 :     }
     261              : }
     262              : 
     263              : // make clippy happy
     264              : impl Default for PersistedPeerInfo {
     265            0 :     fn default() -> Self {
     266            0 :         Self::new()
     267            0 :     }
     268              : }
     269              : 
     270              : /// Note: SafekeeperStateVn is old name for TimelinePersistentStateVn.
     271              : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
     272              : pub struct TimelinePersistentStateV9 {
     273              :     #[serde(with = "hex")]
     274              :     pub tenant_id: TenantId,
     275              :     #[serde(with = "hex")]
     276              :     pub timeline_id: TimelineId,
     277              :     /// persistent acceptor state
     278              :     pub acceptor_state: AcceptorState,
     279              :     /// information about server
     280              :     pub server: ServerInfo,
     281              :     /// Unique id of the last *elected* proposer we dealt with. Not needed
     282              :     /// for correctness, exists for monitoring purposes.
     283              :     #[serde(with = "hex")]
     284              :     pub proposer_uuid: PgUuid,
     285              :     /// Since which LSN this timeline generally starts. Safekeeper might have
     286              :     /// joined later.
     287              :     pub timeline_start_lsn: Lsn,
     288              :     /// Since which LSN safekeeper has (had) WAL for this timeline.
     289              :     /// All WAL segments next to one containing local_start_lsn are
     290              :     /// filled with data from the beginning.
     291              :     pub local_start_lsn: Lsn,
     292              :     /// Part of WAL acknowledged by quorum *and available locally*. Always points
     293              :     /// to record boundary.
     294              :     pub commit_lsn: Lsn,
     295              :     /// LSN that points to the end of the last backed up segment. Useful to
     296              :     /// persist to avoid finding out offloading progress on boot.
     297              :     pub backup_lsn: Lsn,
     298              :     /// Minimal LSN which may be needed for recovery of some safekeeper (end_lsn
     299              :     /// of last record streamed to everyone). Persisting it helps skipping
     300              :     /// recovery in walproposer, generally we compute it from peers. In
     301              :     /// walproposer proto called 'truncate_lsn'. Updates are currently drived
     302              :     /// only by walproposer.
     303              :     pub peer_horizon_lsn: Lsn,
     304              :     /// LSN of the oldest known checkpoint made by pageserver and successfully
     305              :     /// pushed to s3. We don't remove WAL beyond it. Persisted only for
     306              :     /// informational purposes, we receive it from pageserver (or broker).
     307              :     pub remote_consistent_lsn: Lsn,
     308              :     /// Peers and their state as we remember it. Knowing peers themselves is
     309              :     /// fundamental; but state is saved here only for informational purposes and
     310              :     /// obviously can be stale. (Currently not saved at all, but let's provision
     311              :     /// place to have less file version upgrades).
     312              :     pub peers: PersistedPeers,
     313              :     /// Holds names of partial segments uploaded to remote storage. Used to
     314              :     /// clean up old objects without leaving garbage in remote storage.
     315              :     pub partial_backup: wal_backup_partial::State,
     316              :     /// Eviction state of the timeline. If it's Offloaded, we should download
     317              :     /// WAL files from remote storage to serve the timeline.
     318              :     pub eviction_state: EvictionState,
     319              : }
     320              : 
     321            0 : pub fn upgrade_control_file(buf: &[u8], version: u32) -> Result<TimelinePersistentState> {
     322              :     // migrate to storing full term history
     323            0 :     if version == 1 {
     324            0 :         info!("reading safekeeper control file version {}", version);
     325            0 :         let oldstate = SafeKeeperStateV1::des(&buf[..buf.len()])?;
     326            0 :         let ac = AcceptorState {
     327            0 :             term: oldstate.acceptor_state.term,
     328            0 :             term_history: TermHistory(vec![TermLsn {
     329            0 :                 term: oldstate.acceptor_state.epoch,
     330            0 :                 lsn: Lsn(0),
     331            0 :             }]),
     332            0 :         };
     333            0 :         return Ok(TimelinePersistentState {
     334            0 :             tenant_id: oldstate.server.tenant_id,
     335            0 :             timeline_id: oldstate.server.timeline_id,
     336            0 :             mconf: Configuration::empty(),
     337            0 :             acceptor_state: ac,
     338            0 :             server: ServerInfo {
     339            0 :                 pg_version: oldstate.server.pg_version,
     340            0 :                 system_id: oldstate.server.system_id,
     341            0 :                 wal_seg_size: oldstate.server.wal_seg_size,
     342            0 :             },
     343            0 :             proposer_uuid: oldstate.proposer_uuid,
     344            0 :             timeline_start_lsn: Lsn(0),
     345            0 :             local_start_lsn: Lsn(0),
     346            0 :             commit_lsn: oldstate.commit_lsn,
     347            0 :             backup_lsn: Lsn(0),
     348            0 :             peer_horizon_lsn: oldstate.truncate_lsn,
     349            0 :             remote_consistent_lsn: Lsn(0),
     350            0 :             partial_backup: wal_backup_partial::State::default(),
     351            0 :             eviction_state: EvictionState::Present,
     352            0 :             creation_ts: std::time::SystemTime::UNIX_EPOCH,
     353            0 :         });
     354              :     // migrate to hexing some ids
     355            0 :     } else if version == 2 {
     356            0 :         info!("reading safekeeper control file version {}", version);
     357            0 :         let oldstate = SafeKeeperStateV2::des(&buf[..buf.len()])?;
     358            0 :         let server = ServerInfo {
     359            0 :             pg_version: oldstate.server.pg_version,
     360            0 :             system_id: oldstate.server.system_id,
     361            0 :             wal_seg_size: oldstate.server.wal_seg_size,
     362            0 :         };
     363            0 :         return Ok(TimelinePersistentState {
     364            0 :             tenant_id: oldstate.server.tenant_id,
     365            0 :             timeline_id: oldstate.server.timeline_id,
     366            0 :             mconf: Configuration::empty(),
     367            0 :             acceptor_state: oldstate.acceptor_state,
     368            0 :             server,
     369            0 :             proposer_uuid: oldstate.proposer_uuid,
     370            0 :             timeline_start_lsn: Lsn(0),
     371            0 :             local_start_lsn: Lsn(0),
     372            0 :             commit_lsn: oldstate.commit_lsn,
     373            0 :             backup_lsn: Lsn(0),
     374            0 :             peer_horizon_lsn: oldstate.truncate_lsn,
     375            0 :             remote_consistent_lsn: Lsn(0),
     376            0 :             partial_backup: wal_backup_partial::State::default(),
     377            0 :             eviction_state: EvictionState::Present,
     378            0 :             creation_ts: std::time::SystemTime::UNIX_EPOCH,
     379            0 :         });
     380              :     // migrate to moving tenant_id/timeline_id to the top and adding some lsns
     381            0 :     } else if version == 3 {
     382            0 :         info!("reading safekeeper control file version {version}");
     383            0 :         let oldstate = SafeKeeperStateV3::des(&buf[..buf.len()])?;
     384            0 :         let server = ServerInfo {
     385            0 :             pg_version: oldstate.server.pg_version,
     386            0 :             system_id: oldstate.server.system_id,
     387            0 :             wal_seg_size: oldstate.server.wal_seg_size,
     388            0 :         };
     389            0 :         return Ok(TimelinePersistentState {
     390            0 :             tenant_id: oldstate.server.tenant_id,
     391            0 :             timeline_id: oldstate.server.timeline_id,
     392            0 :             mconf: Configuration::empty(),
     393            0 :             acceptor_state: oldstate.acceptor_state,
     394            0 :             server,
     395            0 :             proposer_uuid: oldstate.proposer_uuid,
     396            0 :             timeline_start_lsn: Lsn(0),
     397            0 :             local_start_lsn: Lsn(0),
     398            0 :             commit_lsn: oldstate.commit_lsn,
     399            0 :             backup_lsn: Lsn(0),
     400            0 :             peer_horizon_lsn: oldstate.truncate_lsn,
     401            0 :             remote_consistent_lsn: Lsn(0),
     402            0 :             partial_backup: wal_backup_partial::State::default(),
     403            0 :             eviction_state: EvictionState::Present,
     404            0 :             creation_ts: std::time::SystemTime::UNIX_EPOCH,
     405            0 :         });
     406              :     // migrate to having timeline_start_lsn
     407            0 :     } else if version == 4 {
     408            0 :         info!("reading safekeeper control file version {}", version);
     409            0 :         let oldstate = SafeKeeperStateV4::des(&buf[..buf.len()])?;
     410            0 :         let server = ServerInfo {
     411            0 :             pg_version: oldstate.server.pg_version,
     412            0 :             system_id: oldstate.server.system_id,
     413            0 :             wal_seg_size: oldstate.server.wal_seg_size,
     414            0 :         };
     415            0 :         return Ok(TimelinePersistentState {
     416            0 :             tenant_id: oldstate.tenant_id,
     417            0 :             timeline_id: oldstate.timeline_id,
     418            0 :             mconf: Configuration::empty(),
     419            0 :             acceptor_state: oldstate.acceptor_state,
     420            0 :             server,
     421            0 :             proposer_uuid: oldstate.proposer_uuid,
     422            0 :             timeline_start_lsn: Lsn(0),
     423            0 :             local_start_lsn: Lsn(0),
     424            0 :             commit_lsn: oldstate.commit_lsn,
     425            0 :             backup_lsn: Lsn::INVALID,
     426            0 :             peer_horizon_lsn: oldstate.peer_horizon_lsn,
     427            0 :             remote_consistent_lsn: Lsn(0),
     428            0 :             partial_backup: wal_backup_partial::State::default(),
     429            0 :             eviction_state: EvictionState::Present,
     430            0 :             creation_ts: std::time::SystemTime::UNIX_EPOCH,
     431            0 :         });
     432            0 :     } else if version == 5 {
     433            0 :         info!("reading safekeeper control file version {}", version);
     434            0 :         let mut oldstate = TimelinePersistentState::des(&buf[..buf.len()])?;
     435            0 :         if oldstate.timeline_start_lsn != Lsn(0) {
     436            0 :             return Ok(oldstate);
     437            0 :         }
     438              : 
     439              :         // set special timeline_start_lsn because we don't know the real one
     440            0 :         info!("setting timeline_start_lsn and local_start_lsn to Lsn(1)");
     441            0 :         oldstate.timeline_start_lsn = Lsn(1);
     442            0 :         oldstate.local_start_lsn = Lsn(1);
     443              : 
     444            0 :         return Ok(oldstate);
     445            0 :     } else if version == 6 {
     446            0 :         info!("reading safekeeper control file version {}", version);
     447            0 :         let mut oldstate = TimelinePersistentState::des(&buf[..buf.len()])?;
     448            0 :         if oldstate.server.pg_version != PgVersionId::UNKNOWN {
     449            0 :             return Ok(oldstate);
     450            0 :         }
     451              : 
     452              :         // set pg_version to the default v14
     453            0 :         info!("setting pg_version to 140005");
     454            0 :         oldstate.server.pg_version = PgVersionId::from_full_pg_version(140005);
     455              : 
     456            0 :         return Ok(oldstate);
     457            0 :     } else if version == 7 {
     458            0 :         info!("reading safekeeper control file version {}", version);
     459            0 :         let oldstate = SafeKeeperStateV7::des(&buf[..buf.len()])?;
     460              : 
     461            0 :         return Ok(TimelinePersistentState {
     462            0 :             tenant_id: oldstate.tenant_id,
     463            0 :             timeline_id: oldstate.timeline_id,
     464            0 :             mconf: Configuration::empty(),
     465            0 :             acceptor_state: oldstate.acceptor_state,
     466            0 :             server: oldstate.server,
     467            0 :             proposer_uuid: oldstate.proposer_uuid,
     468            0 :             timeline_start_lsn: oldstate.timeline_start_lsn,
     469            0 :             local_start_lsn: oldstate.local_start_lsn,
     470            0 :             commit_lsn: oldstate.commit_lsn,
     471            0 :             backup_lsn: oldstate.backup_lsn,
     472            0 :             peer_horizon_lsn: oldstate.peer_horizon_lsn,
     473            0 :             remote_consistent_lsn: oldstate.remote_consistent_lsn,
     474            0 :             partial_backup: wal_backup_partial::State::default(),
     475            0 :             eviction_state: EvictionState::Present,
     476            0 :             creation_ts: std::time::SystemTime::UNIX_EPOCH,
     477            0 :         });
     478            0 :     } else if version == 8 {
     479            0 :         let oldstate = SafeKeeperStateV8::des(&buf[..buf.len()])?;
     480              : 
     481            0 :         return Ok(TimelinePersistentState {
     482            0 :             tenant_id: oldstate.tenant_id,
     483            0 :             timeline_id: oldstate.timeline_id,
     484            0 :             mconf: Configuration::empty(),
     485            0 :             acceptor_state: oldstate.acceptor_state,
     486            0 :             server: oldstate.server,
     487            0 :             proposer_uuid: oldstate.proposer_uuid,
     488            0 :             timeline_start_lsn: oldstate.timeline_start_lsn,
     489            0 :             local_start_lsn: oldstate.local_start_lsn,
     490            0 :             commit_lsn: oldstate.commit_lsn,
     491            0 :             backup_lsn: oldstate.backup_lsn,
     492            0 :             peer_horizon_lsn: oldstate.peer_horizon_lsn,
     493            0 :             remote_consistent_lsn: oldstate.remote_consistent_lsn,
     494            0 :             partial_backup: oldstate.partial_backup,
     495            0 :             eviction_state: EvictionState::Present,
     496            0 :             creation_ts: std::time::SystemTime::UNIX_EPOCH,
     497            0 :         });
     498            0 :     } else if version == 9 {
     499            0 :         let oldstate = TimelinePersistentStateV9::des(&buf[..buf.len()])?;
     500            0 :         return Ok(TimelinePersistentState {
     501            0 :             tenant_id: oldstate.tenant_id,
     502            0 :             timeline_id: oldstate.timeline_id,
     503            0 :             mconf: Configuration::empty(),
     504            0 :             acceptor_state: oldstate.acceptor_state,
     505            0 :             server: oldstate.server,
     506            0 :             proposer_uuid: oldstate.proposer_uuid,
     507            0 :             timeline_start_lsn: oldstate.timeline_start_lsn,
     508            0 :             local_start_lsn: oldstate.local_start_lsn,
     509            0 :             commit_lsn: oldstate.commit_lsn,
     510            0 :             backup_lsn: oldstate.backup_lsn,
     511            0 :             peer_horizon_lsn: oldstate.peer_horizon_lsn,
     512            0 :             remote_consistent_lsn: oldstate.remote_consistent_lsn,
     513            0 :             partial_backup: oldstate.partial_backup,
     514            0 :             eviction_state: oldstate.eviction_state,
     515            0 :             creation_ts: std::time::SystemTime::UNIX_EPOCH,
     516            0 :         });
     517            0 :     }
     518              : 
     519              :     // TODO: persist the file back to the disk after upgrade
     520              :     // TODO: think about backward compatibility and rollbacks
     521              : 
     522            0 :     bail!("unsupported safekeeper control file version {}", version)
     523            0 : }
     524              : 
     525              : // Used as a temp hack to make forward compatibility test work. Should be
     526              : // removed after PR adding v10 is merged.
     527           42 : pub fn downgrade_v10_to_v9(state: &TimelinePersistentState) -> TimelinePersistentStateV9 {
     528           42 :     assert!(state.mconf.generation == INVALID_GENERATION);
     529           42 :     TimelinePersistentStateV9 {
     530           42 :         tenant_id: state.tenant_id,
     531           42 :         timeline_id: state.timeline_id,
     532           42 :         acceptor_state: state.acceptor_state.clone(),
     533           42 :         server: state.server.clone(),
     534           42 :         proposer_uuid: state.proposer_uuid,
     535           42 :         timeline_start_lsn: state.timeline_start_lsn,
     536           42 :         local_start_lsn: state.local_start_lsn,
     537           42 :         commit_lsn: state.commit_lsn,
     538           42 :         backup_lsn: state.backup_lsn,
     539           42 :         peer_horizon_lsn: state.peer_horizon_lsn,
     540           42 :         remote_consistent_lsn: state.remote_consistent_lsn,
     541           42 :         peers: PersistedPeers(vec![]),
     542           42 :         partial_backup: state.partial_backup.clone(),
     543           42 :         eviction_state: state.eviction_state,
     544           42 :     }
     545           42 : }
     546              : 
     547              : #[cfg(test)]
     548              : mod tests {
     549              :     use std::str::FromStr;
     550              : 
     551              :     use postgres_versioninfo::PgMajorVersion;
     552              :     use utils::Hex;
     553              :     use utils::id::NodeId;
     554              : 
     555              :     use super::*;
     556              :     use crate::control_file_upgrade::PersistedPeerInfo;
     557              : 
     558              :     #[test]
     559            1 :     fn roundtrip_v1() {
     560            1 :         let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
     561            1 :         let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
     562            1 :         let state = SafeKeeperStateV1 {
     563            1 :             acceptor_state: AcceptorStateV1 {
     564            1 :                 term: 42,
     565            1 :                 epoch: 43,
     566            1 :             },
     567            1 :             server: ServerInfoV2 {
     568            1 :                 pg_version: PgVersionId::from(PgMajorVersion::PG14),
     569            1 :                 system_id: 0x1234567887654321,
     570            1 :                 tenant_id,
     571            1 :                 timeline_id,
     572            1 :                 wal_seg_size: 0x12345678,
     573            1 :             },
     574            1 :             proposer_uuid: {
     575            1 :                 let mut arr = timeline_id.as_arr();
     576            1 :                 arr.reverse();
     577            1 :                 arr
     578            1 :             },
     579            1 :             commit_lsn: Lsn(1234567800),
     580            1 :             truncate_lsn: Lsn(123456780),
     581            1 :             wal_start_lsn: Lsn(1234567800 - 8),
     582            1 :         };
     583              : 
     584            1 :         let ser = state.ser().unwrap();
     585              :         #[rustfmt::skip]
     586            1 :         let expected = [
     587            1 :             // term
     588            1 :             0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     589            1 :             // epoch
     590            1 :             0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     591            1 :             // pg_version = 140000
     592            1 :             0xE0, 0x22, 0x02, 0x00,
     593            1 :             // system_id
     594            1 :             0x21, 0x43, 0x65, 0x87, 0x78, 0x56, 0x34, 0x12,
     595            1 :             // tenant_id
     596            1 :             0xcf, 0x04, 0x80, 0x92, 0x97, 0x07, 0xee, 0x75, 0x37, 0x23, 0x37, 0xef, 0xaa, 0x5e, 0xcf, 0x96,
     597            1 :             // timeline_id
     598            1 :             0x11, 0x2d, 0xed, 0x66, 0x42, 0x2a, 0xa5, 0xe9, 0x53, 0xe5, 0x44, 0x0f, 0xa5, 0x42, 0x7a, 0xc4,
     599            1 :             // wal_seg_size
     600            1 :             0x78, 0x56, 0x34, 0x12,
     601            1 :             // proposer_uuid
     602            1 :             0xc4, 0x7a, 0x42, 0xa5, 0x0f, 0x44, 0xe5, 0x53, 0xe9, 0xa5, 0x2a, 0x42, 0x66, 0xed, 0x2d, 0x11,
     603            1 :             // commit_lsn
     604            1 :             0x78, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
     605            1 :             // truncate_lsn
     606            1 :             0x0c, 0xcd, 0x5b, 0x07, 0x00, 0x00, 0x00, 0x00,
     607            1 :             // wal_start_lsn
     608            1 :             0x70, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
     609            1 :         ];
     610              : 
     611            1 :         assert_eq!(Hex(&ser), Hex(&expected));
     612              : 
     613            1 :         let deser = SafeKeeperStateV1::des(&ser).unwrap();
     614              : 
     615            1 :         assert_eq!(state, deser);
     616            1 :     }
     617              : 
     618              :     #[test]
     619            1 :     fn roundtrip_v2() {
     620            1 :         let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
     621            1 :         let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
     622            1 :         let state = SafeKeeperStateV2 {
     623            1 :             acceptor_state: AcceptorState {
     624            1 :                 term: 42,
     625            1 :                 term_history: TermHistory(vec![TermLsn {
     626            1 :                     lsn: Lsn(0x1),
     627            1 :                     term: 41,
     628            1 :                 }]),
     629            1 :             },
     630            1 :             server: ServerInfoV2 {
     631            1 :                 pg_version: PgVersionId::from(PgMajorVersion::PG14),
     632            1 :                 system_id: 0x1234567887654321,
     633            1 :                 tenant_id,
     634            1 :                 timeline_id,
     635            1 :                 wal_seg_size: 0x12345678,
     636            1 :             },
     637            1 :             proposer_uuid: {
     638            1 :                 let mut arr = timeline_id.as_arr();
     639            1 :                 arr.reverse();
     640            1 :                 arr
     641            1 :             },
     642            1 :             commit_lsn: Lsn(1234567800),
     643            1 :             truncate_lsn: Lsn(123456780),
     644            1 :             wal_start_lsn: Lsn(1234567800 - 8),
     645            1 :         };
     646              : 
     647            1 :         let ser = state.ser().unwrap();
     648            1 :         let expected = [
     649            1 :             0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
     650            1 :             0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
     651            1 :             0x00, 0x00, 0x00, 0x00, 0xE0, 0x22, 0x02, 0x00, 0x21, 0x43, 0x65, 0x87, 0x78, 0x56,
     652            1 :             0x34, 0x12, 0xcf, 0x04, 0x80, 0x92, 0x97, 0x07, 0xee, 0x75, 0x37, 0x23, 0x37, 0xef,
     653            1 :             0xaa, 0x5e, 0xcf, 0x96, 0x11, 0x2d, 0xed, 0x66, 0x42, 0x2a, 0xa5, 0xe9, 0x53, 0xe5,
     654            1 :             0x44, 0x0f, 0xa5, 0x42, 0x7a, 0xc4, 0x78, 0x56, 0x34, 0x12, 0xc4, 0x7a, 0x42, 0xa5,
     655            1 :             0x0f, 0x44, 0xe5, 0x53, 0xe9, 0xa5, 0x2a, 0x42, 0x66, 0xed, 0x2d, 0x11, 0x78, 0x02,
     656            1 :             0x96, 0x49, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xcd, 0x5b, 0x07, 0x00, 0x00, 0x00, 0x00,
     657            1 :             0x70, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
     658            1 :         ];
     659              : 
     660            1 :         assert_eq!(Hex(&ser), Hex(&expected));
     661              : 
     662            1 :         let deser = SafeKeeperStateV2::des(&ser).unwrap();
     663              : 
     664            1 :         assert_eq!(state, deser);
     665            1 :     }
     666              : 
     667              :     #[test]
     668            1 :     fn roundtrip_v3() {
     669            1 :         let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
     670            1 :         let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
     671            1 :         let state = SafeKeeperStateV3 {
     672            1 :             acceptor_state: AcceptorState {
     673            1 :                 term: 42,
     674            1 :                 term_history: TermHistory(vec![TermLsn {
     675            1 :                     lsn: Lsn(0x1),
     676            1 :                     term: 41,
     677            1 :                 }]),
     678            1 :             },
     679            1 :             server: ServerInfoV3 {
     680            1 :                 pg_version: PgVersionId::from(PgMajorVersion::PG14),
     681            1 :                 system_id: 0x1234567887654321,
     682            1 :                 tenant_id,
     683            1 :                 timeline_id,
     684            1 :                 wal_seg_size: 0x12345678,
     685            1 :             },
     686            1 :             proposer_uuid: {
     687            1 :                 let mut arr = timeline_id.as_arr();
     688            1 :                 arr.reverse();
     689            1 :                 arr
     690            1 :             },
     691            1 :             commit_lsn: Lsn(1234567800),
     692            1 :             truncate_lsn: Lsn(123456780),
     693            1 :             wal_start_lsn: Lsn(1234567800 - 8),
     694            1 :         };
     695              : 
     696            1 :         let ser = state.ser().unwrap();
     697            1 :         let expected = [
     698            1 :             0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
     699            1 :             0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
     700            1 :             0x00, 0x00, 0x00, 0x00, 0xE0, 0x22, 0x02, 0x00, 0x21, 0x43, 0x65, 0x87, 0x78, 0x56,
     701            1 :             0x34, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x66, 0x30, 0x34,
     702            1 :             0x38, 0x30, 0x39, 0x32, 0x39, 0x37, 0x30, 0x37, 0x65, 0x65, 0x37, 0x35, 0x33, 0x37,
     703            1 :             0x32, 0x33, 0x33, 0x37, 0x65, 0x66, 0x61, 0x61, 0x35, 0x65, 0x63, 0x66, 0x39, 0x36,
     704            1 :             0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31, 0x32, 0x64, 0x65, 0x64,
     705            1 :             0x36, 0x36, 0x34, 0x32, 0x32, 0x61, 0x61, 0x35, 0x65, 0x39, 0x35, 0x33, 0x65, 0x35,
     706            1 :             0x34, 0x34, 0x30, 0x66, 0x61, 0x35, 0x34, 0x32, 0x37, 0x61, 0x63, 0x34, 0x78, 0x56,
     707            1 :             0x34, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x34, 0x37, 0x61,
     708            1 :             0x34, 0x32, 0x61, 0x35, 0x30, 0x66, 0x34, 0x34, 0x65, 0x35, 0x35, 0x33, 0x65, 0x39,
     709            1 :             0x61, 0x35, 0x32, 0x61, 0x34, 0x32, 0x36, 0x36, 0x65, 0x64, 0x32, 0x64, 0x31, 0x31,
     710            1 :             0x78, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xcd, 0x5b, 0x07, 0x00, 0x00,
     711            1 :             0x00, 0x00, 0x70, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
     712            1 :         ];
     713              : 
     714            1 :         assert_eq!(Hex(&ser), Hex(&expected));
     715              : 
     716            1 :         let deser = SafeKeeperStateV3::des(&ser).unwrap();
     717              : 
     718            1 :         assert_eq!(state, deser);
     719            1 :     }
     720              : 
     721              :     #[test]
     722            1 :     fn roundtrip_v4() {
     723            1 :         let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
     724            1 :         let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
     725            1 :         let state = SafeKeeperStateV4 {
     726            1 :             tenant_id,
     727            1 :             timeline_id,
     728            1 :             acceptor_state: AcceptorState {
     729            1 :                 term: 42,
     730            1 :                 term_history: TermHistory(vec![TermLsn {
     731            1 :                     lsn: Lsn(0x1),
     732            1 :                     term: 41,
     733            1 :                 }]),
     734            1 :             },
     735            1 :             server: ServerInfo {
     736            1 :                 pg_version: PgVersionId::from(PgMajorVersion::PG14),
     737            1 :                 system_id: 0x1234567887654321,
     738            1 :                 wal_seg_size: 0x12345678,
     739            1 :             },
     740            1 :             proposer_uuid: {
     741            1 :                 let mut arr = timeline_id.as_arr();
     742            1 :                 arr.reverse();
     743            1 :                 arr
     744            1 :             },
     745            1 :             peers: PersistedPeers(vec![(
     746            1 :                 NodeId(1),
     747            1 :                 PersistedPeerInfo {
     748            1 :                     backup_lsn: Lsn(1234567000),
     749            1 :                     term: 42,
     750            1 :                     flush_lsn: Lsn(1234567800 - 8),
     751            1 :                     commit_lsn: Lsn(1234567600),
     752            1 :                 },
     753            1 :             )]),
     754            1 :             commit_lsn: Lsn(1234567800),
     755            1 :             s3_wal_lsn: Lsn(1234567300),
     756            1 :             peer_horizon_lsn: Lsn(9999999),
     757            1 :             remote_consistent_lsn: Lsn(1234560000),
     758            1 :         };
     759              : 
     760            1 :         let ser = state.ser().unwrap();
     761            1 :         let expected = [
     762            1 :             0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x66, 0x30, 0x34, 0x38, 0x30,
     763            1 :             0x39, 0x32, 0x39, 0x37, 0x30, 0x37, 0x65, 0x65, 0x37, 0x35, 0x33, 0x37, 0x32, 0x33,
     764            1 :             0x33, 0x37, 0x65, 0x66, 0x61, 0x61, 0x35, 0x65, 0x63, 0x66, 0x39, 0x36, 0x20, 0x00,
     765            1 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31, 0x32, 0x64, 0x65, 0x64, 0x36, 0x36,
     766            1 :             0x34, 0x32, 0x32, 0x61, 0x61, 0x35, 0x65, 0x39, 0x35, 0x33, 0x65, 0x35, 0x34, 0x34,
     767            1 :             0x30, 0x66, 0x61, 0x35, 0x34, 0x32, 0x37, 0x61, 0x63, 0x34, 0x2a, 0x00, 0x00, 0x00,
     768            1 :             0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00,
     769            1 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     770            1 :             0xE0, 0x22, 0x02, 0x00, 0x21, 0x43, 0x65, 0x87, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56,
     771            1 :             0x34, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x34, 0x37, 0x61,
     772            1 :             0x34, 0x32, 0x61, 0x35, 0x30, 0x66, 0x34, 0x34, 0x65, 0x35, 0x35, 0x33, 0x65, 0x39,
     773            1 :             0x61, 0x35, 0x32, 0x61, 0x34, 0x32, 0x36, 0x36, 0x65, 0x64, 0x32, 0x64, 0x31, 0x31,
     774            1 :             0x78, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x96, 0x49, 0x00, 0x00,
     775            1 :             0x00, 0x00, 0x7f, 0x96, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x95, 0x49,
     776            1 :             0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
     777            1 :             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0xff, 0x95, 0x49, 0x00, 0x00, 0x00, 0x00,
     778            1 :             0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x02, 0x96, 0x49, 0x00, 0x00,
     779            1 :             0x00, 0x00, 0xb0, 0x01, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
     780            1 :         ];
     781              : 
     782            1 :         assert_eq!(Hex(&ser), Hex(&expected));
     783              : 
     784            1 :         let deser = SafeKeeperStateV4::des(&ser).unwrap();
     785              : 
     786            1 :         assert_eq!(state, deser);
     787            1 :     }
     788              : }
        

Generated by: LCOV version 2.1-beta