bustubx 0.3.0

A simple relational database written in Rust
Documentation
use crate::buffer::buffer_pool::FrameId;
use crate::buffer::replacer::LRUKReplacer;
use dashmap::DashMap;
use derive_with::With;
use log::error;
use std::ops::Deref;
use std::sync::atomic::AtomicU32;
use std::sync::{Arc, RwLock};

pub type PageId = u32;
pub type AtomicPageId = AtomicU32;

pub const INVALID_PAGE_ID: PageId = 0;
pub const BUSTUBX_PAGE_SIZE: usize = 4096;

#[derive(Debug, Clone, With)]
pub struct Page {
    pub page_id: PageId,
    data: [u8; BUSTUBX_PAGE_SIZE],
    // 被引用次数
    pub pin_count: u32,
    // 是否被写过
    pub is_dirty: bool,
}

impl Page {
    pub fn empty() -> Self {
        Self::new(INVALID_PAGE_ID)
    }
    pub fn new(page_id: PageId) -> Self {
        Self {
            page_id,
            data: [0; BUSTUBX_PAGE_SIZE],
            pin_count: 0,
            is_dirty: false,
        }
    }
    pub fn destroy(&mut self) {
        self.page_id = 0;
        self.data = [0; BUSTUBX_PAGE_SIZE];
        self.pin_count = 0;
        self.is_dirty = false;
    }

    pub fn set_data(&mut self, data: [u8; BUSTUBX_PAGE_SIZE]) {
        self.data = data;
        self.is_dirty = true;
    }

    pub fn data(&self) -> &[u8] {
        &self.data
    }

    pub fn replace(&mut self, other: Page) {
        self.page_id = other.page_id;
        self.data = other.data;
        self.pin_count = other.pin_count;
        self.is_dirty = other.is_dirty;
    }
}

pub struct PageRef {
    pub page: Arc<RwLock<Page>>,
    pub page_table: Arc<DashMap<PageId, FrameId>>,
    pub replacer: Arc<RwLock<LRUKReplacer>>,
}

impl Deref for PageRef {
    type Target = Arc<RwLock<Page>>;

    fn deref(&self) -> &Self::Target {
        &self.page
    }
}

impl Drop for PageRef {
    fn drop(&mut self) {
        if self.page.read().unwrap().pin_count == 0 {
            return;
        }
        let page_id = self.page.read().unwrap().page_id;
        if let Some(frame_id) = self.page_table.get(&page_id) {
            self.page.write().unwrap().pin_count -= 1;
            if self.page.read().unwrap().pin_count == 0 {
                if let Err(e) = self
                    .replacer
                    .write()
                    .unwrap()
                    .set_evictable(*frame_id, true)
                {
                    panic!(
                        "Failed to set evictable to frame {}, err: {:?}",
                        *frame_id, e
                    );
                }
            }
        } else {
            error!("Cannot unpin page id {} as it is not in the pool", page_id);
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::buffer::replacer::LRUKReplacer;
    use crate::buffer::{Page, PageRef};
    use dashmap::DashMap;
    use std::sync::{Arc, RwLock};

    #[test]
    fn page_ref() {
        let page = Arc::new(RwLock::new(Page::new(1)));
        let page_table = Arc::new(DashMap::new());
        let replacer = Arc::new(RwLock::new(LRUKReplacer::new(10, 2)));

        let page_ref = PageRef {
            page: page.clone(),
            page_table,
            replacer,
        };
        assert_eq!(Arc::strong_count(&page), 2);
        assert_eq!(page_ref.read().unwrap().page_id, 1);
        drop(page_ref);
        assert_eq!(Arc::strong_count(&page), 1);
    }
}