Skip to content

Commit

Permalink
Implement reopening an existing database
Browse files Browse the repository at this point in the history
Also added some tests to validate proposal reads and test reopen
  • Loading branch information
rkuris committed Aug 27, 2024
1 parent c7c87ac commit 9b496ce
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 13 deletions.
71 changes: 65 additions & 6 deletions firewood/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,10 @@ mod test {

use crate::{
db::Db,
v2::api::{Db as _, Error, Proposal},
v2::api::{Db as _, DbView as _, Error, Proposal as _},
};

use super::DbConfig;
use super::{BatchOp, DbConfig};

#[tokio::test]
async fn test_cloned_proposal_error() {
Expand All @@ -330,10 +330,53 @@ mod test {
assert!(matches!(result, Ok(())), "{result:?}");
}

#[tokio::test]
async fn test_proposal_reads() {
let db = testdb().await;
let batch = vec![BatchOp::Put {
key: b"k",
value: b"v",
}];
let proposal = db.propose(batch).await.unwrap();
assert_eq!(&*proposal.val(b"k").await.unwrap().unwrap(), b"v");

assert_eq!(proposal.val(b"notfound").await.unwrap(), None);
proposal.commit().await.unwrap();

let batch = vec![BatchOp::Put {
key: b"k",
value: b"v2",
}];
let proposal = db.propose(batch).await.unwrap();
assert_eq!(&*proposal.val(b"k").await.unwrap().unwrap(), b"v2");

let committed = db.root_hash().await.unwrap().unwrap();
let historical = db.revision(committed).await.unwrap();
assert_eq!(&*historical.val(b"k").await.unwrap().unwrap(), b"v");
}

#[tokio::test]
async fn reopen_test() {
let db = testdb().await;
let batch = vec![BatchOp::Put {
key: b"k",
value: b"v",
}];
let proposal = db.propose(batch).await.unwrap();
proposal.commit().await.unwrap();
println!("{:?}", db.root_hash().await.unwrap().unwrap());

let db = db.reopen().await;
println!("{:?}", db.root_hash().await.unwrap().unwrap());
let committed = db.root_hash().await.unwrap().unwrap();
let historical = db.revision(committed).await.unwrap();
assert_eq!(&*historical.val(b"k").await.unwrap().unwrap(), b"v");
}

// Testdb is a helper struct for testing the Db. Once it's dropped, the directory and file disappear
struct TestDb {
db: Db,
_tmpdir: tempfile::TempDir,
tmpdir: tempfile::TempDir,
}
impl Deref for TestDb {
type Target = Db;
Expand All @@ -354,9 +397,25 @@ mod test {
.collect();
let dbconfig = DbConfig::builder().truncate(true).build();
let db = Db::new(dbpath, dbconfig).await.unwrap();
TestDb {
db,
_tmpdir: tmpdir,
TestDb { db, tmpdir }
}

impl TestDb {
fn path(&self) -> PathBuf {
[self.tmpdir.path().to_path_buf(), PathBuf::from("testdb")]
.iter()
.collect()
}
async fn reopen(self) -> Self {
let path = self.path();
drop(self.db);
let dbconfig = DbConfig::builder().truncate(false).build();

let db = Db::new(path, dbconfig).await.unwrap();
TestDb {
db,
tmpdir: self.tmpdir,
}
}
}
}
15 changes: 12 additions & 3 deletions firewood/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,24 @@ impl RevisionManager {
config: RevisionManagerConfig,
) -> Result<Self, Error> {
let storage = Arc::new(FileBacked::new(filename, config.node_cache_size, truncate)?);
let nodestore = Arc::new(NodeStore::new_empty_committed(storage.clone())?);
let manager = Self {
let nodestore = match truncate {
true => Arc::new(NodeStore::new_empty_committed(storage.clone())?),
false => Arc::new(NodeStore::open(storage.clone())?),
};
let mut manager = Self {
max_revisions: config.max_revisions,
filebacked: storage,
historical: VecDeque::from([nodestore]),
historical: VecDeque::from([nodestore.clone()]),
by_hash: Default::default(),
proposals: Default::default(),
// committing_proposals: Default::default(),
};
if nodestore.kind.root_hash().is_some() {
manager.by_hash.insert(
nodestore.kind.root_hash().expect("root hash is present"),
nodestore.clone(),
);
}
Ok(manager)
}

Expand Down
17 changes: 13 additions & 4 deletions storage/src/nodestore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,14 +196,22 @@ impl<S: ReadableStorage> NodeStore<Committed, S> {

drop(stream);

Ok(Self {
let mut nodestore = Self {
header,
kind: Committed {
deleted: Default::default(),
root_hash: None, // TODO: get the root hash
root_hash: None,
},
storage,
})
};

if let Some(root_address) = nodestore.header.root_address {
let node = nodestore.read_node_from_disk(root_address);
let root_hash = node.map(|n| hash_node(&n, &Path(Default::default())))?;
nodestore.kind.root_hash = Some(root_hash);
}

Ok(nodestore)
}

/// Create a new, empty, Committed [NodeStore] and clobber
Expand Down Expand Up @@ -1047,9 +1055,10 @@ impl Hashed for Arc<ImmutableProposal> {}

impl<T: ReadInMemoryNode + Hashed, S: ReadableStorage> RootReader for NodeStore<T, S> {
fn root_node(&self) -> Option<Arc<Node>> {
// TODO: If the read_node fails, we just say there is no root; this is incorrect
self.header
.root_address
.and_then(|addr| self.kind.read_in_memory_node(addr))
.and_then(|addr| self.read_node(addr).ok())
}
}

Expand Down

0 comments on commit 9b496ce

Please sign in to comment.