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::{PersistedPeers, TimelinePersistentState},
5 : };
6 : use anyhow::{bail, Result};
7 : use pq_proto::SystemId;
8 : use serde::{Deserialize, Serialize};
9 : use tracing::*;
10 : use utils::{
11 : bin_ser::LeSer,
12 : id::{TenantId, TimelineId},
13 : lsn::Lsn,
14 : };
15 :
16 : /// Persistent consensus state of the acceptor.
17 4 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
18 : struct AcceptorStateV1 {
19 : /// acceptor's last term it voted for (advanced in 1 phase)
20 : term: Term,
21 : /// acceptor's epoch (advanced, i.e. bumped to 'term' when VCL is reached).
22 : epoch: Term,
23 : }
24 :
25 4 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
26 : struct SafeKeeperStateV1 {
27 : /// persistent acceptor state
28 : acceptor_state: AcceptorStateV1,
29 : /// information about server
30 : server: ServerInfoV2,
31 : /// Unique id of the last *elected* proposer we dealt with. Not needed
32 : /// for correctness, exists for monitoring purposes.
33 : proposer_uuid: PgUuid,
34 : /// part of WAL acknowledged by quorum and available locally
35 : commit_lsn: Lsn,
36 : /// minimal LSN which may be needed for recovery of some safekeeper (end_lsn
37 : /// of last record streamed to everyone)
38 : truncate_lsn: Lsn,
39 : // Safekeeper starts receiving WAL from this LSN, zeros before it ought to
40 : // be skipped during decoding.
41 : wal_start_lsn: Lsn,
42 : }
43 :
44 8 : #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45 : pub struct ServerInfoV2 {
46 : /// Postgres server version
47 : pub pg_version: u32,
48 : pub system_id: SystemId,
49 : pub tenant_id: TenantId,
50 : pub timeline_id: TimelineId,
51 : pub wal_seg_size: u32,
52 : }
53 :
54 4 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
55 : pub struct SafeKeeperStateV2 {
56 : /// persistent acceptor state
57 : pub acceptor_state: AcceptorState,
58 : /// information about server
59 : pub server: ServerInfoV2,
60 : /// Unique id of the last *elected* proposer we dealt with. Not needed
61 : /// for correctness, exists for monitoring purposes.
62 : pub proposer_uuid: PgUuid,
63 : /// part of WAL acknowledged by quorum and available locally
64 : pub commit_lsn: Lsn,
65 : /// minimal LSN which may be needed for recovery of some safekeeper (end_lsn
66 : /// of last record streamed to everyone)
67 : pub truncate_lsn: Lsn,
68 : // Safekeeper starts receiving WAL from this LSN, zeros before it ought to
69 : // be skipped during decoding.
70 : pub wal_start_lsn: Lsn,
71 : }
72 :
73 4 : #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
74 : pub struct ServerInfoV3 {
75 : /// Postgres server version
76 : pub pg_version: u32,
77 : pub system_id: SystemId,
78 : #[serde(with = "hex")]
79 : pub tenant_id: TenantId,
80 : #[serde(with = "hex")]
81 : pub timeline_id: TimelineId,
82 : pub wal_seg_size: u32,
83 : }
84 :
85 4 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
86 : pub struct SafeKeeperStateV3 {
87 : /// persistent acceptor state
88 : pub acceptor_state: AcceptorState,
89 : /// information about server
90 : pub server: ServerInfoV3,
91 : /// Unique id of the last *elected* proposer we dealt with. Not needed
92 : /// for correctness, exists for monitoring purposes.
93 : #[serde(with = "hex")]
94 : pub proposer_uuid: PgUuid,
95 : /// part of WAL acknowledged by quorum and available locally
96 : pub commit_lsn: Lsn,
97 : /// minimal LSN which may be needed for recovery of some safekeeper (end_lsn
98 : /// of last record streamed to everyone)
99 : pub truncate_lsn: Lsn,
100 : // Safekeeper starts receiving WAL from this LSN, zeros before it ought to
101 : // be skipped during decoding.
102 : pub wal_start_lsn: Lsn,
103 : }
104 :
105 6 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
106 : pub struct SafeKeeperStateV4 {
107 : #[serde(with = "hex")]
108 : pub tenant_id: TenantId,
109 : #[serde(with = "hex")]
110 : pub timeline_id: TimelineId,
111 : /// persistent acceptor state
112 : pub acceptor_state: AcceptorState,
113 : /// information about server
114 : pub server: ServerInfo,
115 : /// Unique id of the last *elected* proposer we dealt with. Not needed
116 : /// for correctness, exists for monitoring purposes.
117 : #[serde(with = "hex")]
118 : pub proposer_uuid: PgUuid,
119 : /// Part of WAL acknowledged by quorum and available locally. Always points
120 : /// to record boundary.
121 : pub commit_lsn: Lsn,
122 : /// First LSN not yet offloaded to s3. Useful to persist to avoid finding
123 : /// out offloading progress on boot.
124 : pub s3_wal_lsn: Lsn,
125 : /// Minimal LSN which may be needed for recovery of some safekeeper (end_lsn
126 : /// of last record streamed to everyone). Persisting it helps skipping
127 : /// recovery in walproposer, generally we compute it from peers. In
128 : /// walproposer proto called 'truncate_lsn'.
129 : pub peer_horizon_lsn: Lsn,
130 : /// LSN of the oldest known checkpoint made by pageserver and successfully
131 : /// pushed to s3. We don't remove WAL beyond it. Persisted only for
132 : /// informational purposes, we receive it from pageserver (or broker).
133 : pub remote_consistent_lsn: Lsn,
134 : // Peers and their state as we remember it. Knowing peers themselves is
135 : // fundamental; but state is saved here only for informational purposes and
136 : // obviously can be stale. (Currently not saved at all, but let's provision
137 : // place to have less file version upgrades).
138 : pub peers: PersistedPeers,
139 : }
140 :
141 0 : pub fn upgrade_control_file(buf: &[u8], version: u32) -> Result<TimelinePersistentState> {
142 0 : // migrate to storing full term history
143 0 : if version == 1 {
144 0 : info!("reading safekeeper control file version {}", version);
145 0 : let oldstate = SafeKeeperStateV1::des(&buf[..buf.len()])?;
146 0 : let ac = AcceptorState {
147 0 : term: oldstate.acceptor_state.term,
148 0 : term_history: TermHistory(vec![TermLsn {
149 0 : term: oldstate.acceptor_state.epoch,
150 0 : lsn: Lsn(0),
151 0 : }]),
152 0 : };
153 0 : return Ok(TimelinePersistentState {
154 0 : tenant_id: oldstate.server.tenant_id,
155 0 : timeline_id: oldstate.server.timeline_id,
156 0 : acceptor_state: ac,
157 0 : server: ServerInfo {
158 0 : pg_version: oldstate.server.pg_version,
159 0 : system_id: oldstate.server.system_id,
160 0 : wal_seg_size: oldstate.server.wal_seg_size,
161 0 : },
162 0 : proposer_uuid: oldstate.proposer_uuid,
163 0 : timeline_start_lsn: Lsn(0),
164 0 : local_start_lsn: Lsn(0),
165 0 : commit_lsn: oldstate.commit_lsn,
166 0 : backup_lsn: Lsn(0),
167 0 : peer_horizon_lsn: oldstate.truncate_lsn,
168 0 : remote_consistent_lsn: Lsn(0),
169 0 : peers: PersistedPeers(vec![]),
170 0 : });
171 : // migrate to hexing some ids
172 0 : } else if version == 2 {
173 0 : info!("reading safekeeper control file version {}", version);
174 0 : let oldstate = SafeKeeperStateV2::des(&buf[..buf.len()])?;
175 0 : let server = ServerInfo {
176 0 : pg_version: oldstate.server.pg_version,
177 0 : system_id: oldstate.server.system_id,
178 0 : wal_seg_size: oldstate.server.wal_seg_size,
179 0 : };
180 0 : return Ok(TimelinePersistentState {
181 0 : tenant_id: oldstate.server.tenant_id,
182 0 : timeline_id: oldstate.server.timeline_id,
183 0 : acceptor_state: oldstate.acceptor_state,
184 0 : server,
185 0 : proposer_uuid: oldstate.proposer_uuid,
186 0 : timeline_start_lsn: Lsn(0),
187 0 : local_start_lsn: Lsn(0),
188 0 : commit_lsn: oldstate.commit_lsn,
189 0 : backup_lsn: Lsn(0),
190 0 : peer_horizon_lsn: oldstate.truncate_lsn,
191 0 : remote_consistent_lsn: Lsn(0),
192 0 : peers: PersistedPeers(vec![]),
193 0 : });
194 : // migrate to moving tenant_id/timeline_id to the top and adding some lsns
195 0 : } else if version == 3 {
196 0 : info!("reading safekeeper control file version {version}");
197 0 : let oldstate = SafeKeeperStateV3::des(&buf[..buf.len()])?;
198 0 : let server = ServerInfo {
199 0 : pg_version: oldstate.server.pg_version,
200 0 : system_id: oldstate.server.system_id,
201 0 : wal_seg_size: oldstate.server.wal_seg_size,
202 0 : };
203 0 : return Ok(TimelinePersistentState {
204 0 : tenant_id: oldstate.server.tenant_id,
205 0 : timeline_id: oldstate.server.timeline_id,
206 0 : acceptor_state: oldstate.acceptor_state,
207 0 : server,
208 0 : proposer_uuid: oldstate.proposer_uuid,
209 0 : timeline_start_lsn: Lsn(0),
210 0 : local_start_lsn: Lsn(0),
211 0 : commit_lsn: oldstate.commit_lsn,
212 0 : backup_lsn: Lsn(0),
213 0 : peer_horizon_lsn: oldstate.truncate_lsn,
214 0 : remote_consistent_lsn: Lsn(0),
215 0 : peers: PersistedPeers(vec![]),
216 0 : });
217 : // migrate to having timeline_start_lsn
218 0 : } else if version == 4 {
219 0 : info!("reading safekeeper control file version {}", version);
220 0 : let oldstate = SafeKeeperStateV4::des(&buf[..buf.len()])?;
221 0 : let server = ServerInfo {
222 0 : pg_version: oldstate.server.pg_version,
223 0 : system_id: oldstate.server.system_id,
224 0 : wal_seg_size: oldstate.server.wal_seg_size,
225 0 : };
226 0 : return Ok(TimelinePersistentState {
227 0 : tenant_id: oldstate.tenant_id,
228 0 : timeline_id: oldstate.timeline_id,
229 0 : acceptor_state: oldstate.acceptor_state,
230 0 : server,
231 0 : proposer_uuid: oldstate.proposer_uuid,
232 0 : timeline_start_lsn: Lsn(0),
233 0 : local_start_lsn: Lsn(0),
234 0 : commit_lsn: oldstate.commit_lsn,
235 0 : backup_lsn: Lsn::INVALID,
236 0 : peer_horizon_lsn: oldstate.peer_horizon_lsn,
237 0 : remote_consistent_lsn: Lsn(0),
238 0 : peers: PersistedPeers(vec![]),
239 0 : });
240 0 : } else if version == 5 {
241 0 : info!("reading safekeeper control file version {}", version);
242 0 : let mut oldstate = TimelinePersistentState::des(&buf[..buf.len()])?;
243 0 : if oldstate.timeline_start_lsn != Lsn(0) {
244 0 : return Ok(oldstate);
245 0 : }
246 0 :
247 0 : // set special timeline_start_lsn because we don't know the real one
248 0 : info!("setting timeline_start_lsn and local_start_lsn to Lsn(1)");
249 0 : oldstate.timeline_start_lsn = Lsn(1);
250 0 : oldstate.local_start_lsn = Lsn(1);
251 0 :
252 0 : return Ok(oldstate);
253 0 : } else if version == 6 {
254 0 : info!("reading safekeeper control file version {}", version);
255 0 : let mut oldstate = TimelinePersistentState::des(&buf[..buf.len()])?;
256 0 : if oldstate.server.pg_version != 0 {
257 0 : return Ok(oldstate);
258 0 : }
259 0 :
260 0 : // set pg_version to the default v14
261 0 : info!("setting pg_version to 140005");
262 0 : oldstate.server.pg_version = 140005;
263 0 :
264 0 : return Ok(oldstate);
265 0 : }
266 0 : bail!("unsupported safekeeper control file version {}", version)
267 0 : }
268 :
269 : #[cfg(test)]
270 : mod tests {
271 : use std::str::FromStr;
272 :
273 : use utils::{id::NodeId, Hex};
274 :
275 : use crate::safekeeper::PersistedPeerInfo;
276 :
277 : use super::*;
278 :
279 2 : #[test]
280 2 : fn roundtrip_v1() {
281 2 : let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
282 2 : let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
283 2 : let state = SafeKeeperStateV1 {
284 2 : acceptor_state: AcceptorStateV1 {
285 2 : term: 42,
286 2 : epoch: 43,
287 2 : },
288 2 : server: ServerInfoV2 {
289 2 : pg_version: 14,
290 2 : system_id: 0x1234567887654321,
291 2 : tenant_id,
292 2 : timeline_id,
293 2 : wal_seg_size: 0x12345678,
294 2 : },
295 2 : proposer_uuid: {
296 2 : let mut arr = timeline_id.as_arr();
297 2 : arr.reverse();
298 2 : arr
299 2 : },
300 2 : commit_lsn: Lsn(1234567800),
301 2 : truncate_lsn: Lsn(123456780),
302 2 : wal_start_lsn: Lsn(1234567800 - 8),
303 2 : };
304 2 :
305 2 : let ser = state.ser().unwrap();
306 2 : #[rustfmt::skip]
307 2 : let expected = [
308 2 : // term
309 2 : 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
310 2 : // epoch
311 2 : 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
312 2 : // pg_version
313 2 : 0x0e, 0x00, 0x00, 0x00,
314 2 : // system_id
315 2 : 0x21, 0x43, 0x65, 0x87, 0x78, 0x56, 0x34, 0x12,
316 2 : // tenant_id
317 2 : 0xcf, 0x04, 0x80, 0x92, 0x97, 0x07, 0xee, 0x75, 0x37, 0x23, 0x37, 0xef, 0xaa, 0x5e, 0xcf, 0x96,
318 2 : // timeline_id
319 2 : 0x11, 0x2d, 0xed, 0x66, 0x42, 0x2a, 0xa5, 0xe9, 0x53, 0xe5, 0x44, 0x0f, 0xa5, 0x42, 0x7a, 0xc4,
320 2 : // wal_seg_size
321 2 : 0x78, 0x56, 0x34, 0x12,
322 2 : // proposer_uuid
323 2 : 0xc4, 0x7a, 0x42, 0xa5, 0x0f, 0x44, 0xe5, 0x53, 0xe9, 0xa5, 0x2a, 0x42, 0x66, 0xed, 0x2d, 0x11,
324 2 : // commit_lsn
325 2 : 0x78, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
326 2 : // truncate_lsn
327 2 : 0x0c, 0xcd, 0x5b, 0x07, 0x00, 0x00, 0x00, 0x00,
328 2 : // wal_start_lsn
329 2 : 0x70, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
330 2 : ];
331 2 :
332 2 : assert_eq!(Hex(&ser), Hex(&expected));
333 :
334 2 : let deser = SafeKeeperStateV1::des(&ser).unwrap();
335 2 :
336 2 : assert_eq!(state, deser);
337 2 : }
338 :
339 2 : #[test]
340 2 : fn roundtrip_v2() {
341 2 : let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
342 2 : let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
343 2 : let state = SafeKeeperStateV2 {
344 2 : acceptor_state: AcceptorState {
345 2 : term: 42,
346 2 : term_history: TermHistory(vec![TermLsn {
347 2 : lsn: Lsn(0x1),
348 2 : term: 41,
349 2 : }]),
350 2 : },
351 2 : server: ServerInfoV2 {
352 2 : pg_version: 14,
353 2 : system_id: 0x1234567887654321,
354 2 : tenant_id,
355 2 : timeline_id,
356 2 : wal_seg_size: 0x12345678,
357 2 : },
358 2 : proposer_uuid: {
359 2 : let mut arr = timeline_id.as_arr();
360 2 : arr.reverse();
361 2 : arr
362 2 : },
363 2 : commit_lsn: Lsn(1234567800),
364 2 : truncate_lsn: Lsn(123456780),
365 2 : wal_start_lsn: Lsn(1234567800 - 8),
366 2 : };
367 2 :
368 2 : let ser = state.ser().unwrap();
369 2 : let expected = [
370 2 : 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
371 2 : 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
372 2 : 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x21, 0x43, 0x65, 0x87, 0x78, 0x56,
373 2 : 0x34, 0x12, 0xcf, 0x04, 0x80, 0x92, 0x97, 0x07, 0xee, 0x75, 0x37, 0x23, 0x37, 0xef,
374 2 : 0xaa, 0x5e, 0xcf, 0x96, 0x11, 0x2d, 0xed, 0x66, 0x42, 0x2a, 0xa5, 0xe9, 0x53, 0xe5,
375 2 : 0x44, 0x0f, 0xa5, 0x42, 0x7a, 0xc4, 0x78, 0x56, 0x34, 0x12, 0xc4, 0x7a, 0x42, 0xa5,
376 2 : 0x0f, 0x44, 0xe5, 0x53, 0xe9, 0xa5, 0x2a, 0x42, 0x66, 0xed, 0x2d, 0x11, 0x78, 0x02,
377 2 : 0x96, 0x49, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xcd, 0x5b, 0x07, 0x00, 0x00, 0x00, 0x00,
378 2 : 0x70, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
379 2 : ];
380 2 :
381 2 : assert_eq!(Hex(&ser), Hex(&expected));
382 :
383 2 : let deser = SafeKeeperStateV2::des(&ser).unwrap();
384 2 :
385 2 : assert_eq!(state, deser);
386 2 : }
387 :
388 2 : #[test]
389 2 : fn roundtrip_v3() {
390 2 : let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
391 2 : let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
392 2 : let state = SafeKeeperStateV3 {
393 2 : acceptor_state: AcceptorState {
394 2 : term: 42,
395 2 : term_history: TermHistory(vec![TermLsn {
396 2 : lsn: Lsn(0x1),
397 2 : term: 41,
398 2 : }]),
399 2 : },
400 2 : server: ServerInfoV3 {
401 2 : pg_version: 14,
402 2 : system_id: 0x1234567887654321,
403 2 : tenant_id,
404 2 : timeline_id,
405 2 : wal_seg_size: 0x12345678,
406 2 : },
407 2 : proposer_uuid: {
408 2 : let mut arr = timeline_id.as_arr();
409 2 : arr.reverse();
410 2 : arr
411 2 : },
412 2 : commit_lsn: Lsn(1234567800),
413 2 : truncate_lsn: Lsn(123456780),
414 2 : wal_start_lsn: Lsn(1234567800 - 8),
415 2 : };
416 2 :
417 2 : let ser = state.ser().unwrap();
418 2 : let expected = [
419 2 : 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
420 2 : 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
421 2 : 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x21, 0x43, 0x65, 0x87, 0x78, 0x56,
422 2 : 0x34, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x66, 0x30, 0x34,
423 2 : 0x38, 0x30, 0x39, 0x32, 0x39, 0x37, 0x30, 0x37, 0x65, 0x65, 0x37, 0x35, 0x33, 0x37,
424 2 : 0x32, 0x33, 0x33, 0x37, 0x65, 0x66, 0x61, 0x61, 0x35, 0x65, 0x63, 0x66, 0x39, 0x36,
425 2 : 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31, 0x32, 0x64, 0x65, 0x64,
426 2 : 0x36, 0x36, 0x34, 0x32, 0x32, 0x61, 0x61, 0x35, 0x65, 0x39, 0x35, 0x33, 0x65, 0x35,
427 2 : 0x34, 0x34, 0x30, 0x66, 0x61, 0x35, 0x34, 0x32, 0x37, 0x61, 0x63, 0x34, 0x78, 0x56,
428 2 : 0x34, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x34, 0x37, 0x61,
429 2 : 0x34, 0x32, 0x61, 0x35, 0x30, 0x66, 0x34, 0x34, 0x65, 0x35, 0x35, 0x33, 0x65, 0x39,
430 2 : 0x61, 0x35, 0x32, 0x61, 0x34, 0x32, 0x36, 0x36, 0x65, 0x64, 0x32, 0x64, 0x31, 0x31,
431 2 : 0x78, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xcd, 0x5b, 0x07, 0x00, 0x00,
432 2 : 0x00, 0x00, 0x70, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
433 2 : ];
434 2 :
435 2 : assert_eq!(Hex(&ser), Hex(&expected));
436 :
437 2 : let deser = SafeKeeperStateV3::des(&ser).unwrap();
438 2 :
439 2 : assert_eq!(state, deser);
440 2 : }
441 :
442 2 : #[test]
443 2 : fn roundtrip_v4() {
444 2 : let tenant_id = TenantId::from_str("cf0480929707ee75372337efaa5ecf96").unwrap();
445 2 : let timeline_id = TimelineId::from_str("112ded66422aa5e953e5440fa5427ac4").unwrap();
446 2 : let state = SafeKeeperStateV4 {
447 2 : tenant_id,
448 2 : timeline_id,
449 2 : acceptor_state: AcceptorState {
450 2 : term: 42,
451 2 : term_history: TermHistory(vec![TermLsn {
452 2 : lsn: Lsn(0x1),
453 2 : term: 41,
454 2 : }]),
455 2 : },
456 2 : server: ServerInfo {
457 2 : pg_version: 14,
458 2 : system_id: 0x1234567887654321,
459 2 : wal_seg_size: 0x12345678,
460 2 : },
461 2 : proposer_uuid: {
462 2 : let mut arr = timeline_id.as_arr();
463 2 : arr.reverse();
464 2 : arr
465 2 : },
466 2 : peers: PersistedPeers(vec![(
467 2 : NodeId(1),
468 2 : PersistedPeerInfo {
469 2 : backup_lsn: Lsn(1234567000),
470 2 : term: 42,
471 2 : flush_lsn: Lsn(1234567800 - 8),
472 2 : commit_lsn: Lsn(1234567600),
473 2 : },
474 2 : )]),
475 2 : commit_lsn: Lsn(1234567800),
476 2 : s3_wal_lsn: Lsn(1234567300),
477 2 : peer_horizon_lsn: Lsn(9999999),
478 2 : remote_consistent_lsn: Lsn(1234560000),
479 2 : };
480 2 :
481 2 : let ser = state.ser().unwrap();
482 2 : let expected = [
483 2 : 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x66, 0x30, 0x34, 0x38, 0x30,
484 2 : 0x39, 0x32, 0x39, 0x37, 0x30, 0x37, 0x65, 0x65, 0x37, 0x35, 0x33, 0x37, 0x32, 0x33,
485 2 : 0x33, 0x37, 0x65, 0x66, 0x61, 0x61, 0x35, 0x65, 0x63, 0x66, 0x39, 0x36, 0x20, 0x00,
486 2 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31, 0x32, 0x64, 0x65, 0x64, 0x36, 0x36,
487 2 : 0x34, 0x32, 0x32, 0x61, 0x61, 0x35, 0x65, 0x39, 0x35, 0x33, 0x65, 0x35, 0x34, 0x34,
488 2 : 0x30, 0x66, 0x61, 0x35, 0x34, 0x32, 0x37, 0x61, 0x63, 0x34, 0x2a, 0x00, 0x00, 0x00,
489 2 : 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00,
490 2 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 2 : 0x0e, 0x00, 0x00, 0x00, 0x21, 0x43, 0x65, 0x87, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56,
492 2 : 0x34, 0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x34, 0x37, 0x61,
493 2 : 0x34, 0x32, 0x61, 0x35, 0x30, 0x66, 0x34, 0x34, 0x65, 0x35, 0x35, 0x33, 0x65, 0x39,
494 2 : 0x61, 0x35, 0x32, 0x61, 0x34, 0x32, 0x36, 0x36, 0x65, 0x64, 0x32, 0x64, 0x31, 0x31,
495 2 : 0x78, 0x02, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x96, 0x49, 0x00, 0x00,
496 2 : 0x00, 0x00, 0x7f, 0x96, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x95, 0x49,
497 2 : 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
498 2 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0xff, 0x95, 0x49, 0x00, 0x00, 0x00, 0x00,
499 2 : 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x02, 0x96, 0x49, 0x00, 0x00,
500 2 : 0x00, 0x00, 0xb0, 0x01, 0x96, 0x49, 0x00, 0x00, 0x00, 0x00,
501 2 : ];
502 2 :
503 2 : assert_eq!(Hex(&ser), Hex(&expected));
504 :
505 2 : let deser = SafeKeeperStateV4::des(&ser).unwrap();
506 2 :
507 2 : assert_eq!(state, deser);
508 2 : }
509 : }
|