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