Skip to main content

page_db/
store.rs

1//! The [`PageStore`] trait: the storage seam the buffer pool sits on.
2
3use crate::error::PageResult;
4use crate::page::{Page, PageId};
5
6/// A backing store of fixed-size pages addressed by [`PageId`].
7///
8/// This is the extension seam between the buffer pool and what holds the pages.
9/// [`PageFile`](crate::PageFile) is the implementation that matters — pages on
10/// disk through Direct I/O — but the pool is written against this trait so it
11/// can be driven by an in-memory store in tests, or by an alternative backend
12/// later, without change.
13///
14/// A store does not cache, pin, or track dirtiness; those are the pool's job. A
15/// store only has to read a page by id, write a page to an id, allocate a blank
16/// page of the right size, and flush to durability.
17///
18/// # Examples
19///
20/// A [`PageFile`](crate::PageFile) is a `PageStore`, so it can back a pool:
21///
22/// ```
23/// use page_db::{BufferPool, PageFile, PageId, DEFAULT_PAGE_SIZE};
24///
25/// # let dir = tempfile::tempdir().unwrap();
26/// # let path = dir.path().join("data.pages");
27/// let file = PageFile::open(&path, DEFAULT_PAGE_SIZE)?;
28/// let pool = BufferPool::new(file, 64);   // 64 frames over the file
29/// let _ = pool.new_page(PageId::new(0))?;
30/// # Ok::<(), page_db::PageError>(())
31/// ```
32pub trait PageStore: Send + Sync {
33    /// The fixed page size of this store, in bytes.
34    fn page_size(&self) -> usize;
35
36    /// Allocate a blank page sized for this store. The page is in memory only.
37    fn allocate_page(&self) -> Page;
38
39    /// Read the page at `id` into `page`, verifying it.
40    ///
41    /// Reusing the caller's buffer is what lets the pool recycle a frame on a
42    /// miss without allocating.
43    ///
44    /// # Errors
45    ///
46    /// Implementation-defined; for a file this is a short read past end-of-file
47    /// or a failed integrity check.
48    fn read_into(&self, id: PageId, page: &mut Page) -> PageResult<()>;
49
50    /// Write `page` to slot `id`. The page's header is stamped (id + checksum)
51    /// as part of the write.
52    ///
53    /// # Errors
54    ///
55    /// Implementation-defined; for a file this is an I/O failure or a page-size
56    /// mismatch.
57    fn write_page(&self, id: PageId, page: &mut Page) -> PageResult<()>;
58
59    /// Flush all written pages to stable storage.
60    ///
61    /// # Errors
62    ///
63    /// Implementation-defined; for a file this is an I/O failure.
64    fn sync(&self) -> PageResult<()>;
65}
66
67impl<S: PageStore> PageStore for std::sync::Arc<S> {
68    #[inline]
69    fn page_size(&self) -> usize {
70        (**self).page_size()
71    }
72
73    #[inline]
74    fn allocate_page(&self) -> Page {
75        (**self).allocate_page()
76    }
77
78    #[inline]
79    fn read_into(&self, id: PageId, page: &mut Page) -> PageResult<()> {
80        (**self).read_into(id, page)
81    }
82
83    #[inline]
84    fn write_page(&self, id: PageId, page: &mut Page) -> PageResult<()> {
85        (**self).write_page(id, page)
86    }
87
88    #[inline]
89    fn sync(&self) -> PageResult<()> {
90        (**self).sync()
91    }
92}
93
94impl PageStore for crate::file::PageFile {
95    #[inline]
96    fn page_size(&self) -> usize {
97        crate::file::PageFile::page_size(self)
98    }
99
100    #[inline]
101    fn allocate_page(&self) -> Page {
102        crate::file::PageFile::allocate_page(self)
103    }
104
105    #[inline]
106    fn read_into(&self, id: PageId, page: &mut Page) -> PageResult<()> {
107        crate::file::PageFile::read_into(self, id, page)
108    }
109
110    #[inline]
111    fn write_page(&self, id: PageId, page: &mut Page) -> PageResult<()> {
112        crate::file::PageFile::write_page(self, id, page)
113    }
114
115    #[inline]
116    fn sync(&self) -> PageResult<()> {
117        crate::file::PageFile::sync(self)
118    }
119}