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