Line data Source code
1 : //!
2 : //! Low-level Block-oriented I/O functions
3 : //!
4 :
5 : use super::ephemeral_file::EphemeralFile;
6 : use super::storage_layer::delta_layer::{Adapter, DeltaLayerInner};
7 : use crate::context::RequestContext;
8 : use crate::page_cache::{self, PageReadGuard, PageWriteGuard, ReadBufResult, PAGE_SZ};
9 : use crate::virtual_file::VirtualFile;
10 : use bytes::Bytes;
11 : use std::ops::Deref;
12 :
13 : /// This is implemented by anything that can read 8 kB (PAGE_SZ)
14 : /// blocks, using the page cache
15 : ///
16 : /// There are currently two implementations: EphemeralFile, and FileBlockReader
17 : /// below.
18 : pub trait BlockReader {
19 : ///
20 : /// Create a new "cursor" for reading from this reader.
21 : ///
22 : /// A cursor caches the last accessed page, allowing for faster
23 : /// access if the same block is accessed repeatedly.
24 : fn block_cursor(&self) -> BlockCursor<'_>;
25 : }
26 :
27 : impl<B> BlockReader for &B
28 : where
29 : B: BlockReader,
30 : {
31 23938756 : fn block_cursor(&self) -> BlockCursor<'_> {
32 23938756 : (*self).block_cursor()
33 23938756 : }
34 : }
35 :
36 : /// Reference to an in-memory copy of an immutable on-disk block.
37 : pub enum BlockLease<'a> {
38 : PageReadGuard(PageReadGuard<'static>),
39 : EphemeralFileMutableTail(&'a [u8; PAGE_SZ]),
40 : #[cfg(test)]
41 : Arc(std::sync::Arc<[u8; PAGE_SZ]>),
42 : #[cfg(test)]
43 : Vec(Vec<u8>),
44 : }
45 :
46 : impl From<PageReadGuard<'static>> for BlockLease<'static> {
47 107314103 : fn from(value: PageReadGuard<'static>) -> BlockLease<'static> {
48 107314103 : BlockLease::PageReadGuard(value)
49 107314103 : }
50 : }
51 :
52 : #[cfg(test)]
53 : impl<'a> From<std::sync::Arc<[u8; PAGE_SZ]>> for BlockLease<'a> {
54 1016332 : fn from(value: std::sync::Arc<[u8; PAGE_SZ]>) -> Self {
55 1016332 : BlockLease::Arc(value)
56 1016332 : }
57 : }
58 :
59 : impl<'a> Deref for BlockLease<'a> {
60 : type Target = [u8; PAGE_SZ];
61 :
62 357798547 : fn deref(&self) -> &Self::Target {
63 357798547 : match self {
64 354968378 : BlockLease::PageReadGuard(v) => v.deref(),
65 1779329 : BlockLease::EphemeralFileMutableTail(v) => v,
66 : #[cfg(test)]
67 1016332 : BlockLease::Arc(v) => v.deref(),
68 : #[cfg(test)]
69 34508 : BlockLease::Vec(v) => {
70 34508 : TryFrom::try_from(&v[..]).expect("caller must ensure that v has PAGE_SZ")
71 : }
72 : }
73 357798547 : }
74 : }
75 :
76 : /// Provides the ability to read blocks from different sources,
77 : /// similar to using traits for this purpose.
78 : ///
79 : /// Unlike traits, we also support the read function to be async though.
80 : pub(crate) enum BlockReaderRef<'a> {
81 : FileBlockReader(&'a FileBlockReader),
82 : EphemeralFile(&'a EphemeralFile),
83 : Adapter(Adapter<&'a DeltaLayerInner>),
84 : #[cfg(test)]
85 : TestDisk(&'a super::disk_btree::tests::TestDisk),
86 : #[cfg(test)]
87 : VirtualFile(&'a VirtualFile),
88 : }
89 :
90 : impl<'a> BlockReaderRef<'a> {
91 : #[inline(always)]
92 182461467 : async fn read_blk(
93 182461467 : &self,
94 182461467 : blknum: u32,
95 182461467 : ctx: &RequestContext,
96 182461467 : ) -> Result<BlockLease, std::io::Error> {
97 182461467 : use BlockReaderRef::*;
98 182461467 : match self {
99 88400025 : FileBlockReader(r) => r.read_blk(blknum, ctx).await,
100 74145234 : EphemeralFile(r) => r.read_blk(blknum, ctx).await,
101 18879676 : Adapter(r) => r.read_blk(blknum, ctx).await,
102 : #[cfg(test)]
103 1016332 : TestDisk(r) => r.read_blk(blknum),
104 : #[cfg(test)]
105 20200 : VirtualFile(r) => r.read_blk(blknum).await,
106 : }
107 182461465 : }
108 : }
109 :
110 : ///
111 : /// A "cursor" for efficiently reading multiple pages from a BlockReader
112 : ///
113 : /// You can access the last page with `*cursor`. 'read_blk' returns 'self', so
114 : /// that in many cases you can use a BlockCursor as a drop-in replacement for
115 : /// the underlying BlockReader. For example:
116 : ///
117 : /// ```no_run
118 : /// # use pageserver::tenant::block_io::{BlockReader, FileBlockReader};
119 : /// # use pageserver::context::RequestContext;
120 : /// # let reader: FileBlockReader = unimplemented!("stub");
121 : /// # let ctx: RequestContext = unimplemented!("stub");
122 : /// let cursor = reader.block_cursor();
123 : /// let buf = cursor.read_blk(1, &ctx);
124 : /// // do stuff with 'buf'
125 : /// let buf = cursor.read_blk(2, &ctx);
126 : /// // do stuff with 'buf'
127 : /// ```
128 : ///
129 : pub struct BlockCursor<'a> {
130 : reader: BlockReaderRef<'a>,
131 : }
132 :
133 : impl<'a> BlockCursor<'a> {
134 72766750 : pub(crate) fn new(reader: BlockReaderRef<'a>) -> Self {
135 72766750 : BlockCursor { reader }
136 72766750 : }
137 : // Needed by cli
138 0 : pub fn new_fileblockreader(reader: &'a FileBlockReader) -> Self {
139 0 : BlockCursor {
140 0 : reader: BlockReaderRef::FileBlockReader(reader),
141 0 : }
142 0 : }
143 :
144 : /// Read a block.
145 : ///
146 : /// Returns a "lease" object that can be used to
147 : /// access to the contents of the page. (For the page cache, the
148 : /// lease object represents a lock on the buffer.)
149 : #[inline(always)]
150 182461566 : pub async fn read_blk(
151 182461566 : &self,
152 182461566 : blknum: u32,
153 182461566 : ctx: &RequestContext,
154 182461566 : ) -> Result<BlockLease, std::io::Error> {
155 182461467 : self.reader.read_blk(blknum, ctx).await
156 182461465 : }
157 : }
158 :
159 : /// An adapter for reading a (virtual) file using the page cache.
160 : ///
161 : /// The file is assumed to be immutable. This doesn't provide any functions
162 : /// for modifying the file, nor for invalidating the cache if it is modified.
163 : pub struct FileBlockReader {
164 : pub file: VirtualFile,
165 :
166 : /// Unique ID of this file, used as key in the page cache.
167 : file_id: page_cache::FileId,
168 : }
169 :
170 : impl FileBlockReader {
171 34407 : pub fn new(file: VirtualFile) -> Self {
172 34407 : let file_id = page_cache::next_file_id();
173 34407 :
174 34407 : FileBlockReader { file_id, file }
175 34407 : }
176 :
177 : /// Read a page from the underlying file into given buffer.
178 4765211 : async fn fill_buffer(
179 4765211 : &self,
180 4765211 : buf: PageWriteGuard<'static>,
181 4765211 : blkno: u32,
182 4765211 : ) -> Result<PageWriteGuard<'static>, std::io::Error> {
183 4765211 : assert!(buf.len() == PAGE_SZ);
184 4765211 : self.file
185 4765211 : .read_exact_at_page(buf, blkno as u64 * PAGE_SZ as u64)
186 63028 : .await
187 4765211 : }
188 : /// Read a block.
189 : ///
190 : /// Returns a "lease" object that can be used to
191 : /// access to the contents of the page. (For the page cache, the
192 : /// lease object represents a lock on the buffer.)
193 107314104 : pub async fn read_blk(
194 107314104 : &self,
195 107314104 : blknum: u32,
196 107314104 : ctx: &RequestContext,
197 107314108 : ) -> Result<BlockLease, std::io::Error> {
198 107314108 : let cache = page_cache::get();
199 107314108 : match cache
200 107314108 : .read_immutable_buf(self.file_id, blknum, ctx)
201 1255698 : .await
202 107314107 : .map_err(|e| {
203 0 : std::io::Error::new(
204 0 : std::io::ErrorKind::Other,
205 0 : format!("Failed to read immutable buf: {e:#}"),
206 0 : )
207 107314107 : })? {
208 102548896 : ReadBufResult::Found(guard) => Ok(guard.into()),
209 4765211 : ReadBufResult::NotFound(write_guard) => {
210 : // Read the page from disk into the buffer
211 4765211 : let write_guard = self.fill_buffer(write_guard, blknum).await?;
212 4765211 : Ok(write_guard.mark_valid().into())
213 : }
214 : }
215 107314107 : }
216 : }
217 :
218 : impl BlockReader for FileBlockReader {
219 47873271 : fn block_cursor(&self) -> BlockCursor<'_> {
220 47873271 : BlockCursor::new(BlockReaderRef::FileBlockReader(self))
221 47873271 : }
222 : }
223 :
224 : ///
225 : /// Trait for block-oriented output
226 : ///
227 : pub trait BlockWriter {
228 : ///
229 : /// Write a page to the underlying storage.
230 : ///
231 : /// 'buf' must be of size PAGE_SZ. Returns the block number the page was
232 : /// written to.
233 : ///
234 : fn write_blk(&mut self, buf: Bytes) -> Result<u32, std::io::Error>;
235 : }
236 :
237 : ///
238 : /// A simple in-memory buffer of blocks.
239 : ///
240 : pub struct BlockBuf {
241 : pub blocks: Vec<Bytes>,
242 : }
243 : impl BlockWriter for BlockBuf {
244 124468 : fn write_blk(&mut self, buf: Bytes) -> Result<u32, std::io::Error> {
245 124468 : assert!(buf.len() == PAGE_SZ);
246 124468 : let blknum = self.blocks.len();
247 124468 : self.blocks.push(buf);
248 124468 : Ok(blknum as u32)
249 124468 : }
250 : }
251 :
252 : impl BlockBuf {
253 21947 : pub fn new() -> Self {
254 21947 : BlockBuf { blocks: Vec::new() }
255 21947 : }
256 :
257 2373630 : pub fn size(&self) -> u64 {
258 2373630 : (self.blocks.len() * PAGE_SZ) as u64
259 2373630 : }
260 : }
261 : impl Default for BlockBuf {
262 0 : fn default() -> Self {
263 0 : Self::new()
264 0 : }
265 : }
|