From c64161b220b32a6e635212d7a82de78fbeb0d860 Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Tue, 5 Mar 2024 12:08:26 +0000 Subject: [PATCH 1/2] pageserver: fix AUX key vectored get validation The value reconstruct of AUX_FILES_KEY from records is not deterministic since it uses a hash map under the hood. Hence, deserialise both results before comparing. All other keys should reconstruct deterministically, so we simply compare the blobs. --- pageserver/src/pgdatadir_mapping.rs | 2 +- pageserver/src/tenant/timeline.rs | 41 +++++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/pageserver/src/pgdatadir_mapping.rs b/pageserver/src/pgdatadir_mapping.rs index 7be08f86b19e..628aeb5a281c 100644 --- a/pageserver/src/pgdatadir_mapping.rs +++ b/pageserver/src/pgdatadir_mapping.rs @@ -1677,7 +1677,7 @@ struct RelDirectory { rels: HashSet<(Oid, u8)>, } -#[derive(Debug, Serialize, Deserialize, Default)] +#[derive(Debug, Serialize, Deserialize, Default, PartialEq)] pub(crate) struct AuxFilesDirectory { pub(crate) files: HashMap, } diff --git a/pageserver/src/tenant/timeline.rs b/pageserver/src/tenant/timeline.rs index 1f811155f672..6a49ef6c7b72 100644 --- a/pageserver/src/tenant/timeline.rs +++ b/pageserver/src/tenant/timeline.rs @@ -17,6 +17,7 @@ use futures::stream::StreamExt; use itertools::Itertools; use once_cell::sync::Lazy; use pageserver_api::{ + key::AUX_FILES_KEY, keyspace::KeySpaceAccum, models::{ CompactionAlgorithm, DownloadRemoteLayersTaskInfo, DownloadRemoteLayersTaskSpawnRequest, @@ -891,8 +892,7 @@ impl Timeline { assert_eq!(seq_key, vec_key); match (seq_res, vec_res) { (Ok(seq_blob), Ok(vec_blob)) => { - assert_eq!(seq_blob, vec_blob, - "Image mismatch for key {seq_key} - keyspace={keyspace:?} lsn={lsn}"); + Self::validate_key_equivalence(seq_key, &keyspace, lsn, seq_blob, vec_blob); }, (Err(err), Ok(_)) => { panic!( @@ -911,6 +911,43 @@ impl Timeline { } } + fn validate_key_equivalence( + key: &Key, + keyspace: &KeySpace, + lsn: Lsn, + seq: &Bytes, + vec: &Bytes, + ) { + use utils::bin_ser::BeSer; + + if *key == AUX_FILES_KEY { + // The value reconstruct of AUX_FILES_KEY from records is not deterministic + // since it uses a hash map under the hood. Hence, deserialise both results + // before comparing. + let seq_aux_dir_res = AuxFilesDirectory::des(seq); + let vec_aux_dir_res = AuxFilesDirectory::des(vec); + match (&seq_aux_dir_res, &vec_aux_dir_res) { + (Ok(seq_aux_dir), Ok(vec_aux_dir)) => { + assert_eq!( + seq_aux_dir, vec_aux_dir, + "Mismatch for key {} - keyspace={:?} lsn={}: {:?} != {:?}", + key, keyspace, lsn, seq_aux_dir, vec_aux_dir + ); + } + (Err(_), Err(_)) => {} + _ => { + panic!("Mismatch for {key}: {seq_aux_dir_res:?} != {vec_aux_dir_res:?}"); + } + } + } else { + // All other keys should reconstruct deterministically, so we simply compare the blobs. + assert_eq!( + seq, vec, + "Image mismatch for key {key} - keyspace={keyspace:?} lsn={lsn}" + ); + } + } + /// Get last or prev record separately. Same as get_last_record_rlsn().last/prev. pub(crate) fn get_last_record_lsn(&self) -> Lsn { self.last_record_lsn.load().last From a9e0d09e80b01e86267510bae6b98faaf137df6b Mon Sep 17 00:00:00 2001 From: Vlad Lazar Date: Tue, 5 Mar 2024 12:38:10 +0000 Subject: [PATCH 2/2] review: remove redundant entries from the assert str --- pageserver/src/tenant/timeline.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pageserver/src/tenant/timeline.rs b/pageserver/src/tenant/timeline.rs index 6a49ef6c7b72..309ec2e82971 100644 --- a/pageserver/src/tenant/timeline.rs +++ b/pageserver/src/tenant/timeline.rs @@ -930,8 +930,8 @@ impl Timeline { (Ok(seq_aux_dir), Ok(vec_aux_dir)) => { assert_eq!( seq_aux_dir, vec_aux_dir, - "Mismatch for key {} - keyspace={:?} lsn={}: {:?} != {:?}", - key, keyspace, lsn, seq_aux_dir, vec_aux_dir + "Mismatch for key {} - keyspace={:?} lsn={}", + key, keyspace, lsn ); } (Err(_), Err(_)) => {}