mirror of
https://github.com/neondatabase/neon.git
synced 2026-05-25 09:00:37 +00:00
Have separate routine and http endpoint to create timeline on safekeepers. It is not used yet, i.e. timeline is still created implicitly, but we'll change that once infrastructure for learning which tlis are assigned to which safekeepers will be ready, preventing accidental creation by compute. Changes format of safekeeper control file, allowing to store set of peers. Knowing peers provides a part of foundation for peer recovery (calculating min horizons like truncate_lsn for WAL truncation and commit_lsn for sync-safekeepers replacement) and proper membership change; similarly, we don't yet use it for now. Employing cf file version bump, extracts tenant_id and timeline_id to top level where it is more suitable. Also adds a bunch of LSNs there and rename truncate_lsn to more specific peer_horizon_lsn.
179 lines
6.5 KiB
Rust
179 lines
6.5 KiB
Rust
//! Code to deal with safekeeper control file upgrades
|
|
use crate::safekeeper::{
|
|
AcceptorState, Peers, PgUuid, SafeKeeperState, ServerInfo, Term, TermHistory, TermSwitchEntry,
|
|
};
|
|
use anyhow::{bail, Result};
|
|
use serde::{Deserialize, Serialize};
|
|
use tracing::*;
|
|
use zenith_utils::{
|
|
bin_ser::LeSer,
|
|
lsn::Lsn,
|
|
pq_proto::SystemId,
|
|
zid::{ZTenantId, ZTimelineId},
|
|
};
|
|
|
|
/// Persistent consensus state of the acceptor.
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
struct AcceptorStateV1 {
|
|
/// acceptor's last term it voted for (advanced in 1 phase)
|
|
term: Term,
|
|
/// acceptor's epoch (advanced, i.e. bumped to 'term' when VCL is reached).
|
|
epoch: Term,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
struct SafeKeeperStateV1 {
|
|
/// persistent acceptor state
|
|
acceptor_state: AcceptorStateV1,
|
|
/// information about server
|
|
server: ServerInfoV2,
|
|
/// Unique id of the last *elected* proposer we dealed with. Not needed
|
|
/// for correctness, exists for monitoring purposes.
|
|
proposer_uuid: PgUuid,
|
|
/// part of WAL acknowledged by quorum and available locally
|
|
commit_lsn: Lsn,
|
|
/// minimal LSN which may be needed for recovery of some safekeeper (end_lsn
|
|
/// of last record streamed to everyone)
|
|
truncate_lsn: Lsn,
|
|
// Safekeeper starts receiving WAL from this LSN, zeros before it ought to
|
|
// be skipped during decoding.
|
|
wal_start_lsn: Lsn,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct ServerInfoV2 {
|
|
/// Postgres server version
|
|
pub pg_version: u32,
|
|
pub system_id: SystemId,
|
|
pub tenant_id: ZTenantId,
|
|
/// Zenith timelineid
|
|
pub ztli: ZTimelineId,
|
|
pub wal_seg_size: u32,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SafeKeeperStateV2 {
|
|
/// persistent acceptor state
|
|
pub acceptor_state: AcceptorState,
|
|
/// information about server
|
|
pub server: ServerInfoV2,
|
|
/// Unique id of the last *elected* proposer we dealed with. Not needed
|
|
/// for correctness, exists for monitoring purposes.
|
|
pub proposer_uuid: PgUuid,
|
|
/// part of WAL acknowledged by quorum and available locally
|
|
pub commit_lsn: Lsn,
|
|
/// minimal LSN which may be needed for recovery of some safekeeper (end_lsn
|
|
/// of last record streamed to everyone)
|
|
pub truncate_lsn: Lsn,
|
|
// Safekeeper starts receiving WAL from this LSN, zeros before it ought to
|
|
// be skipped during decoding.
|
|
pub wal_start_lsn: Lsn,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct ServerInfoV3 {
|
|
/// Postgres server version
|
|
pub pg_version: u32,
|
|
pub system_id: SystemId,
|
|
#[serde(with = "hex")]
|
|
pub tenant_id: ZTenantId,
|
|
/// Zenith timelineid
|
|
#[serde(with = "hex")]
|
|
pub timeline_id: ZTimelineId,
|
|
pub wal_seg_size: u32,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SafeKeeperStateV3 {
|
|
/// persistent acceptor state
|
|
pub acceptor_state: AcceptorState,
|
|
/// information about server
|
|
pub server: ServerInfoV3,
|
|
/// Unique id of the last *elected* proposer we dealed with. Not needed
|
|
/// for correctness, exists for monitoring purposes.
|
|
#[serde(with = "hex")]
|
|
pub proposer_uuid: PgUuid,
|
|
/// part of WAL acknowledged by quorum and available locally
|
|
pub commit_lsn: Lsn,
|
|
/// minimal LSN which may be needed for recovery of some safekeeper (end_lsn
|
|
/// of last record streamed to everyone)
|
|
pub truncate_lsn: Lsn,
|
|
// Safekeeper starts receiving WAL from this LSN, zeros before it ought to
|
|
// be skipped during decoding.
|
|
pub wal_start_lsn: Lsn,
|
|
}
|
|
|
|
pub fn upgrade_control_file(buf: &[u8], version: u32) -> Result<SafeKeeperState> {
|
|
// migrate to storing full term history
|
|
if version == 1 {
|
|
info!("reading safekeeper control file version {}", version);
|
|
let oldstate = SafeKeeperStateV1::des(&buf[..buf.len()])?;
|
|
let ac = AcceptorState {
|
|
term: oldstate.acceptor_state.term,
|
|
term_history: TermHistory(vec![TermSwitchEntry {
|
|
term: oldstate.acceptor_state.epoch,
|
|
lsn: Lsn(0),
|
|
}]),
|
|
};
|
|
return Ok(SafeKeeperState {
|
|
tenant_id: oldstate.server.tenant_id,
|
|
timeline_id: oldstate.server.ztli,
|
|
acceptor_state: ac,
|
|
server: ServerInfo {
|
|
pg_version: oldstate.server.pg_version,
|
|
system_id: oldstate.server.system_id,
|
|
wal_seg_size: oldstate.server.wal_seg_size,
|
|
},
|
|
proposer_uuid: oldstate.proposer_uuid,
|
|
commit_lsn: oldstate.commit_lsn,
|
|
s3_wal_lsn: Lsn(0),
|
|
peer_horizon_lsn: oldstate.truncate_lsn,
|
|
remote_consistent_lsn: Lsn(0),
|
|
peers: Peers(vec![]),
|
|
});
|
|
// migrate to hexing some zids
|
|
} else if version == 2 {
|
|
info!("reading safekeeper control file version {}", version);
|
|
let oldstate = SafeKeeperStateV2::des(&buf[..buf.len()])?;
|
|
let server = ServerInfo {
|
|
pg_version: oldstate.server.pg_version,
|
|
system_id: oldstate.server.system_id,
|
|
wal_seg_size: oldstate.server.wal_seg_size,
|
|
};
|
|
return Ok(SafeKeeperState {
|
|
tenant_id: oldstate.server.tenant_id,
|
|
timeline_id: oldstate.server.ztli,
|
|
acceptor_state: oldstate.acceptor_state,
|
|
server,
|
|
proposer_uuid: oldstate.proposer_uuid,
|
|
commit_lsn: oldstate.commit_lsn,
|
|
s3_wal_lsn: Lsn(0),
|
|
peer_horizon_lsn: oldstate.truncate_lsn,
|
|
remote_consistent_lsn: Lsn(0),
|
|
peers: Peers(vec![]),
|
|
});
|
|
// migrate to moving ztenantid/ztli to the top and adding some lsns
|
|
} else if version == 3 {
|
|
info!("reading safekeeper control file version {}", version);
|
|
let oldstate = SafeKeeperStateV3::des(&buf[..buf.len()])?;
|
|
let server = ServerInfo {
|
|
pg_version: oldstate.server.pg_version,
|
|
system_id: oldstate.server.system_id,
|
|
wal_seg_size: oldstate.server.wal_seg_size,
|
|
};
|
|
return Ok(SafeKeeperState {
|
|
tenant_id: oldstate.server.tenant_id,
|
|
timeline_id: oldstate.server.timeline_id,
|
|
acceptor_state: oldstate.acceptor_state,
|
|
server,
|
|
proposer_uuid: oldstate.proposer_uuid,
|
|
commit_lsn: oldstate.commit_lsn,
|
|
s3_wal_lsn: Lsn(0),
|
|
peer_horizon_lsn: oldstate.truncate_lsn,
|
|
remote_consistent_lsn: Lsn(0),
|
|
peers: Peers(vec![]),
|
|
});
|
|
}
|
|
bail!("unsupported safekeeper control file version {}", version)
|
|
}
|