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