feophantlib/engine/io/index_formats/
btree_first_page.rs

1use crate::engine::io::{
2    format_traits::{Parseable, Serializable},
3    page_formats::{PageOffset, PageOffsetError},
4};
5use bytes::BufMut;
6use thiserror::Error;
7
8/// Special page that points to where the root page of the index is really located
9#[derive(Debug, PartialEq)]
10pub struct BTreeFirstPage {
11    pub root_offset: PageOffset,
12}
13
14impl Parseable<BTreeFirstPageError> for BTreeFirstPage {
15    type Output = Self;
16    fn parse(buffer: &mut impl bytes::Buf) -> Result<Self::Output, BTreeFirstPageError> {
17        let root_offset = PageOffset::parse(buffer)?;
18        Ok(BTreeFirstPage { root_offset })
19    }
20}
21
22impl Serializable for BTreeFirstPage {
23    fn serialize(&self, buffer: &mut impl BufMut) {
24        self.root_offset.serialize(buffer);
25    }
26}
27
28#[derive(Debug, Error)]
29pub enum BTreeFirstPageError {
30    #[error(transparent)]
31    PageOffsetError(#[from] PageOffsetError),
32}
33
34#[cfg(test)]
35mod tests {
36    use std::sync::Arc;
37
38    use bytes::BytesMut;
39    use tempfile::TempDir;
40    use uuid::Uuid;
41
42    use crate::{
43        constants::PAGE_SIZE,
44        engine::io::{
45            block_layer::file_manager2::FileManager2,
46            page_formats::{PageId, PageType},
47        },
48    };
49
50    use super::*;
51
52    #[test]
53    fn test_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
54        let first = BTreeFirstPage {
55            root_offset: PageOffset(1),
56        };
57
58        let mut buffer = BytesMut::with_capacity(PAGE_SIZE as usize);
59        first.serialize(&mut buffer);
60
61        let result = BTreeFirstPage::parse(&mut buffer)?;
62
63        assert_eq!(first, result);
64
65        Ok(())
66    }
67
68    #[tokio::test]
69    async fn test_on_disk() -> Result<(), Box<dyn std::error::Error>> {
70        let tmp = TempDir::new()?;
71        let tmp_dir = tmp.path().as_os_str().to_os_string();
72
73        let fm = Arc::new(FileManager2::new(tmp_dir.clone())?);
74
75        let page_id = PageId {
76            resource_key: Uuid::new_v4(),
77            page_type: PageType::Data,
78        };
79
80        let (first_offset, first_guard) = fm.get_next_offset(&page_id).await?;
81        assert_eq!(first_offset, PageOffset(0));
82
83        let (root_offset, _root_guard) = fm.get_next_offset(&page_id).await?;
84        assert_ne!(root_offset, PageOffset(0));
85
86        let btfp = BTreeFirstPage { root_offset };
87        fm.update_page(first_guard, btfp.serialize_and_pad())
88            .await?;
89
90        // Okay now its time to actually test, without drop
91        let (mut new_first_page, _new_first_guard) = fm.get_page(&page_id, &PageOffset(0)).await?;
92        let btfp2 = BTreeFirstPage::parse(&mut new_first_page)?;
93        assert_ne!(btfp2.root_offset, PageOffset(0));
94
95        // Test again with a drop
96        drop(fm);
97        let fm2 = Arc::new(FileManager2::new(tmp_dir)?);
98
99        let (mut new_first_page2, _new_first_guard2) =
100            fm2.get_page(&page_id, &PageOffset(0)).await?;
101        let btfp2 = BTreeFirstPage::parse(&mut new_first_page2)?;
102        assert_ne!(btfp2.root_offset, PageOffset(0));
103
104        Ok(())
105    }
106}