Skip to content

Commit

Permalink
API implementation part 3 (#308)
Browse files Browse the repository at this point in the history
  • Loading branch information
rkuris authored Oct 6, 2023
1 parent de377f1 commit 4fd35a4
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 16 deletions.
2 changes: 1 addition & 1 deletion firewood/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ parking_lot = "0.12.1"
serde = { version = "1.0", features = ["derive"] }
sha3 = "0.10.2"
thiserror = "1.0.38"
tokio = { version = "1.21.1", features = ["rt", "sync", "macros"] }
tokio = { version = "1.21.1", features = ["rt", "sync", "macros", "rt-multi-thread"] }
typed-builder = "0.16.0"
bincode = "1.3.3"

Expand Down
4 changes: 3 additions & 1 deletion firewood/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use std::{
sync::Arc,
thread::JoinHandle,
};
use tokio::task::block_in_place;

mod proposal;

Expand Down Expand Up @@ -390,7 +391,8 @@ impl api::Db for Db {
type Proposal = Proposal;

async fn revision(&self, root_hash: HashKey) -> Result<Arc<Self::Historical>, api::Error> {
if let Some(rev) = self.get_revision(&TrieHash(root_hash)) {
let rev = block_in_place(|| self.get_revision(&TrieHash(root_hash)));
if let Some(rev) = rev {
Ok(Arc::new(rev.rev))
} else {
Err(api::Error::HashNotFound {
Expand Down
7 changes: 4 additions & 3 deletions firewood/src/db/proposal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use async_trait::async_trait;
use parking_lot::{Mutex, RwLock};
use shale::CachedStore;
use std::{io::ErrorKind, sync::Arc};
use tokio::task::block_in_place;

pub use crate::v2::api::{Batch, BatchOp};

Expand Down Expand Up @@ -43,11 +44,11 @@ pub enum ProposalBase {
}

#[async_trait]
impl<T: crate::v2::api::DbView> crate::v2::api::Proposal<T> for Proposal {
impl crate::v2::api::Proposal for Proposal {
type Proposal = Proposal;

async fn commit(self: Arc<Self>) -> Result<Arc<T>, api::Error> {
todo!()
async fn commit(self: Arc<Self>) -> Result<(), api::Error> {
block_in_place(|| self.commit_sync().map_err(Into::into))
}

async fn propose<K: api::KeyType, V: api::ValueType>(
Expand Down
12 changes: 4 additions & 8 deletions firewood/src/v2/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ pub struct Proof<N>(pub HashMap<HashKey, N>);
pub trait Db {
type Historical: DbView;

type Proposal: DbView + Proposal<Self::Historical>;
type Proposal: DbView + Proposal;

/// Get a reference to a specific view based on a hash
///
Expand Down Expand Up @@ -172,15 +172,11 @@ pub trait DbView {
/// [DbView], which means you can fetch values from it or
/// obtain proofs.
#[async_trait]
pub trait Proposal<T: DbView>: DbView {
type Proposal: DbView + Proposal<T>;
pub trait Proposal: DbView {
type Proposal: DbView + Proposal;

/// Commit this revision
///
/// # Return value
///
/// * A reference to a new historical view
async fn commit(self: Arc<Self>) -> Result<Arc<T>, Error>;
async fn commit(self: Arc<Self>) -> Result<(), Error>;

/// Propose a new revision on top of an existing proposal
///
Expand Down
6 changes: 3 additions & 3 deletions firewood/src/v2/propose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ impl<T: api::DbView + Send + Sync> api::DbView for Proposal<T> {
}

#[async_trait]
impl<T: api::DbView + Send + Sync> api::Proposal<T> for Proposal<T> {
impl<T: api::DbView + Send + Sync> api::Proposal for Proposal<T> {
type Proposal = Proposal<T>;

async fn propose<K: KeyType, V: ValueType>(
Expand All @@ -150,12 +150,12 @@ impl<T: api::DbView + Send + Sync> api::Proposal<T> for Proposal<T> {
Ok(Proposal::new(ProposalBase::Proposal(self), data))
}

async fn commit(self: Arc<Self>) -> Result<Arc<T>, api::Error> {
async fn commit(self: Arc<Self>) -> Result<(), api::Error> {
// TODO: commit should modify the db; this will only work for
// emptydb at the moment
match &self.base {
ProposalBase::Proposal(base) => base.clone().commit().await,
ProposalBase::View(v) => Ok(v.clone()),
ProposalBase::View(_) => Ok(()),
}
}
}
Expand Down
70 changes: 70 additions & 0 deletions firewood/tests/v2api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (C) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE.md for licensing terms.

use std::{error::Error, path::PathBuf, sync::Arc};

use firewood::{
db::{BatchOp, Db as PersistedDb, DbConfig, DbError, WalConfig},
v2::api::{Db, DbView, Proposal},
};

#[tokio::test(flavor = "multi_thread")]
async fn smoke() -> Result<(), Box<dyn Error>> {
let cfg = DbConfig::builder()
.meta_ncached_pages(1024)
.meta_ncached_files(128)
.payload_ncached_pages(1024)
.payload_ncached_files(128)
.payload_file_nbit(16)
.payload_regn_nbit(16)
.wal(
WalConfig::builder()
.file_nbit(15)
.block_nbit(8)
.max_revisions(10)
.build(),
)
.truncate(true)
.build();
let db = Arc::new(testdb(cfg).await?);
let empty_hash = db.root_hash().await?;
assert_ne!(empty_hash, [0; 32]);

// insert a single key/value
let (key, value) = (b"smoke", b"test");
let batch_put = BatchOp::Put { key, value };
let proposal: Arc<firewood::db::Proposal> = db.propose(vec![batch_put]).await?.into();
proposal.commit().await?;

// ensure the latest hash is different
let latest = db.root_hash().await?;
assert_ne!(empty_hash, latest);

// fetch the view of the latest
let view = db.revision(latest).await.unwrap();

// check that the key/value is there
let got_value = view.val(key).await.unwrap().unwrap();
assert_eq!(got_value, value);

// TODO: also fetch view of empty; this currently does not work, as you can't reference
// the empty hash
// let empty_view = db.revision(empty_hash).await.unwrap();
// let value = empty_view.val(b"smoke").await.unwrap();
// assert_eq!(value, None);

Ok(())
}

async fn testdb(cfg: DbConfig) -> Result<PersistedDb, DbError> {
let tmpdbpath = tmp_dir().join("testdb");
tokio::task::spawn_blocking(move || PersistedDb::new(tmpdbpath, &cfg))
.await
.unwrap()
}

fn tmp_dir() -> PathBuf {
option_env!("CARGO_TARGET_TMPDIR")
.map(PathBuf::from)
.unwrap_or(std::env::temp_dir())
}

0 comments on commit 4fd35a4

Please sign in to comment.