LCOV - code coverage report
Current view: top level - safekeeper/src - control_file_upgrade.rs (source / functions) Coverage Total Hit
Test: 5445d246133daeceb0507e6cc0797ab7c1c70cb8.info Lines: 52.7 % 459 242
Test Date: 2025-03-12 18:05:02 Functions: 21.2 % 85 18

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

Generated by: LCOV version 2.1-beta