diff --git a/Cargo.lock b/Cargo.lock index 68c691ff47..0e1cc0005d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1972,6 +1972,7 @@ dependencies = [ "anyhow", "aversion", "bookfile", + "hex", "rand", "serde", "structopt", diff --git a/snapfile/Cargo.toml b/snapfile/Cargo.toml index 29329a224d..021818e532 100644 --- a/snapfile/Cargo.toml +++ b/snapfile/Cargo.toml @@ -15,6 +15,7 @@ serde = { version = "1.0", features = ["derive"] } rand = "0.8.3" structopt = "0.3" zenith_utils = { path = "../zenith_utils" } +hex = "0.4.3" [dev-dependencies] tempfile = "3.2" diff --git a/snapfile/src/lib.rs b/snapfile/src/lib.rs index efcf6b93b4..a32a1e43b5 100644 --- a/snapfile/src/lib.rs +++ b/snapfile/src/lib.rs @@ -27,23 +27,27 @@ pub use versioned::{PageIndex, PageLocation, Predecessor, SnapFileMeta}; use zenith_utils::lsn::Lsn; impl SnapFileMeta { - pub fn new(previous: Option, lsn: Lsn) -> Self { + pub fn new(previous: Option, timeline: [u8; 16], lsn: Lsn) -> Self { // Store the metadata of the predecessor snapshot, if there is one. let predecessor = previous.map(|prev| Predecessor { - id: prev.snap_id, + timeline: prev.timeline, lsn: prev.lsn, }); - let snap_id: u64 = rand::random(); SnapFileMeta { - snap_id, + timeline, predecessor, lsn: lsn.into(), } } fn to_filename(&self) -> OsString { - format!("{:x}.zdb", self.snap_id).into() + let timeline_string = hex::encode(self.timeline); + let pred_lsn = match &self.predecessor { + None => 0, + Some(pred) => pred.lsn, + }; + format!("{}_{:x}_{:x}.zdb", timeline_string, pred_lsn, self.lsn).into() } } @@ -266,13 +270,15 @@ mod tests { use std::path::PathBuf; use tempfile::TempDir; + const TEST_TIMELINE: [u8; 16] = [99u8; 16]; + #[test] fn snap_two_pages() { // When `dir` goes out of scope the directory will be unlinked. let dir = TempDir::new().unwrap(); let snap_meta = { // Write out a new snapshot file with two pages. - let meta = SnapFileMeta::new(None, Lsn(1234)); + let meta = SnapFileMeta::new(None, TEST_TIMELINE, Lsn(1234)); let mut snap = SnapWriter::new(dir.path(), meta).unwrap(); // Write the pages out of order, because why not? let page99 = [99u8; 8192]; @@ -313,7 +319,7 @@ mod tests { let dir = TempDir::new().unwrap(); let snap_meta = { // Write out a new snapshot file with no pages. - let meta = SnapFileMeta::new(None, Lsn(1234)); + let meta = SnapFileMeta::new(None, TEST_TIMELINE, Lsn(1234)); let snap = SnapWriter::new(dir.path(), meta).unwrap(); snap.finish().unwrap() }; diff --git a/snapfile/src/squash.rs b/snapfile/src/squash.rs index 11b8665f1f..905d821017 100644 --- a/snapfile/src/squash.rs +++ b/snapfile/src/squash.rs @@ -42,7 +42,7 @@ pub fn squash(older: &Path, newer: &Path, out_dir: &Path) -> Result<()> { // Check that snap1 is the predecessor of snap2. match meta2.predecessor { - Some(pred) if pred.id == meta1.snap_id => {} + Some(pred) if pred.timeline == meta1.timeline => {} _ => { bail!( "snap file {:?} is not the predecessor of {:?}", @@ -55,11 +55,12 @@ pub fn squash(older: &Path, newer: &Path, out_dir: &Path) -> Result<()> { // The new combined snapshot will have most fields from meta2 (the later // snapshot), but will have the predecessor from meta1. let new_meta = SnapFileMeta { - // FIXME: Wow, this seems wrong. Need to sort out what to do here. - // should snap_id even exist? The fact that we plan on squashing - // snapshots often implies that maybe they shouldn't. - // How do we identify predecessor? timeline_id + lsn? - snap_id: meta2.snap_id, + // There is some danger in squashing snapshots across two timelines, + // in that it's possible to get confused about what the history + // looks like. Ultimately, it should be possible to squash our way + // to a "complete" snapshot (that contains all pages), so this must + // be possible. + timeline: meta2.timeline, predecessor: meta1.predecessor, lsn: meta2.lsn, }; diff --git a/snapfile/src/versioned.rs b/snapfile/src/versioned.rs index 0391714aca..f76f775b93 100644 --- a/snapfile/src/versioned.rs +++ b/snapfile/src/versioned.rs @@ -30,21 +30,22 @@ pub(crate) const CHAPTER_PAGE_INDEX: u64 = 3; /// of that snapshot. #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct Predecessor { - /// This is the id number of the previous snapshot. + /// This is the ID number of the predecessor timeline. /// - /// This must match the snap_id of the previous snapshot. - pub id: u64, + /// This may match the current snapshot's timeline id, but + /// it may not (if the precessor was the branch point). + pub timeline: [u8; 16], - /// This is the LSN of the previous snapshot. + /// This is the LSN of the predecessor snapshot. pub lsn: u64, } #[derive(Debug, PartialEq, Serialize, Deserialize, Versioned, UpgradeLatest)] pub struct SnapFileMetaV1 { - /// This is a unique ID number for this snapshot. + /// This is a unique ID number for this timeline. /// /// This number guarantees that snapshot history is unique. - pub snap_id: u64, + pub timeline: [u8; 16], /// Information about the predecessor snapshot. ///