Line data Source code
1 : //! Every image of a certain timeline from [`crate::tenant::Tenant`]
2 : //! has a metadata that needs to be stored persistently.
3 : //!
4 : //! Later, the file gets used in [`remote_timeline_client`] as a part of
5 : //! external storage import and export operations.
6 : //!
7 : //! The module contains all structs and related helper methods related to timeline metadata.
8 : //!
9 : //! [`remote_timeline_client`]: super::remote_timeline_client
10 :
11 : use anyhow::ensure;
12 : use serde::{de::Error, Deserialize, Serialize, Serializer};
13 : use utils::bin_ser::SerializeError;
14 : use utils::{bin_ser::BeSer, id::TimelineId, lsn::Lsn};
15 :
16 : /// Use special format number to enable backward compatibility.
17 : const METADATA_FORMAT_VERSION: u16 = 4;
18 :
19 : /// Previous supported format versions.
20 : const METADATA_OLD_FORMAT_VERSION: u16 = 3;
21 :
22 : /// We assume that a write of up to METADATA_MAX_SIZE bytes is atomic.
23 : ///
24 : /// This is the same assumption that PostgreSQL makes with the control file,
25 : /// see PG_CONTROL_MAX_SAFE_SIZE
26 : const METADATA_MAX_SIZE: usize = 512;
27 :
28 : /// Metadata stored on disk for each timeline
29 : ///
30 : /// The fields correspond to the values we hold in memory, in Timeline.
31 : #[derive(Debug, Clone, PartialEq, Eq)]
32 : pub struct TimelineMetadata {
33 : hdr: TimelineMetadataHeader,
34 : body: TimelineMetadataBodyV2,
35 : }
36 :
37 64 : #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38 : struct TimelineMetadataHeader {
39 : checksum: u32, // CRC of serialized metadata body
40 : size: u16, // size of serialized metadata
41 : format_version: u16, // metadata format version (used for compatibility checks)
42 : }
43 : const METADATA_HDR_SIZE: usize = std::mem::size_of::<TimelineMetadataHeader>();
44 :
45 60 : #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46 : struct TimelineMetadataBodyV2 {
47 : disk_consistent_lsn: Lsn,
48 : // This is only set if we know it. We track it in memory when the page
49 : // server is running, but we only track the value corresponding to
50 : // 'last_record_lsn', not 'disk_consistent_lsn' which can lag behind by a
51 : // lot. We only store it in the metadata file when we flush *all* the
52 : // in-memory data so that 'last_record_lsn' is the same as
53 : // 'disk_consistent_lsn'. That's OK, because after page server restart, as
54 : // soon as we reprocess at least one record, we will have a valid
55 : // 'prev_record_lsn' value in memory again. This is only really needed when
56 : // doing a clean shutdown, so that there is no more WAL beyond
57 : // 'disk_consistent_lsn'
58 : prev_record_lsn: Option<Lsn>,
59 : ancestor_timeline: Option<TimelineId>,
60 : ancestor_lsn: Lsn,
61 : latest_gc_cutoff_lsn: Lsn,
62 : initdb_lsn: Lsn,
63 : pg_version: u32,
64 : }
65 :
66 2 : #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
67 : struct TimelineMetadataBodyV1 {
68 : disk_consistent_lsn: Lsn,
69 : // This is only set if we know it. We track it in memory when the page
70 : // server is running, but we only track the value corresponding to
71 : // 'last_record_lsn', not 'disk_consistent_lsn' which can lag behind by a
72 : // lot. We only store it in the metadata file when we flush *all* the
73 : // in-memory data so that 'last_record_lsn' is the same as
74 : // 'disk_consistent_lsn'. That's OK, because after page server restart, as
75 : // soon as we reprocess at least one record, we will have a valid
76 : // 'prev_record_lsn' value in memory again. This is only really needed when
77 : // doing a clean shutdown, so that there is no more WAL beyond
78 : // 'disk_consistent_lsn'
79 : prev_record_lsn: Option<Lsn>,
80 : ancestor_timeline: Option<TimelineId>,
81 : ancestor_lsn: Lsn,
82 : latest_gc_cutoff_lsn: Lsn,
83 : initdb_lsn: Lsn,
84 : }
85 :
86 : impl TimelineMetadata {
87 350 : pub fn new(
88 350 : disk_consistent_lsn: Lsn,
89 350 : prev_record_lsn: Option<Lsn>,
90 350 : ancestor_timeline: Option<TimelineId>,
91 350 : ancestor_lsn: Lsn,
92 350 : latest_gc_cutoff_lsn: Lsn,
93 350 : initdb_lsn: Lsn,
94 350 : pg_version: u32,
95 350 : ) -> Self {
96 350 : Self {
97 350 : hdr: TimelineMetadataHeader {
98 350 : checksum: 0,
99 350 : size: 0,
100 350 : format_version: METADATA_FORMAT_VERSION,
101 350 : },
102 350 : body: TimelineMetadataBodyV2 {
103 350 : disk_consistent_lsn,
104 350 : prev_record_lsn,
105 350 : ancestor_timeline,
106 350 : ancestor_lsn,
107 350 : latest_gc_cutoff_lsn,
108 350 : initdb_lsn,
109 350 : pg_version,
110 350 : },
111 350 : }
112 350 : }
113 :
114 2 : fn upgrade_timeline_metadata(metadata_bytes: &[u8]) -> anyhow::Result<Self> {
115 2 : let mut hdr = TimelineMetadataHeader::des(&metadata_bytes[0..METADATA_HDR_SIZE])?;
116 :
117 : // backward compatible only up to this version
118 2 : ensure!(
119 2 : hdr.format_version == METADATA_OLD_FORMAT_VERSION,
120 0 : "unsupported metadata format version {}",
121 : hdr.format_version
122 : );
123 :
124 2 : let metadata_size = hdr.size as usize;
125 :
126 2 : let body: TimelineMetadataBodyV1 =
127 2 : TimelineMetadataBodyV1::des(&metadata_bytes[METADATA_HDR_SIZE..metadata_size])?;
128 :
129 2 : let body = TimelineMetadataBodyV2 {
130 2 : disk_consistent_lsn: body.disk_consistent_lsn,
131 2 : prev_record_lsn: body.prev_record_lsn,
132 2 : ancestor_timeline: body.ancestor_timeline,
133 2 : ancestor_lsn: body.ancestor_lsn,
134 2 : latest_gc_cutoff_lsn: body.latest_gc_cutoff_lsn,
135 2 : initdb_lsn: body.initdb_lsn,
136 2 : pg_version: 14, // All timelines created before this version had pg_version 14
137 2 : };
138 2 :
139 2 : hdr.format_version = METADATA_FORMAT_VERSION;
140 2 :
141 2 : Ok(Self { hdr, body })
142 2 : }
143 :
144 62 : pub fn from_bytes(metadata_bytes: &[u8]) -> anyhow::Result<Self> {
145 62 : ensure!(
146 62 : metadata_bytes.len() == METADATA_MAX_SIZE,
147 0 : "metadata bytes size is wrong"
148 : );
149 62 : let hdr = TimelineMetadataHeader::des(&metadata_bytes[0..METADATA_HDR_SIZE])?;
150 :
151 62 : let metadata_size = hdr.size as usize;
152 62 : ensure!(
153 62 : metadata_size <= METADATA_MAX_SIZE,
154 0 : "corrupted metadata file"
155 : );
156 62 : let calculated_checksum = crc32c::crc32c(&metadata_bytes[METADATA_HDR_SIZE..metadata_size]);
157 62 : ensure!(
158 62 : hdr.checksum == calculated_checksum,
159 0 : "metadata checksum mismatch"
160 : );
161 :
162 62 : if hdr.format_version != METADATA_FORMAT_VERSION {
163 : // If metadata has the old format,
164 : // upgrade it and return the result
165 2 : TimelineMetadata::upgrade_timeline_metadata(metadata_bytes)
166 : } else {
167 60 : let body =
168 60 : TimelineMetadataBodyV2::des(&metadata_bytes[METADATA_HDR_SIZE..metadata_size])?;
169 60 : ensure!(
170 60 : body.disk_consistent_lsn.is_aligned(),
171 0 : "disk_consistent_lsn is not aligned"
172 : );
173 60 : Ok(TimelineMetadata { hdr, body })
174 : }
175 62 : }
176 :
177 1188 : pub fn to_bytes(&self) -> Result<Vec<u8>, SerializeError> {
178 1188 : let body_bytes = self.body.ser()?;
179 1188 : let metadata_size = METADATA_HDR_SIZE + body_bytes.len();
180 1188 : let hdr = TimelineMetadataHeader {
181 1188 : size: metadata_size as u16,
182 1188 : format_version: METADATA_FORMAT_VERSION,
183 1188 : checksum: crc32c::crc32c(&body_bytes),
184 1188 : };
185 1188 : let hdr_bytes = hdr.ser()?;
186 1188 : let mut metadata_bytes = vec![0u8; METADATA_MAX_SIZE];
187 1188 : metadata_bytes[0..METADATA_HDR_SIZE].copy_from_slice(&hdr_bytes);
188 1188 : metadata_bytes[METADATA_HDR_SIZE..metadata_size].copy_from_slice(&body_bytes);
189 1188 : Ok(metadata_bytes)
190 1188 : }
191 :
192 : /// [`Lsn`] that corresponds to the corresponding timeline directory
193 : /// contents, stored locally in the pageserver workdir.
194 2722 : pub fn disk_consistent_lsn(&self) -> Lsn {
195 2722 : self.body.disk_consistent_lsn
196 2722 : }
197 :
198 334 : pub fn prev_record_lsn(&self) -> Option<Lsn> {
199 334 : self.body.prev_record_lsn
200 334 : }
201 :
202 346 : pub fn ancestor_timeline(&self) -> Option<TimelineId> {
203 346 : self.body.ancestor_timeline
204 346 : }
205 :
206 334 : pub fn ancestor_lsn(&self) -> Lsn {
207 334 : self.body.ancestor_lsn
208 334 : }
209 :
210 : /// When reparenting, the `ancestor_lsn` does not change.
211 0 : pub fn reparent(&mut self, timeline: &TimelineId) {
212 0 : assert!(self.body.ancestor_timeline.is_some());
213 : // no assertion for redoing this: it's fine, we may have to repeat this multiple times over
214 0 : self.body.ancestor_timeline = Some(*timeline);
215 0 : }
216 :
217 0 : pub fn detach_from_ancestor(&mut self, timeline: &TimelineId, ancestor_lsn: &Lsn) {
218 0 : if let Some(ancestor) = self.body.ancestor_timeline {
219 0 : assert_eq!(ancestor, *timeline);
220 0 : }
221 0 : if self.body.ancestor_lsn != Lsn(0) {
222 0 : assert_eq!(self.body.ancestor_lsn, *ancestor_lsn);
223 0 : }
224 0 : self.body.ancestor_timeline = None;
225 0 : self.body.ancestor_lsn = Lsn(0);
226 0 : }
227 :
228 334 : pub fn latest_gc_cutoff_lsn(&self) -> Lsn {
229 334 : self.body.latest_gc_cutoff_lsn
230 334 : }
231 :
232 334 : pub fn initdb_lsn(&self) -> Lsn {
233 334 : self.body.initdb_lsn
234 334 : }
235 :
236 334 : pub fn pg_version(&self) -> u32 {
237 334 : self.body.pg_version
238 334 : }
239 :
240 : // Checksums make it awkward to build a valid instance by hand. This helper
241 : // provides a TimelineMetadata with a valid checksum in its header.
242 : #[cfg(test)]
243 12 : pub fn example() -> Self {
244 12 : let instance = Self::new(
245 12 : "0/16960E8".parse::<Lsn>().unwrap(),
246 12 : None,
247 12 : None,
248 12 : Lsn::from_hex("00000000").unwrap(),
249 12 : Lsn::from_hex("00000000").unwrap(),
250 12 : Lsn::from_hex("00000000").unwrap(),
251 12 : 0,
252 12 : );
253 12 : let bytes = instance.to_bytes().unwrap();
254 12 : Self::from_bytes(&bytes).unwrap()
255 12 : }
256 :
257 928 : pub(crate) fn apply(&mut self, update: &MetadataUpdate) {
258 928 : self.body.disk_consistent_lsn = update.disk_consistent_lsn;
259 928 : self.body.prev_record_lsn = update.prev_record_lsn;
260 928 : self.body.latest_gc_cutoff_lsn = update.latest_gc_cutoff_lsn;
261 928 : }
262 : }
263 :
264 : impl<'de> Deserialize<'de> for TimelineMetadata {
265 34 : fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
266 34 : where
267 34 : D: serde::Deserializer<'de>,
268 34 : {
269 34 : let bytes = Vec::<u8>::deserialize(deserializer)?;
270 34 : Self::from_bytes(bytes.as_slice()).map_err(|e| D::Error::custom(format!("{e}")))
271 34 : }
272 : }
273 :
274 : impl Serialize for TimelineMetadata {
275 1170 : fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
276 1170 : where
277 1170 : S: Serializer,
278 1170 : {
279 1170 : let bytes = self
280 1170 : .to_bytes()
281 1170 : .map_err(|e| serde::ser::Error::custom(format!("{e}")))?;
282 1170 : bytes.serialize(serializer)
283 1170 : }
284 : }
285 :
286 : /// Parts of the metadata which are regularly modified.
287 : pub(crate) struct MetadataUpdate {
288 : disk_consistent_lsn: Lsn,
289 : prev_record_lsn: Option<Lsn>,
290 : latest_gc_cutoff_lsn: Lsn,
291 : }
292 :
293 : impl MetadataUpdate {
294 928 : pub(crate) fn new(
295 928 : disk_consistent_lsn: Lsn,
296 928 : prev_record_lsn: Option<Lsn>,
297 928 : latest_gc_cutoff_lsn: Lsn,
298 928 : ) -> Self {
299 928 : Self {
300 928 : disk_consistent_lsn,
301 928 : prev_record_lsn,
302 928 : latest_gc_cutoff_lsn,
303 928 : }
304 928 : }
305 : }
306 :
307 : #[cfg(test)]
308 : mod tests {
309 : use super::*;
310 : use crate::tenant::harness::TIMELINE_ID;
311 :
312 : #[test]
313 2 : fn metadata_serializes_correctly() {
314 2 : let original_metadata = TimelineMetadata::new(
315 2 : Lsn(0x200),
316 2 : Some(Lsn(0x100)),
317 2 : Some(TIMELINE_ID),
318 2 : Lsn(0),
319 2 : Lsn(0),
320 2 : Lsn(0),
321 2 : // Any version will do here, so use the default
322 2 : crate::DEFAULT_PG_VERSION,
323 2 : );
324 2 :
325 2 : let metadata_bytes = original_metadata
326 2 : .to_bytes()
327 2 : .expect("Should serialize correct metadata to bytes");
328 2 :
329 2 : let deserialized_metadata = TimelineMetadata::from_bytes(&metadata_bytes)
330 2 : .expect("Should deserialize its own bytes");
331 2 :
332 2 : assert_eq!(
333 : deserialized_metadata.body, original_metadata.body,
334 0 : "Metadata that was serialized to bytes and deserialized back should not change"
335 : );
336 2 : }
337 :
338 : // Generate old version metadata and read it with current code.
339 : // Ensure that it is upgraded correctly
340 : #[test]
341 2 : fn test_metadata_upgrade() {
342 2 : #[derive(Debug, Clone, PartialEq, Eq)]
343 2 : struct TimelineMetadataV1 {
344 2 : hdr: TimelineMetadataHeader,
345 2 : body: TimelineMetadataBodyV1,
346 2 : }
347 2 :
348 2 : let metadata_v1 = TimelineMetadataV1 {
349 2 : hdr: TimelineMetadataHeader {
350 2 : checksum: 0,
351 2 : size: 0,
352 2 : format_version: METADATA_OLD_FORMAT_VERSION,
353 2 : },
354 2 : body: TimelineMetadataBodyV1 {
355 2 : disk_consistent_lsn: Lsn(0x200),
356 2 : prev_record_lsn: Some(Lsn(0x100)),
357 2 : ancestor_timeline: Some(TIMELINE_ID),
358 2 : ancestor_lsn: Lsn(0),
359 2 : latest_gc_cutoff_lsn: Lsn(0),
360 2 : initdb_lsn: Lsn(0),
361 2 : },
362 2 : };
363 2 :
364 2 : impl TimelineMetadataV1 {
365 2 : pub fn to_bytes(&self) -> anyhow::Result<Vec<u8>> {
366 2 : let body_bytes = self.body.ser()?;
367 2 : let metadata_size = METADATA_HDR_SIZE + body_bytes.len();
368 2 : let hdr = TimelineMetadataHeader {
369 2 : size: metadata_size as u16,
370 2 : format_version: METADATA_OLD_FORMAT_VERSION,
371 2 : checksum: crc32c::crc32c(&body_bytes),
372 2 : };
373 2 : let hdr_bytes = hdr.ser()?;
374 2 : let mut metadata_bytes = vec![0u8; METADATA_MAX_SIZE];
375 2 : metadata_bytes[0..METADATA_HDR_SIZE].copy_from_slice(&hdr_bytes);
376 2 : metadata_bytes[METADATA_HDR_SIZE..metadata_size].copy_from_slice(&body_bytes);
377 2 : Ok(metadata_bytes)
378 2 : }
379 2 : }
380 2 :
381 2 : let metadata_bytes = metadata_v1
382 2 : .to_bytes()
383 2 : .expect("Should serialize correct metadata to bytes");
384 2 :
385 2 : // This should deserialize to the latest version format
386 2 : let deserialized_metadata = TimelineMetadata::from_bytes(&metadata_bytes)
387 2 : .expect("Should deserialize its own bytes");
388 2 :
389 2 : let expected_metadata = TimelineMetadata::new(
390 2 : Lsn(0x200),
391 2 : Some(Lsn(0x100)),
392 2 : Some(TIMELINE_ID),
393 2 : Lsn(0),
394 2 : Lsn(0),
395 2 : Lsn(0),
396 2 : 14, // All timelines created before this version had pg_version 14
397 2 : );
398 2 :
399 2 : assert_eq!(
400 : deserialized_metadata.body, expected_metadata.body,
401 0 : "Metadata of the old version {} should be upgraded to the latest version {}",
402 : METADATA_OLD_FORMAT_VERSION, METADATA_FORMAT_VERSION
403 : );
404 2 : }
405 :
406 : #[test]
407 2 : fn test_metadata_bincode_serde() {
408 2 : let original_metadata = TimelineMetadata::new(
409 2 : Lsn(0x200),
410 2 : Some(Lsn(0x100)),
411 2 : Some(TIMELINE_ID),
412 2 : Lsn(0),
413 2 : Lsn(0),
414 2 : Lsn(0),
415 2 : // Any version will do here, so use the default
416 2 : crate::DEFAULT_PG_VERSION,
417 2 : );
418 2 : let metadata_bytes = original_metadata
419 2 : .to_bytes()
420 2 : .expect("Cannot create bytes array from metadata");
421 2 :
422 2 : let metadata_bincode_be_bytes = original_metadata
423 2 : .ser()
424 2 : .expect("Cannot serialize the metadata");
425 2 :
426 2 : // 8 bytes for the length of the vector
427 2 : assert_eq!(metadata_bincode_be_bytes.len(), 8 + metadata_bytes.len());
428 :
429 2 : let expected_bincode_bytes = {
430 2 : let mut temp = vec![];
431 2 : let len_bytes = metadata_bytes.len().to_be_bytes();
432 2 : temp.extend_from_slice(&len_bytes);
433 2 : temp.extend_from_slice(&metadata_bytes);
434 2 : temp
435 2 : };
436 2 : assert_eq!(metadata_bincode_be_bytes, expected_bincode_bytes);
437 :
438 2 : let deserialized_metadata = TimelineMetadata::des(&metadata_bincode_be_bytes).unwrap();
439 2 : // Deserialized metadata has the metadata header, which is different from the serialized one.
440 2 : // Reference: TimelineMetaData::to_bytes()
441 2 : let expected_metadata = {
442 2 : let mut temp_metadata = original_metadata;
443 2 : let body_bytes = temp_metadata
444 2 : .body
445 2 : .ser()
446 2 : .expect("Cannot serialize the metadata body");
447 2 : let metadata_size = METADATA_HDR_SIZE + body_bytes.len();
448 2 : let hdr = TimelineMetadataHeader {
449 2 : size: metadata_size as u16,
450 2 : format_version: METADATA_FORMAT_VERSION,
451 2 : checksum: crc32c::crc32c(&body_bytes),
452 2 : };
453 2 : temp_metadata.hdr = hdr;
454 2 : temp_metadata
455 2 : };
456 2 : assert_eq!(deserialized_metadata, expected_metadata);
457 2 : }
458 :
459 : #[test]
460 2 : fn test_metadata_bincode_serde_ensure_roundtrip() {
461 2 : let original_metadata = TimelineMetadata::new(
462 2 : Lsn(0x200),
463 2 : Some(Lsn(0x100)),
464 2 : Some(TIMELINE_ID),
465 2 : Lsn(0),
466 2 : Lsn(0),
467 2 : Lsn(0),
468 2 : // Any version will do here, so use the default
469 2 : crate::DEFAULT_PG_VERSION,
470 2 : );
471 2 : let expected_bytes = vec![
472 2 : /* bincode length encoding bytes */
473 2 : 0, 0, 0, 0, 0, 0, 2, 0, // 8 bytes for the length of the serialized vector
474 2 : /* TimelineMetadataHeader */
475 2 : 4, 37, 101, 34, 0, 70, 0, 4, // checksum, size, format_version (4 + 2 + 2)
476 2 : /* TimelineMetadataBodyV2 */
477 2 : 0, 0, 0, 0, 0, 0, 2, 0, // disk_consistent_lsn (8 bytes)
478 2 : 1, 0, 0, 0, 0, 0, 0, 1, 0, // prev_record_lsn (9 bytes)
479 2 : 1, 17, 34, 51, 68, 85, 102, 119, 136, 17, 34, 51, 68, 85, 102, 119,
480 2 : 136, // ancestor_timeline (17 bytes)
481 2 : 0, 0, 0, 0, 0, 0, 0, 0, // ancestor_lsn (8 bytes)
482 2 : 0, 0, 0, 0, 0, 0, 0, 0, // latest_gc_cutoff_lsn (8 bytes)
483 2 : 0, 0, 0, 0, 0, 0, 0, 0, // initdb_lsn (8 bytes)
484 2 : 0, 0, 0, 15, // pg_version (4 bytes)
485 2 : /* padding bytes */
486 2 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
487 2 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
488 2 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
489 2 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
490 2 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
491 2 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
492 2 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
493 2 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
494 2 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
495 2 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
496 2 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
497 2 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
498 2 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
499 2 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
500 2 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
501 2 : 0, 0, 0, 0, 0, 0, 0,
502 2 : ];
503 2 : let metadata_ser_bytes = original_metadata.ser().unwrap();
504 2 : assert_eq!(metadata_ser_bytes, expected_bytes);
505 :
506 2 : let expected_metadata = {
507 2 : let mut temp_metadata = original_metadata;
508 2 : let body_bytes = temp_metadata
509 2 : .body
510 2 : .ser()
511 2 : .expect("Cannot serialize the metadata body");
512 2 : let metadata_size = METADATA_HDR_SIZE + body_bytes.len();
513 2 : let hdr = TimelineMetadataHeader {
514 2 : size: metadata_size as u16,
515 2 : format_version: METADATA_FORMAT_VERSION,
516 2 : checksum: crc32c::crc32c(&body_bytes),
517 2 : };
518 2 : temp_metadata.hdr = hdr;
519 2 : temp_metadata
520 2 : };
521 2 : let des_metadata = TimelineMetadata::des(&metadata_ser_bytes).unwrap();
522 2 : assert_eq!(des_metadata, expected_metadata);
523 2 : }
524 : }
|