Overview
PageCache implements a buffer pool that caches database pages in memory with a CLOCK (second-chance) eviction policy. It provides pinning semantics to prevent eviction of in-use pages and automatic writeback of dirty pages.
Module: databas_core::page_cache
Visibility: pub(crate) - Internal to the core crate
Structure
Underlying disk manager for reading and writing pages.
Fixed-size array of in-memory page frames. Each frame can hold one page.
Mapping from page ID to frame ID for resident pages.
Current position of the CLOCK hand for victim selection.
Frame Structure
Page currently stored in this frame, or
None if empty.4096-byte buffer containing the page data.
CLOCK reference bit. Set on access, cleared by CLOCK algorithm.
Whether the page has been modified since it was loaded or last flushed.
Number of active
PinGuard instances referencing this frame. Cannot be evicted if > 0.Methods
new
Creates a new page cache with a fixed number of frames.The disk manager to use for page I/O.
Number of page frames to allocate. Must be at least 1.
Returns a new cache with all frames empty and unpinned.
PageCacheError::InvalidFrameCount-frame_countis 0
fetch_page
Fetches a page from disk or cache and returns a pinned guard.The page to fetch. Must be a valid page in the database file.
Returns a pin guard providing access to the page data. The page remains pinned until the guard is dropped.
- Cache hit: Sets reference bit, increments pin count, returns guard
- Cache miss:
- Selects a victim frame using CLOCK algorithm
- Flushes victim frame if dirty
- Reads requested page from disk
- Loads page into frame with
pin_count = 1andreference = true - Updates page table
PageCacheError::NoEvictableFrame- All frames are pinnedPageCacheError::Disk(DiskManagerError)- Disk I/O errorPageCacheError::CorruptPageTableEntry- Internal corruption detected
new_page
Allocates a new page on disk and returns it pinned in cache.Returns the new page ID and a pin guard for the zero-initialized page.
- Selects a victim frame first (before disk allocation)
- Calls
DiskManager::new_page()to extend the file - Loads the zero-initialized page into the selected frame
- Returns the page pinned and ready for writes
PageCacheError::NoEvictableFrame- All frames are pinned (disk not modified)PageCacheError::Disk(DiskManagerError)- Disk allocation failed
flush_page
Flushes a single page to disk if it is dirty and unpinned.The page to flush. May be resident or non-resident.
Returns
Ok(()) on success.- Non-resident pages are a no-op (returns
Ok(())) - Clean pages are a no-op
- Dirty unpinned pages are written to disk and marked clean
PageCacheError::PinnedPage- Cannot flush a pinned pagePageCacheError::Disk(DiskManagerError)- Disk write failed
flush_all
Flushes all dirty unpinned pages to disk.Returns
Ok(()) if all dirty unpinned pages were flushed.- Iterates through all frames
- Writes each dirty unpinned page to disk
- Stops on first error
PageCacheError::PinnedPage- Found a dirty pinned pagePageCacheError::Disk(DiskManagerError)- Disk write failed
PinGuard
PinGuard provides safe access to cached pages with RAII-based pinning.
page
Returns an immutable reference to the page data.page_mut
Returns a mutable reference to the page data and marks it dirty.- Automatically sets the dirty bit on the frame
- Page will be written back on eviction or explicit flush
Drop behavior
When aPinGuard is dropped, the pin count is decremented:
Error Types
PageCacheError
Underlying disk I/O error during read or write.
NoEvictableFrame
All frames are pinned; cannot fetch or allocate new pages. Caller must unpin pages.
Attempted to flush a page that is currently pinned.
Attempted to create a cache with 0 frames.
Internal data structure corruption: page table references an invalid frame.
PageCacheResult
CLOCK Algorithm
The cache uses the CLOCK (second-chance) replacement algorithm:- Reference bit: Set to
truewhen a page is accessed - Clock hand: Iterates through frames circularly
- Victim selection:
- Skip pinned frames (pin_count > 0)
- If reference bit is set, clear it and continue (second chance)
- If reference bit is clear, select as victim
- Scan up to
2 * frame_countframes before giving up
- Approximates LRU with lower overhead
- Gives recently accessed pages a second chance
- No expensive timestamp tracking
Drop Behavior
PageCache performs best-effort flushing on drop:
Performance Considerations
Frame Count
- More frames = higher hit rate, less I/O
- Too many frames = excessive memory usage
- Typical range: 128-1024 frames (512 KB - 4 MB)
Pinning
- Keep pages pinned for the minimum necessary time
- Excessive pinning leads to
NoEvictableFrameerrors - Consider unpinning and re-fetching for long operations
Flushing
- Explicit
flush_page()provides durability guarantees - Drop-based flushing is best-effort only
flush_all()before shutdown ensures consistency
Usage Examples
Basic read-modify-write
Multiple concurrent pins
Allocate and initialize
Related Types
- DiskManager - Underlying disk I/O layer
- PinGuard - RAII guard for pinned pages
- PageId - Type alias for
u64 - FrameId - Type alias for
usize - PAGE_SIZE - Constant defining page size (4096)