use firewood::api::{self, BoxKeyValueIter, DbView, HashKey, IntoBatchIter, Proposal as _};
use crate::{IteratorHandle, iterator::CreateIteratorResult, metrics::MetricsContextExt};
use firewood_metrics::{firewood_increment, firewood_record, fwd_expensive_timed_result};
#[derive(Debug)]
pub struct ProposalHandle<'db> {
hash_key: Option<HashKey>,
proposal: firewood::db::Proposal<'db>,
handle: &'db crate::DatabaseHandle,
}
impl<'db> DbView for ProposalHandle<'db> {
type Iter<'view>
= <firewood::db::Proposal<'db> as DbView>::Iter<'view>
where
Self: 'view;
fn root_hash(&self) -> Option<HashKey> {
self.proposal.root_hash()
}
fn val<K: api::KeyType>(&self, key: K) -> Result<Option<firewood::merkle::Value>, api::Error> {
self.proposal.val(key)
}
fn single_key_proof<K: api::KeyType>(&self, key: K) -> Result<api::FrozenProof, api::Error> {
self.proposal.single_key_proof(key)
}
fn range_proof<K: api::KeyType>(
&self,
first_key: Option<K>,
last_key: Option<K>,
limit: Option<std::num::NonZeroUsize>,
) -> Result<api::FrozenRangeProof, api::Error> {
self.proposal.range_proof(first_key, last_key, limit)
}
fn iter_option<K: api::KeyType>(
&self,
first_key: Option<K>,
) -> Result<Self::Iter<'_>, api::Error> {
self.proposal.iter_option(first_key)
}
fn dump_to_string(&self) -> Result<String, api::Error> {
self.proposal.dump_to_string()
}
}
impl ProposalHandle<'_> {
#[must_use]
pub fn hash_key(&self) -> Option<crate::HashKey> {
self.hash_key.clone().map(Into::into)
}
pub fn commit_proposal(self) -> Result<Option<HashKey>, api::Error> {
let ProposalHandle {
hash_key,
proposal,
handle,
} = self;
if let Some(ref hash_key) = hash_key {
_ = handle.get_root(hash_key.clone());
}
let (commit_result, commit_time) =
fwd_expensive_timed_result!(crate::registry::COMMIT_MS_BUCKET, proposal.commit());
commit_result?;
handle.clear_cached_view();
firewood_increment!(crate::registry::COMMIT_MS, commit_time.as_millis() as u64);
firewood_increment!(crate::registry::COMMIT_COUNT, 1);
Ok(hash_key)
}
#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn iter_from(&self, first_key: Option<&[u8]>) -> CreateIteratorResult<'_> {
let it = self
.iter_option(first_key)
.expect("infallible; see issue #1329");
CreateIteratorResult(IteratorHandle::new(
self.proposal.view(),
Box::new(it) as BoxKeyValueIter<'_>,
self.handle.metrics_context(),
))
}
}
#[derive(Debug)]
pub struct CreateProposalResult<'db> {
pub handle: ProposalHandle<'db>,
}
impl<'db> CreateProposalResult<'db> {
pub(crate) fn new(
handle: &'db crate::DatabaseHandle,
f: impl FnOnce() -> Result<firewood::db::Proposal<'db>, api::Error>,
) -> Result<Self, api::Error> {
let (proposal_result, propose_time) =
fwd_expensive_timed_result!(crate::registry::PROPOSE_MS_BUCKET, f());
let proposal = proposal_result?;
firewood_increment!(crate::registry::PROPOSE_MS, propose_time.as_millis() as u64);
firewood_increment!(crate::registry::PROPOSE_COUNT, 1);
firewood_record!(
crate::registry::PROPOSE_MS_BUCKET,
propose_time.as_secs_f64() * 1000.0,
expensive
);
let hash_key = proposal.root_hash();
Ok(CreateProposalResult {
handle: ProposalHandle {
hash_key,
proposal,
handle,
},
})
}
}
pub trait CView<'db> {
fn handle(&self) -> &'db crate::DatabaseHandle;
fn create_proposal(
self,
values: impl IntoBatchIter,
) -> Result<firewood::db::Proposal<'db>, api::Error>;
fn create_proposal_handle(
self,
values: impl IntoBatchIter,
) -> Result<CreateProposalResult<'db>, api::Error>
where
Self: Sized,
{
let handle = self.handle();
CreateProposalResult::new(handle, || self.create_proposal(values))
}
}
impl<'db> CView<'db> for &ProposalHandle<'db> {
fn handle(&self) -> &'db crate::DatabaseHandle {
self.handle
}
fn create_proposal(
self,
values: impl IntoBatchIter,
) -> Result<firewood::db::Proposal<'db>, api::Error> {
self.proposal.propose(values)
}
}
impl crate::MetricsContextExt for ProposalHandle<'_> {
fn metrics_context(&self) -> Option<firewood_metrics::MetricsContext> {
self.handle.metrics_context()
}
}