mirror of
https://github.com/neondatabase/neon.git
synced 2026-01-05 20:42:54 +00:00
snapfile: split apart code into multiple files
versioned.rs: for things that get serialized and must be versioned to avoid breaking backwards compatibility. page.rs: for the Page struct.
This commit is contained in:
@@ -4,41 +4,19 @@
|
||||
#![forbid(unsafe_code)]
|
||||
#![warn(clippy::cast_possible_truncation)]
|
||||
|
||||
mod page;
|
||||
mod versioned;
|
||||
#[doc(inline)]
|
||||
pub use page::Page;
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use aversion::group::{DataSink, DataSourceExt};
|
||||
use aversion::util::cbor::CborData;
|
||||
use aversion::{assign_message_ids, UpgradeLatest, Versioned};
|
||||
use bookfile::{Book, BookWriter, ChapterIndex, ChapterWriter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
// A random constant, to identify this file type.
|
||||
const SNAPFILE_MAGIC: u32 = 0x7fb8_38a8;
|
||||
|
||||
// Constant chapter numbers
|
||||
// FIXME: the bookfile crate should use something better to index, e.g. strings.
|
||||
/// Snapshot-specific file metadata
|
||||
#[allow(dead_code)] // FIXME: this is a placeholder for future functionality.
|
||||
const CHAPTER_SNAP_META: u64 = 1;
|
||||
/// A packed set of 8KB pages.
|
||||
const CHAPTER_PAGES: u64 = 2;
|
||||
/// An index of pages.
|
||||
const CHAPTER_PAGE_INDEX: u64 = 3;
|
||||
|
||||
// FIXME: move serialized data structs to a separate file.
|
||||
|
||||
/// An index from page number to offset within the pages chapter.
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Versioned, UpgradeLatest)]
|
||||
pub struct PageIndexV1 {
|
||||
/// A map from page number to file offset.
|
||||
map: BTreeMap<u64, u64>,
|
||||
}
|
||||
|
||||
// A placeholder type, that will always point to the latest version.
|
||||
type PageIndex = PageIndexV1;
|
||||
use versioned::PageIndex;
|
||||
|
||||
impl PageIndex {
|
||||
/// Retrieve the page offset from the index.
|
||||
@@ -53,44 +31,6 @@ impl PageIndex {
|
||||
}
|
||||
}
|
||||
|
||||
// Each message gets a unique message id, for tracking by the aversion traits.
|
||||
assign_message_ids! {
|
||||
PageIndex: 100,
|
||||
}
|
||||
|
||||
/// A single 8KB page.
|
||||
pub struct Page(pub Box<[u8; 8192]>);
|
||||
|
||||
impl Default for Page {
|
||||
fn default() -> Self {
|
||||
Page(Box::new([0u8; 8192]))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 8192]> for Page {
|
||||
fn from(array: [u8; 8192]) -> Self {
|
||||
Page(Box::new(array))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<[u8; 8192]>> for Page {
|
||||
fn from(heap_array: Box<[u8; 8192]>) -> Self {
|
||||
Page(heap_array)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8; 8192]> for Page {
|
||||
fn as_ref(&self) -> &[u8; 8192] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u8; 8192]> for Page {
|
||||
fn as_mut(&mut self) -> &mut [u8; 8192] {
|
||||
self.0.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// A read-only snapshot file.
|
||||
pub struct SnapFile {
|
||||
book: Book<File>,
|
||||
@@ -106,19 +46,19 @@ impl SnapFile {
|
||||
pub fn new(path: &Path) -> Result<Self> {
|
||||
let file = File::open(path)?;
|
||||
let mut book = Book::new(file)?;
|
||||
if book.magic() != SNAPFILE_MAGIC {
|
||||
if book.magic() != versioned::SNAPFILE_MAGIC {
|
||||
bail!("bad magic number");
|
||||
}
|
||||
|
||||
// Read the page index into memory.
|
||||
let chapter_num = book
|
||||
.find_chapter(CHAPTER_PAGE_INDEX)
|
||||
.find_chapter(versioned::CHAPTER_PAGE_INDEX)
|
||||
.ok_or_else(|| anyhow!("snapfile missing index chapter"))?;
|
||||
let chapter_reader = book.chapter_reader(chapter_num)?;
|
||||
let mut source = CborData::new(chapter_reader);
|
||||
let page_index: PageIndex = source.expect_message()?;
|
||||
let page_chapter_num = book
|
||||
.find_chapter(CHAPTER_PAGES)
|
||||
.find_chapter(versioned::CHAPTER_PAGES)
|
||||
.ok_or_else(|| anyhow!("snapfile missing pages chapter"))?;
|
||||
Ok(SnapFile {
|
||||
book,
|
||||
@@ -176,8 +116,8 @@ impl SnapWriter {
|
||||
/// Create a new `SnapWriter`
|
||||
pub fn new(path: &Path) -> Result<Self> {
|
||||
let file = File::create(path)?;
|
||||
let book = BookWriter::new(file, SNAPFILE_MAGIC)?;
|
||||
let writer = book.new_chapter(CHAPTER_PAGES);
|
||||
let book = BookWriter::new(file, versioned::SNAPFILE_MAGIC)?;
|
||||
let writer = book.new_chapter(versioned::CHAPTER_PAGES);
|
||||
Ok(SnapWriter {
|
||||
writer,
|
||||
page_index: PageIndex::default(),
|
||||
@@ -211,7 +151,7 @@ impl SnapWriter {
|
||||
// necessary file metadata.
|
||||
// FIXME: these 3 lines could be combined into a single function
|
||||
// that means "serialize this data structure with this format into this chapter".
|
||||
let writer = book.new_chapter(CHAPTER_PAGE_INDEX);
|
||||
let writer = book.new_chapter(versioned::CHAPTER_PAGE_INDEX);
|
||||
let mut sink = CborData::new(writer);
|
||||
sink.write_message(&self.page_index)?;
|
||||
|
||||
|
||||
32
snapfile/src/page.rs
Normal file
32
snapfile/src/page.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
/// A single 8KB page.
|
||||
pub struct Page(pub Box<[u8; 8192]>);
|
||||
|
||||
impl Default for Page {
|
||||
fn default() -> Self {
|
||||
Page(Box::new([0u8; 8192]))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 8192]> for Page {
|
||||
fn from(array: [u8; 8192]) -> Self {
|
||||
Page(Box::new(array))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<[u8; 8192]>> for Page {
|
||||
fn from(heap_array: Box<[u8; 8192]>) -> Self {
|
||||
Page(heap_array)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8; 8192]> for Page {
|
||||
fn as_ref(&self) -> &[u8; 8192] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u8; 8192]> for Page {
|
||||
fn as_mut(&mut self) -> &mut [u8; 8192] {
|
||||
self.0.as_mut()
|
||||
}
|
||||
}
|
||||
43
snapfile/src/versioned.rs
Normal file
43
snapfile/src/versioned.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
//! Versioned data structures for snapshot files
|
||||
//!
|
||||
//! To ensure that future versions of software can read snapshot files,
|
||||
//! all data structures that are serialized into the snapshot files should
|
||||
//! live in this module.
|
||||
//!
|
||||
//! Once released, versioned data structures should never be modified.
|
||||
//! Instead, new versions should be created and conversion functions should
|
||||
//! be defined using the `FromVersion` trait.
|
||||
|
||||
use aversion::{assign_message_ids, UpgradeLatest, Versioned};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
// A random constant, to identify this file type.
|
||||
pub(crate) const SNAPFILE_MAGIC: u32 = 0x7fb8_38a8;
|
||||
|
||||
// Constant chapter numbers
|
||||
// FIXME: the bookfile crate should use something better to index, e.g. strings.
|
||||
/// Snapshot-specific file metadata
|
||||
#[allow(dead_code)] // FIXME: this is a placeholder for future functionality.
|
||||
pub(crate) const CHAPTER_SNAP_META: u64 = 1;
|
||||
/// A packed set of 8KB pages.
|
||||
pub(crate) const CHAPTER_PAGES: u64 = 2;
|
||||
/// An index of pages.
|
||||
pub(crate) const CHAPTER_PAGE_INDEX: u64 = 3;
|
||||
|
||||
// FIXME: move serialized data structs to a separate file.
|
||||
|
||||
/// An index from page number to offset within the pages chapter.
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Versioned, UpgradeLatest)]
|
||||
pub struct PageIndexV1 {
|
||||
/// A map from page number to file offset.
|
||||
pub(crate) map: BTreeMap<u64, u64>,
|
||||
}
|
||||
|
||||
// A placeholder type, that will always point to the latest version.
|
||||
pub type PageIndex = PageIndexV1;
|
||||
|
||||
// Each message gets a unique message id, for tracking by the aversion traits.
|
||||
assign_message_ids! {
|
||||
PageIndex: 100,
|
||||
}
|
||||
Reference in New Issue
Block a user