From f4cac1f30f096ceb8c1fa4a3281319883d10be6e Mon Sep 17 00:00:00 2001 From: "Alex Chi Z." <4198311+skyzh@users.noreply.github.com> Date: Sat, 24 Aug 2024 00:38:42 +0800 Subject: [PATCH] impr(pageserver): error if keys are unordered in merge iter (#8818) In case of corrupted delta layers, we can detect the corruption and bail out the compaction. ## Summary of changes * Detect wrong delta desc of key range * Detect unordered deltas Signed-off-by: Alex Chi Z --- .../src/tenant/storage_layer/delta_layer.rs | 16 +++++++++++ .../src/tenant/storage_layer/image_layer.rs | 15 +++++++++++ .../tenant/storage_layer/merge_iterator.rs | 27 ++++++++++++++++++- 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/pageserver/src/tenant/storage_layer/delta_layer.rs b/pageserver/src/tenant/storage_layer/delta_layer.rs index 6c2391d72d..b1b5217f7f 100644 --- a/pageserver/src/tenant/storage_layer/delta_layer.rs +++ b/pageserver/src/tenant/storage_layer/delta_layer.rs @@ -232,6 +232,18 @@ pub struct DeltaLayerInner { max_vectored_read_bytes: Option, } +impl DeltaLayerInner { + pub(crate) fn layer_dbg_info(&self) -> String { + format!( + "delta {}..{} {}..{}", + self.key_range().start, + self.key_range().end, + self.lsn_range().start, + self.lsn_range().end + ) + } +} + impl std::fmt::Debug for DeltaLayerInner { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("DeltaLayerInner") @@ -1527,6 +1539,10 @@ pub struct DeltaLayerIterator<'a> { } impl<'a> DeltaLayerIterator<'a> { + pub(crate) fn layer_dbg_info(&self) -> String { + self.delta_layer.layer_dbg_info() + } + /// Retrieve a batch of key-value pairs into the iterator buffer. async fn next_batch(&mut self) -> anyhow::Result<()> { assert!(self.key_values_batch.is_empty()); diff --git a/pageserver/src/tenant/storage_layer/image_layer.rs b/pageserver/src/tenant/storage_layer/image_layer.rs index 9a19e4e2c7..94120a4e3e 100644 --- a/pageserver/src/tenant/storage_layer/image_layer.rs +++ b/pageserver/src/tenant/storage_layer/image_layer.rs @@ -167,6 +167,17 @@ pub struct ImageLayerInner { max_vectored_read_bytes: Option, } +impl ImageLayerInner { + pub(crate) fn layer_dbg_info(&self) -> String { + format!( + "image {}..{} {}", + self.key_range().start, + self.key_range().end, + self.lsn() + ) + } +} + impl std::fmt::Debug for ImageLayerInner { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ImageLayerInner") @@ -1024,6 +1035,10 @@ pub struct ImageLayerIterator<'a> { } impl<'a> ImageLayerIterator<'a> { + pub(crate) fn layer_dbg_info(&self) -> String { + self.image_layer.layer_dbg_info() + } + /// Retrieve a batch of key-value pairs into the iterator buffer. async fn next_batch(&mut self) -> anyhow::Result<()> { assert!(self.key_values_batch.is_empty()); diff --git a/pageserver/src/tenant/storage_layer/merge_iterator.rs b/pageserver/src/tenant/storage_layer/merge_iterator.rs index b4bd976033..d2c341e5ce 100644 --- a/pageserver/src/tenant/storage_layer/merge_iterator.rs +++ b/pageserver/src/tenant/storage_layer/merge_iterator.rs @@ -3,6 +3,7 @@ use std::{ collections::{binary_heap, BinaryHeap}, }; +use anyhow::bail; use pageserver_api::key::Key; use utils::lsn::Lsn; @@ -26,6 +27,13 @@ impl<'a> LayerRef<'a> { Self::Delta(x) => LayerIterRef::Delta(x.iter(ctx)), } } + + fn layer_dbg_info(&self) -> String { + match self { + Self::Image(x) => x.layer_dbg_info(), + Self::Delta(x) => x.layer_dbg_info(), + } + } } enum LayerIterRef<'a> { @@ -40,6 +48,13 @@ impl LayerIterRef<'_> { Self::Image(x) => x.next().await, } } + + fn layer_dbg_info(&self) -> String { + match self { + Self::Image(x) => x.layer_dbg_info(), + Self::Delta(x) => x.layer_dbg_info(), + } + } } /// This type plays several roles at once @@ -75,6 +90,11 @@ impl<'a> PeekableLayerIterRef<'a> { async fn next(&mut self) -> anyhow::Result> { let result = self.peeked.take(); self.peeked = self.iter.next().await?; + if let (Some((k1, l1, _)), Some((k2, l2, _))) = (&self.peeked, &result) { + if (k1, l1) < (k2, l2) { + bail!("iterator is not ordered: {}", self.iter.layer_dbg_info()); + } + } Ok(result) } } @@ -178,7 +198,12 @@ impl<'a> IteratorWrapper<'a> { let iter = PeekableLayerIterRef::create(iter).await?; if let Some((k1, l1, _)) = iter.peek() { let (k2, l2) = first_key_lower_bound; - debug_assert!((k1, l1) >= (k2, l2)); + if (k1, l1) < (k2, l2) { + bail!( + "layer key range did not include the first key in the layer: {}", + layer.layer_dbg_info() + ); + } } *self = Self::Loaded { iter }; Ok(())