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