From d6f1ee776e98031bfa9826d5c443e5147ceddd7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arpad=20M=C3=BCller?= Date: Mon, 14 Aug 2023 22:43:13 +0200 Subject: [PATCH 1/4] Use read_blk through BlockCursor and not through BlockReader At least in the cases where the concrete type won't be known clearly. --- pageserver/src/tenant/disk_btree.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pageserver/src/tenant/disk_btree.rs b/pageserver/src/tenant/disk_btree.rs index 2064a62c5607..18130e4abfa0 100644 --- a/pageserver/src/tenant/disk_btree.rs +++ b/pageserver/src/tenant/disk_btree.rs @@ -259,9 +259,10 @@ where { let mut stack = Vec::new(); stack.push((self.root_blk, None)); + let block_cursor = self.reader.block_cursor(); while let Some((node_blknum, opt_iter)) = stack.pop() { // Locate the node. - let node_buf = self.reader.read_blk(self.start_blk + node_blknum)?; + let node_buf = block_cursor.read_blk(self.start_blk + node_blknum)?; let node = OnDiskNode::deparse(node_buf.as_ref())?; let prefix_len = node.prefix_len as usize; @@ -353,8 +354,10 @@ where stack.push((self.root_blk, String::new(), 0, 0, 0)); + let block_cursor = self.reader.block_cursor(); + while let Some((blknum, path, depth, child_idx, key_off)) = stack.pop() { - let blk = self.reader.read_blk(self.start_blk + blknum)?; + let blk = block_cursor.read_blk(self.start_blk + blknum)?; let buf: &[u8] = blk.as_ref(); let node = OnDiskNode::::deparse(buf)?; From 2cd595b1b63174ab9dc8a68f795caf31799ccb8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arpad=20M=C3=BCller?= Date: Mon, 14 Aug 2023 23:07:27 +0200 Subject: [PATCH 2/4] Add BlockReaderRef enum Also, require BlockReader implementors to impl block_cursor. --- pageserver/ctl/src/layers.rs | 2 +- pageserver/src/tenant/blob_io.rs | 7 +- pageserver/src/tenant/block_io.rs | 88 ++++++++++++----- pageserver/src/tenant/disk_btree.rs | 9 +- pageserver/src/tenant/ephemeral_file.rs | 9 +- .../src/tenant/storage_layer/delta_layer.rs | 95 +++++++------------ 6 files changed, 116 insertions(+), 94 deletions(-) diff --git a/pageserver/ctl/src/layers.rs b/pageserver/ctl/src/layers.rs index 2d178a42bd2d..d81f6d09992e 100644 --- a/pageserver/ctl/src/layers.rs +++ b/pageserver/ctl/src/layers.rs @@ -70,7 +70,7 @@ async fn read_delta_file(path: impl AsRef) -> Result<()> { }, ) .await?; - let cursor = BlockCursor::new(&file); + let cursor = BlockCursor::new_fileblockreader_virtual(&file); for (k, v) in all { let value = cursor.read_blob(v.pos()).await?; println!("key:{} value_len:{}", k, value.len()); diff --git a/pageserver/src/tenant/blob_io.rs b/pageserver/src/tenant/blob_io.rs index fd211e827660..2a680bd890ac 100644 --- a/pageserver/src/tenant/blob_io.rs +++ b/pageserver/src/tenant/blob_io.rs @@ -12,14 +12,11 @@ //! len >= 128: 1XXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX //! use crate::page_cache::PAGE_SZ; -use crate::tenant::block_io::{BlockCursor, BlockReader}; +use crate::tenant::block_io::BlockCursor; use std::cmp::min; use std::io::{Error, ErrorKind}; -impl BlockCursor -where - R: BlockReader, -{ +impl<'a> BlockCursor<'a> { /// Read a blob into a new buffer. pub async fn read_blob(&self, offset: u64) -> Result, std::io::Error> { let mut buf = Vec::new(); diff --git a/pageserver/src/tenant/block_io.rs b/pageserver/src/tenant/block_io.rs index 503e5bd4e670..9fc696c0d0a6 100644 --- a/pageserver/src/tenant/block_io.rs +++ b/pageserver/src/tenant/block_io.rs @@ -2,8 +2,12 @@ //! Low-level Block-oriented I/O functions //! +use super::ephemeral_file::EphemeralFile; +use super::storage_layer::delta_layer::{Adapter, DeltaLayerInner}; use crate::page_cache::{self, PageReadGuard, ReadBufResult, PAGE_SZ}; +use crate::virtual_file::VirtualFile; use bytes::Bytes; +use std::fs::File; use std::ops::{Deref, DerefMut}; use std::os::unix::fs::FileExt; @@ -25,12 +29,7 @@ pub trait BlockReader { /// /// A cursor caches the last accessed page, allowing for faster /// access if the same block is accessed repeatedly. - fn block_cursor(&self) -> BlockCursor<&Self> - where - Self: Sized, - { - BlockCursor::new(self) - } + fn block_cursor(&self) -> BlockCursor<'_>; } impl BlockReader for &B @@ -40,6 +39,9 @@ where fn read_blk(&self, blknum: u32) -> Result { (*self).read_blk(blknum) } + fn block_cursor(&self) -> BlockCursor<'_> { + (*self).block_cursor() + } } /// Reference to an in-memory copy of an immutable on-disk block. @@ -76,6 +78,30 @@ impl<'a> Deref for BlockLease<'a> { } } +pub(crate) enum BlockReaderRef<'a> { + FileBlockReaderVirtual(&'a FileBlockReader), + FileBlockReaderFile(&'a FileBlockReader), + EphemeralFile(&'a EphemeralFile), + Adapter(Adapter<&'a DeltaLayerInner>), + #[cfg(test)] + TestDisk(&'a super::disk_btree::tests::TestDisk), +} + +impl<'a> BlockReaderRef<'a> { + #[inline(always)] + fn read_blk(&self, blknum: u32) -> Result { + use BlockReaderRef::*; + match self { + FileBlockReaderVirtual(r) => r.read_blk(blknum), + FileBlockReaderFile(r) => r.read_blk(blknum), + EphemeralFile(r) => r.read_blk(blknum), + Adapter(r) => r.read_blk(blknum), + #[cfg(test)] + TestDisk(r) => r.read_blk(blknum), + } + } +} + /// /// A "cursor" for efficiently reading multiple pages from a BlockReader /// @@ -93,21 +119,27 @@ impl<'a> Deref for BlockLease<'a> { /// // do stuff with 'buf' /// ``` /// -pub struct BlockCursor -where - R: BlockReader, -{ - reader: R, +pub struct BlockCursor<'a> { + reader: BlockReaderRef<'a>, } -impl BlockCursor -where - R: BlockReader, -{ - pub fn new(reader: R) -> Self { +impl<'a> BlockCursor<'a> { + pub(crate) fn new(reader: BlockReaderRef<'a>) -> Self { BlockCursor { reader } } + // Needed by cli + pub fn new_fileblockreader_virtual(reader: &'a FileBlockReader) -> Self { + BlockCursor { + reader: BlockReaderRef::FileBlockReaderVirtual(reader), + } + } + /// Read a block. + /// + /// Returns a "lease" object that can be used to + /// access to the contents of the page. (For the page cache, the + /// lease object represents a lock on the buffer.) + #[inline(always)] pub fn read_blk(&self, blknum: u32) -> Result { self.reader.read_blk(blknum) } @@ -139,12 +171,6 @@ where assert!(buf.len() == PAGE_SZ); self.file.read_exact_at(buf, blkno as u64 * PAGE_SZ as u64) } -} - -impl BlockReader for FileBlockReader -where - F: FileExt, -{ fn read_blk(&self, blknum: u32) -> Result { let cache = page_cache::get(); loop { @@ -170,6 +196,24 @@ where } } +impl BlockReader for FileBlockReader { + fn read_blk(&self, blknum: u32) -> Result { + self.read_blk(blknum) + } + fn block_cursor(&self) -> BlockCursor<'_> { + BlockCursor::new(BlockReaderRef::FileBlockReaderFile(self)) + } +} + +impl BlockReader for FileBlockReader { + fn read_blk(&self, blknum: u32) -> Result { + self.read_blk(blknum) + } + fn block_cursor(&self) -> BlockCursor<'_> { + BlockCursor::new(BlockReaderRef::FileBlockReaderVirtual(self)) + } +} + /// /// Trait for block-oriented output /// diff --git a/pageserver/src/tenant/disk_btree.rs b/pageserver/src/tenant/disk_btree.rs index 18130e4abfa0..deb5c858c8b0 100644 --- a/pageserver/src/tenant/disk_btree.rs +++ b/pageserver/src/tenant/disk_btree.rs @@ -686,15 +686,15 @@ impl BuildNode { } #[cfg(test)] -mod tests { +pub(crate) mod tests { use super::*; - use crate::tenant::block_io::BlockLease; + use crate::tenant::block_io::{BlockCursor, BlockLease, BlockReaderRef}; use rand::Rng; use std::collections::BTreeMap; use std::sync::atomic::{AtomicUsize, Ordering}; #[derive(Clone, Default)] - struct TestDisk { + pub(crate) struct TestDisk { blocks: Vec, } impl TestDisk { @@ -708,6 +708,9 @@ mod tests { buf.copy_from_slice(&self.blocks[blknum as usize]); Ok(std::rc::Rc::new(buf).into()) } + fn block_cursor(&self) -> BlockCursor<'_> { + BlockCursor::new(BlockReaderRef::TestDisk(self)) + } } impl BlockWriter for &mut TestDisk { fn write_blk(&mut self, buf: Bytes) -> io::Result { diff --git a/pageserver/src/tenant/ephemeral_file.rs b/pageserver/src/tenant/ephemeral_file.rs index 0bc88afd40f3..b7204a67ddfd 100644 --- a/pageserver/src/tenant/ephemeral_file.rs +++ b/pageserver/src/tenant/ephemeral_file.rs @@ -3,7 +3,7 @@ use crate::config::PageServerConf; use crate::page_cache::{self, PAGE_SZ}; -use crate::tenant::block_io::{BlockLease, BlockReader}; +use crate::tenant::block_io::{BlockCursor, BlockLease, BlockReader}; use crate::virtual_file::VirtualFile; use std::cmp::min; use std::fs::OpenOptions; @@ -243,12 +243,15 @@ impl BlockReader for EphemeralFile { Ok(BlockLease::EphemeralFileMutableTail(&self.mutable_tail)) } } + fn block_cursor(&self) -> super::block_io::BlockCursor<'_> { + BlockCursor::new(super::block_io::BlockReaderRef::EphemeralFile(self)) + } } #[cfg(test)] mod tests { use super::*; - use crate::tenant::block_io::BlockCursor; + use crate::tenant::block_io::{BlockCursor, BlockReaderRef}; use rand::{thread_rng, RngCore}; use std::fs; use std::str::FromStr; @@ -304,7 +307,7 @@ mod tests { blobs.push((pos, data)); } - let cursor = BlockCursor::new(&file); + let cursor = BlockCursor::new(BlockReaderRef::EphemeralFile(&file)); for (pos, expected) in blobs { let actual = cursor.read_blob(pos).await?; assert_eq!(actual, expected); diff --git a/pageserver/src/tenant/storage_layer/delta_layer.rs b/pageserver/src/tenant/storage_layer/delta_layer.rs index 620c3726a3c1..7a482706e519 100644 --- a/pageserver/src/tenant/storage_layer/delta_layer.rs +++ b/pageserver/src/tenant/storage_layer/delta_layer.rs @@ -318,30 +318,28 @@ impl DeltaLayer { tree_reader.dump().await?; - let keys = DeltaLayerInner::load_keys(&Ref(&**inner)).await?; + let keys = DeltaLayerInner::load_keys(&inner).await?; // A subroutine to dump a single blob - let dump_blob = |val: ValueRef<_>| -> _ { - async move { - let buf = val.reader.read_blob(val.blob_ref.pos()).await?; - let val = Value::des(&buf)?; - let desc = match val { - Value::Image(img) => { - format!(" img {} bytes", img.len()) - } - Value::WalRecord(rec) => { - let wal_desc = walrecord::describe_wal_record(&rec)?; - format!( - " rec {} bytes will_init: {} {}", - buf.len(), - rec.will_init(), - wal_desc - ) - } - }; - Ok(desc) - } - }; + async fn dump_blob(val: ValueRef<'_>) -> Result { + let buf = val.reader.read_blob(val.blob_ref.pos()).await?; + let val = Value::des(&buf)?; + let desc = match val { + Value::Image(img) => { + format!(" img {} bytes", img.len()) + } + Value::WalRecord(rec) => { + let wal_desc = walrecord::describe_wal_record(&rec)?; + format!( + " rec {} bytes will_init: {} {}", + buf.len(), + rec.will_init(), + wal_desc + ) + } + }; + Ok(desc) + } for entry in keys { let DeltaEntry { key, lsn, val, .. } = entry; @@ -552,17 +550,12 @@ impl DeltaLayer { /// Loads all keys stored in the layer. Returns key, lsn, value size and value reference. /// /// The value can be obtained via the [`ValueRef::load`] function. - pub(crate) async fn load_keys( - &self, - ctx: &RequestContext, - ) -> Result>>> { + pub(crate) async fn load_keys(&self, ctx: &RequestContext) -> Result>> { let inner = self .load(LayerAccessKind::KeyIter, ctx) .await .context("load delta layer keys")?; - - let inner = Ref(&**inner); - DeltaLayerInner::load_keys(&inner) + DeltaLayerInner::load_keys(inner) .await .context("Layer index is corrupted") } @@ -958,14 +951,14 @@ impl DeltaLayerInner { pub(super) async fn load_keys + Clone>( this: &T, - ) -> Result>> { + ) -> Result>> { let dl = this.as_ref(); let file = &dl.file; let tree_reader = DiskBtreeReader::<_, DELTA_KEY_SIZE>::new(dl.index_start_blk, dl.index_root_blk, file); - let mut all_keys: Vec> = Vec::new(); + let mut all_keys: Vec> = Vec::new(); tree_reader .visit( @@ -975,7 +968,9 @@ impl DeltaLayerInner { let delta_key = DeltaKey::from_slice(key); let val_ref = ValueRef { blob_ref: BlobRef(value), - reader: BlockCursor::new(Adapter(this.clone())), + reader: BlockCursor::new(crate::tenant::block_io::BlockReaderRef::Adapter( + Adapter(dl), + )), }; let pos = BlobRef(value).pos(); if let Some(last) = all_keys.last_mut() { @@ -1004,43 +999,23 @@ impl DeltaLayerInner { } } -/// Cloneable borrow wrapper to make borrows behave like smart pointers. -/// -/// Shared references are trivially copyable. This wrapper avoids (confusion) to otherwise attempt -/// cloning DeltaLayerInner. -pub(crate) struct Ref(T); - -impl<'a, T> AsRef for Ref<&'a T> { - fn as_ref(&self) -> &T { - self.0 - } -} - -impl<'a, T> Clone for Ref<&'a T> { - fn clone(&self) -> Self { - *self - } -} - -impl<'a, T> Copy for Ref<&'a T> {} - /// A set of data associated with a delta layer key and its value -pub struct DeltaEntry> { +pub struct DeltaEntry<'a> { pub key: Key, pub lsn: Lsn, /// Size of the stored value pub size: u64, /// Reference to the on-disk value - pub val: ValueRef, + pub val: ValueRef<'a>, } /// Reference to an on-disk value -pub struct ValueRef> { +pub struct ValueRef<'a> { blob_ref: BlobRef, - reader: BlockCursor>, + reader: BlockCursor<'a>, } -impl> ValueRef { +impl<'a> ValueRef<'a> { /// Loads the value from disk pub async fn load(&self) -> Result { // theoretically we *could* record an access time for each, but it does not really matter @@ -1050,10 +1025,10 @@ impl> ValueRef { } } -struct Adapter>(T); +pub(crate) struct Adapter(T); -impl> BlockReader for Adapter { - fn read_blk(&self, blknum: u32) -> Result { +impl> Adapter { + pub(crate) fn read_blk(&self, blknum: u32) -> Result { self.0.as_ref().file.read_blk(blknum) } } From 3719b16e5196a75eb3d4dd9e5b8818234fa5d7c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arpad=20M=C3=BCller?= Date: Wed, 16 Aug 2023 22:49:40 +0200 Subject: [PATCH 3/4] Remove BlockReader::read_blk in favour of inherent function --- pageserver/ctl/src/layer_map_analyzer.rs | 2 +- pageserver/ctl/src/layers.rs | 2 - pageserver/src/tenant/block_io.rs | 23 ++----- pageserver/src/tenant/disk_btree.rs | 6 +- pageserver/src/tenant/ephemeral_file.rs | 79 ++++++++++++------------ 5 files changed, 50 insertions(+), 62 deletions(-) diff --git a/pageserver/ctl/src/layer_map_analyzer.rs b/pageserver/ctl/src/layer_map_analyzer.rs index 9d9a1fa15110..883762a273ef 100644 --- a/pageserver/ctl/src/layer_map_analyzer.rs +++ b/pageserver/ctl/src/layer_map_analyzer.rs @@ -10,7 +10,7 @@ use std::{fs, path::Path, str}; use pageserver::page_cache::PAGE_SZ; use pageserver::repository::{Key, KEY_SIZE}; -use pageserver::tenant::block_io::{BlockReader, FileBlockReader}; +use pageserver::tenant::block_io::FileBlockReader; use pageserver::tenant::disk_btree::{DiskBtreeReader, VisitDirection}; use pageserver::tenant::storage_layer::delta_layer::{Summary, DELTA_KEY_SIZE}; use pageserver::tenant::storage_layer::range_overlaps; diff --git a/pageserver/ctl/src/layers.rs b/pageserver/ctl/src/layers.rs index d81f6d09992e..51fcd2bb22ef 100644 --- a/pageserver/ctl/src/layers.rs +++ b/pageserver/ctl/src/layers.rs @@ -44,8 +44,6 @@ pub(crate) enum LayerCmd { } async fn read_delta_file(path: impl AsRef) -> Result<()> { - use pageserver::tenant::block_io::BlockReader; - let path = path.as_ref(); virtual_file::init(10); page_cache::init(100); diff --git a/pageserver/src/tenant/block_io.rs b/pageserver/src/tenant/block_io.rs index 9fc696c0d0a6..f3262accf828 100644 --- a/pageserver/src/tenant/block_io.rs +++ b/pageserver/src/tenant/block_io.rs @@ -17,13 +17,6 @@ use std::os::unix::fs::FileExt; /// There are currently two implementations: EphemeralFile, and FileBlockReader /// below. pub trait BlockReader { - /// - /// Read a block. Returns a "lease" object that can be used to - /// access to the contents of the page. (For the page cache, the - /// lease object represents a lock on the buffer.) - /// - fn read_blk(&self, blknum: u32) -> Result; - /// /// Create a new "cursor" for reading from this reader. /// @@ -36,9 +29,6 @@ impl BlockReader for &B where B: BlockReader, { - fn read_blk(&self, blknum: u32) -> Result { - (*self).read_blk(blknum) - } fn block_cursor(&self) -> BlockCursor<'_> { (*self).block_cursor() } @@ -171,7 +161,12 @@ where assert!(buf.len() == PAGE_SZ); self.file.read_exact_at(buf, blkno as u64 * PAGE_SZ as u64) } - fn read_blk(&self, blknum: u32) -> Result { + /// Read a block. + /// + /// Returns a "lease" object that can be used to + /// access to the contents of the page. (For the page cache, the + /// lease object represents a lock on the buffer.) + pub fn read_blk(&self, blknum: u32) -> Result { let cache = page_cache::get(); loop { match cache @@ -197,18 +192,12 @@ where } impl BlockReader for FileBlockReader { - fn read_blk(&self, blknum: u32) -> Result { - self.read_blk(blknum) - } fn block_cursor(&self) -> BlockCursor<'_> { BlockCursor::new(BlockReaderRef::FileBlockReaderFile(self)) } } impl BlockReader for FileBlockReader { - fn read_blk(&self, blknum: u32) -> Result { - self.read_blk(blknum) - } fn block_cursor(&self) -> BlockCursor<'_> { BlockCursor::new(BlockReaderRef::FileBlockReaderVirtual(self)) } diff --git a/pageserver/src/tenant/disk_btree.rs b/pageserver/src/tenant/disk_btree.rs index deb5c858c8b0..02da01063c2d 100644 --- a/pageserver/src/tenant/disk_btree.rs +++ b/pageserver/src/tenant/disk_btree.rs @@ -701,13 +701,13 @@ pub(crate) mod tests { fn new() -> Self { Self::default() } - } - impl BlockReader for TestDisk { - fn read_blk(&self, blknum: u32) -> io::Result { + pub(crate) fn read_blk(&self, blknum: u32) -> io::Result { let mut buf = [0u8; PAGE_SZ]; buf.copy_from_slice(&self.blocks[blknum as usize]); Ok(std::rc::Rc::new(buf).into()) } + } + impl BlockReader for TestDisk { fn block_cursor(&self) -> BlockCursor<'_> { BlockCursor::new(BlockReaderRef::TestDisk(self)) } diff --git a/pageserver/src/tenant/ephemeral_file.rs b/pageserver/src/tenant/ephemeral_file.rs index b7204a67ddfd..b149bc978809 100644 --- a/pageserver/src/tenant/ephemeral_file.rs +++ b/pageserver/src/tenant/ephemeral_file.rs @@ -61,6 +61,46 @@ impl EphemeralFile { self.len } + pub(crate) fn read_blk(&self, blknum: u32) -> Result { + let flushed_blknums = 0..self.len / PAGE_SZ as u64; + if flushed_blknums.contains(&(blknum as u64)) { + let cache = page_cache::get(); + loop { + match cache + .read_immutable_buf(self.page_cache_file_id, blknum) + .map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + // order path before error because error is anyhow::Error => might have many contexts + format!( + "ephemeral file: read immutable page #{}: {}: {:#}", + blknum, + self.file.path.display(), + e, + ), + ) + })? { + page_cache::ReadBufResult::Found(guard) => { + return Ok(BlockLease::PageReadGuard(guard)) + } + page_cache::ReadBufResult::NotFound(mut write_guard) => { + let buf: &mut [u8] = write_guard.deref_mut(); + debug_assert_eq!(buf.len(), PAGE_SZ); + self.file + .read_exact_at(&mut buf[..], blknum as u64 * PAGE_SZ as u64)?; + write_guard.mark_valid(); + + // Swap for read lock + continue; + } + }; + } + } else { + debug_assert_eq!(blknum as u64, self.len / PAGE_SZ as u64); + Ok(BlockLease::EphemeralFileMutableTail(&self.mutable_tail)) + } + } + pub(crate) async fn write_blob(&mut self, srcbuf: &[u8]) -> Result { struct Writer<'a> { ephemeral_file: &'a mut EphemeralFile, @@ -204,45 +244,6 @@ impl Drop for EphemeralFile { } impl BlockReader for EphemeralFile { - fn read_blk(&self, blknum: u32) -> Result { - let flushed_blknums = 0..self.len / PAGE_SZ as u64; - if flushed_blknums.contains(&(blknum as u64)) { - let cache = page_cache::get(); - loop { - match cache - .read_immutable_buf(self.page_cache_file_id, blknum) - .map_err(|e| { - std::io::Error::new( - std::io::ErrorKind::Other, - // order path before error because error is anyhow::Error => might have many contexts - format!( - "ephemeral file: read immutable page #{}: {}: {:#}", - blknum, - self.file.path.display(), - e, - ), - ) - })? { - page_cache::ReadBufResult::Found(guard) => { - return Ok(BlockLease::PageReadGuard(guard)) - } - page_cache::ReadBufResult::NotFound(mut write_guard) => { - let buf: &mut [u8] = write_guard.deref_mut(); - debug_assert_eq!(buf.len(), PAGE_SZ); - self.file - .read_exact_at(&mut buf[..], blknum as u64 * PAGE_SZ as u64)?; - write_guard.mark_valid(); - - // Swap for read lock - continue; - } - }; - } - } else { - debug_assert_eq!(blknum as u64, self.len / PAGE_SZ as u64); - Ok(BlockLease::EphemeralFileMutableTail(&self.mutable_tail)) - } - } fn block_cursor(&self) -> super::block_io::BlockCursor<'_> { BlockCursor::new(super::block_io::BlockReaderRef::EphemeralFile(self)) } From a1f0143c87f46a09afe11312161fcc212e0c91d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arpad=20M=C3=BCller?= Date: Fri, 25 Aug 2023 10:19:41 +0200 Subject: [PATCH 4/4] Rustdoc --- pageserver/src/tenant/block_io.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pageserver/src/tenant/block_io.rs b/pageserver/src/tenant/block_io.rs index f3262accf828..3a6806357be7 100644 --- a/pageserver/src/tenant/block_io.rs +++ b/pageserver/src/tenant/block_io.rs @@ -68,6 +68,10 @@ impl<'a> Deref for BlockLease<'a> { } } +/// Provides the ability to read blocks from different sources, +/// similar to using traits for this purpose. +/// +/// Unlike traits, we also support the read function to be async though. pub(crate) enum BlockReaderRef<'a> { FileBlockReaderVirtual(&'a FileBlockReader), FileBlockReaderFile(&'a FileBlockReader),