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 : }
|