Skip to content

Commit

Permalink
revert obsolete changes and simplify merk cache
Browse files Browse the repository at this point in the history
  • Loading branch information
fominok committed Dec 5, 2024
1 parent 8da0d5a commit fda42a1
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 1,029 deletions.
193 changes: 36 additions & 157 deletions grovedb/src/element/insert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ use grovedb_version::{
};
use integer_encoding::VarInt;

use super::CascadeOnUpdate;
use crate::{reference_path::ReferencePathType, Element, Error, Hash};
use crate::{Element, Element::SumItem, Error, Hash};

impl Element {
#[cfg(feature = "full")]
Expand All @@ -23,48 +22,29 @@ impl Element {
/// If transaction is passed, the operation will be committed on the
/// transaction commit.
pub fn insert<'db, K: AsRef<[u8]>, S: StorageContext<'db>>(
self,
&self,
merk: &mut Merk<S>,
key: K,
options: Option<MerkOptions>,
grove_version: &GroveVersion,
) -> CostResult<(), Error> {
check_grovedb_v0_with_cost!("insert", grove_version.grovedb_versions.element.insert);

let mut original_cost = Default::default();

let to_insert = if let Some(mut prev) = cost_return_on_error!(
&mut original_cost,
Self::get_optional_from_storage(&merk.storage, key.as_ref(), grove_version)
) {
cost_return_on_error_no_add!(
original_cost,
self.promote_to_referenced_variant(&mut prev)
)
} else {
self
};

let serialized = cost_return_on_error_default!(to_insert.serialize(grove_version));
let serialized = cost_return_on_error_default!(self.serialize(grove_version));

if !merk.is_sum_tree && to_insert.is_sum_item() {
if !merk.is_sum_tree && self.is_sum_item() {
return Err(Error::InvalidInput("cannot add sum item to non sum tree"))
.wrap_with_cost(original_cost);
.wrap_with_cost(Default::default());
}

let merk_feature_type =
cost_return_on_error_default!(to_insert.get_feature_type(merk.is_sum_tree));
let batch_operations = if matches!(
to_insert,
Element::SumItem(..) | Element::SumItemWithBackwardsReferences(..)
) {
let value_cost = cost_return_on_error_no_add!(
original_cost,
to_insert.get_specialized_cost(grove_version)
);
cost_return_on_error_default!(self.get_feature_type(merk.is_sum_tree));
let batch_operations = if matches!(self, SumItem(..)) {
let value_cost =
cost_return_on_error_default!(self.get_specialized_cost(grove_version));

let cost = value_cost
+ to_insert.get_flags().as_ref().map_or(0, |flags| {
+ self.get_flags().as_ref().map_or(0, |flags| {
let flags_len = flags.len() as u32;
flags_len + flags_len.required_space() as u32
});
Expand All @@ -89,7 +69,6 @@ impl Element {
grove_version,
)
.map_err(|e| Error::CorruptedData(e.to_string()))
.add_cost(original_cost)
}

#[cfg(feature = "full")]
Expand All @@ -115,7 +94,7 @@ impl Element {
Err(e) => return Err(e).wrap_with_cost(Default::default()),
};

let entry = if matches!(self, Element::SumItem(..)) {
let entry = if matches!(self, SumItem(..)) {
let value_cost =
cost_return_on_error_default!(self.get_specialized_cost(grove_version));

Expand All @@ -142,7 +121,7 @@ impl Element {
/// If transaction is passed, the operation will be committed on the
/// transaction commit.
pub fn insert_if_not_exists<'db, S: StorageContext<'db>>(
self,
&self,
merk: &mut Merk<S>,
key: &[u8],
options: Option<MerkOptions>,
Expand Down Expand Up @@ -210,52 +189,21 @@ impl Element {
}
}

#[cfg(feature = "full")]
/// Promote `Element` to referenced variant in case the old one was already
/// referenced.
fn promote_to_referenced_variant(self, old_element: &mut Element) -> Result<Element, Error> {
if let Some(refs) = old_element.take_backward_references() {
// Since variants with backward references are publicly available, we still have
// to address them, meaning filling in the actual information about references
// from the database by discarding user input.

match self {
Element::Item(value, flags)
| Element::ItemWithBackwardsReferences(value, _, flags) => {
Ok(Element::ItemWithBackwardsReferences(value, refs, flags))
}
Element::Reference(ref_path, max_hops, flags)
| Element::BidirectionalReference(ref_path, _, max_hops, flags) => Ok(
Element::BidirectionalReference(ref_path, refs, max_hops, flags),
),
Element::SumItem(sum, flags)
| Element::SumItemWithBackwardsReferences(sum, _, flags) => {
Ok(Element::SumItemWithBackwardsReferences(sum, refs, flags))
}

Element::Tree(..) | Element::SumTree(..) => Err(Error::NotSupported(
"cannot insert subtree in place of a referenced item".to_owned(),
)),
}
} else {
Ok(self)
}
}

#[cfg(feature = "full")]
/// Insert an element in Merk under a key if the value is different from
/// what already exists; path should be resolved and proper Merk should
/// be loaded by this moment If transaction is not passed, the batch
/// will be written immediately. If transaction is passed, the operation
/// will be committed on the transaction commit.
/// If the value changed, it returns the old element under `Some`.
/// The bool represents if we indeed inserted.
/// If the value changed we return the old element.
pub fn insert_if_changed_value<'db, S: StorageContext<'db>>(
self,
&self,
merk: &mut Merk<S>,
key: &[u8],
options: Option<MerkOptions>,
grove_version: &GroveVersion,
) -> CostResult<Option<Element>, Error> {
) -> CostResult<(bool, Option<Element>), Error> {
check_grovedb_v0_with_cost!(
"insert_if_changed_value",
grove_version
Expand All @@ -265,29 +213,19 @@ impl Element {
);

let mut cost = OperationCost::default();
let mut previous_element = cost_return_on_error!(
let previous_element = cost_return_on_error!(
&mut cost,
Self::get_optional_from_storage(&merk.storage, key, grove_version)
);
let to_insert = if let Some(prev) = previous_element.as_mut() {
cost_return_on_error_no_add!(cost, self.promote_to_referenced_variant(prev))
} else {
self
let needs_insert = match &previous_element {
None => true,
Some(previous_element) => previous_element != self,
};

let changed = previous_element
.as_ref()
.map(|p| !p.eq_no_backreferences(&to_insert))
.unwrap_or(true);

if changed {
cost_return_on_error!(
&mut cost,
to_insert.insert(merk, key, options, grove_version)
);
Ok(previous_element).wrap_with_cost(cost)
if !needs_insert {
Ok((false, None)).wrap_with_cost(cost)
} else {
Ok(None).wrap_with_cost(cost)
cost_return_on_error!(&mut cost, self.insert(merk, key, options, grove_version));
Ok((true, previous_element)).wrap_with_cost(cost)
}
}

Expand Down Expand Up @@ -347,43 +285,28 @@ impl Element {
/// If transaction is not passed, the batch will be written immediately.
/// If transaction is passed, the operation will be committed on the
/// transaction commit.
/// Returns `bool` that indicates if a reference propagation is required.
pub fn insert_reference<'db, K: AsRef<[u8]>, S: StorageContext<'db>>(
self,
&self,
merk: &mut Merk<S>,
key: K,
referenced_value: Hash,
options: Option<MerkOptions>,
grove_version: &GroveVersion,
) -> CostResult<bool, Error> {
) -> CostResult<(), Error> {
check_grovedb_v0_with_cost!(
"insert_reference",
grove_version.grovedb_versions.element.insert_reference
);

let mut cost = Default::default();

let (ref_updated, to_insert) = if let Some(mut prev) = cost_return_on_error!(
&mut cost,
Self::get_optional_from_storage(&merk.storage, key.as_ref(), grove_version)
) {
(
!prev.eq_no_backreferences(&self) && prev.has_backward_references(),
cost_return_on_error_no_add!(cost, self.promote_to_referenced_variant(&mut prev)),
)
} else {
(false, self)
};

let serialized = match to_insert.serialize(grove_version) {
let serialized = match self.serialize(grove_version) {
Ok(s) => s,
Err(e) => return Err(e).wrap_with_cost(cost),
Err(e) => return Err(e).wrap_with_cost(Default::default()),
};

let mut cost = OperationCost::default();
let merk_feature_type = cost_return_on_error!(
&mut cost,
to_insert
.get_feature_type(merk.is_sum_tree)
self.get_feature_type(merk.is_sum_tree)
.wrap_with_cost(OperationCost::default())
);

Expand All @@ -404,7 +327,6 @@ impl Element {
grove_version,
)
.map_err(|e| Error::CorruptedData(e.to_string()))
.map_ok(|_| ref_updated)
}

#[cfg(feature = "full")]
Expand Down Expand Up @@ -541,52 +463,6 @@ impl Element {
batch_operations.push(entry);
Ok(()).wrap_with_cost(Default::default())
}

/// Adds info on reference that points to this element.
pub(crate) fn referenced_from(
mut self,
reference: ReferencePathType,
cascade_on_update: CascadeOnUpdate,
) -> Result<Self, Error> {
match self {
Element::Item(data, flags) => Ok(Element::ItemWithBackwardsReferences(
data,
vec![(reference, cascade_on_update)],
flags,
)),
Element::ItemWithBackwardsReferences(_, ref mut backward_references, _) => {
backward_references.push((reference, cascade_on_update));
Ok(self)
}
Element::Reference(reference_path, max_hops, flags) => {
Ok(Element::BidirectionalReference(
reference_path,
vec![(reference, cascade_on_update)],
max_hops,
flags,
))
}
Element::BidirectionalReference(_, ref mut backward_references, ..) => {
backward_references.push((reference, cascade_on_update));
Ok(self)
}
Element::SumItem(value, flags) => Ok(Element::SumItemWithBackwardsReferences(
value,
vec![(reference, cascade_on_update)],
flags,
)),
Element::SumItemWithBackwardsReferences(_, ref mut backward_references, _) => {
backward_references.push((reference, cascade_on_update));
Ok(self)
}
Element::Tree(..) => Err(Error::NotSupported(
"Cannot add references pointing to subtrees".to_owned(),
)),
Element::SumTree(..) => Err(Error::NotSupported(
"Cannot add references pointing to sumtrees".to_owned(),
)),
}
}
}

#[cfg(feature = "full")]
Expand Down Expand Up @@ -634,13 +510,14 @@ mod tests {

merk.commit(grove_version);

let previous = Element::new_item(b"value".to_vec())
let (inserted, previous) = Element::new_item(b"value".to_vec())
.insert_if_changed_value(&mut merk, b"another-key", None, grove_version)
.unwrap()
.expect("expected successful insertion 2");

merk.commit(grove_version);

assert!(!inserted);
assert_eq!(previous, None);
assert_eq!(
Element::get(&merk, b"another-key", true, grove_version)
Expand Down Expand Up @@ -675,11 +552,12 @@ mod tests {

let batch = StorageBatch::new();
let mut merk = empty_path_merk(&*storage, &batch, &tx, grove_version);
let previous = Element::new_item(b"value2".to_vec())
let (inserted, previous) = Element::new_item(b"value2".to_vec())
.insert_if_changed_value(&mut merk, b"another-key", None, grove_version)
.unwrap()
.expect("expected successful insertion 2");

assert!(inserted);
assert_eq!(previous, Some(Element::new_item(b"value".to_vec())),);

storage
Expand All @@ -704,11 +582,12 @@ mod tests {
.insert(&mut merk, b"mykey", None, grove_version)
.unwrap()
.expect("expected successful insertion");
let previous = Element::new_item(b"value2".to_vec())
let (inserted, previous) = Element::new_item(b"value2".to_vec())
.insert_if_changed_value(&mut merk, b"another-key", None, grove_version)
.unwrap()
.expect("expected successful insertion 2");

assert!(inserted);
assert_eq!(previous, None);

assert_eq!(
Expand Down
27 changes: 1 addition & 26 deletions grovedb/src/element/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ pub use query::QueryOptions;
mod serialize;
#[cfg(any(feature = "full", feature = "verify"))]
use std::fmt;
use std::mem;

use bincode::{Decode, Encode};
#[cfg(any(feature = "full", feature = "verify"))]
Expand Down Expand Up @@ -93,7 +92,7 @@ pub enum Element {
/// A reference to an object by its path
BidirectionalReference(
ReferencePathType,
Vec<(ReferencePathType, CascadeOnUpdate)>,
Option<(ReferencePathType, CascadeOnUpdate)>,
MaxReferenceHop,
Option<ElementFlags>,
),
Expand Down Expand Up @@ -308,30 +307,6 @@ impl Element {
crate::value_hash(&bytes).map(Result::Ok)
}

/// Returns backward references if the `Element` in question participates in
/// bidirectional referencing machinery.
pub(crate) fn take_backward_references(
&mut self,
) -> Option<Vec<(ReferencePathType, CascadeOnUpdate)>> {
match self {
Element::BidirectionalReference(_, refs, ..)
| Element::ItemWithBackwardsReferences(_, refs, ..)
| Element::SumItemWithBackwardsReferences(_, refs, ..)
if !refs.is_empty() =>
{
Some(mem::take(refs))
}
_ => None,
}
}

/// Returns true if there are references to this `Element`.
pub(crate) fn has_backward_references(&self) -> bool {
matches!(self, Element::BidirectionalReference(_, refs, ..)
| Element::ItemWithBackwardsReferences(_, refs, ..)
| Element::SumItemWithBackwardsReferences(_, refs, ..) if !refs.is_empty())
}

/// Checks elements for equality ignoring backreferences part.
pub(crate) fn eq_no_backreferences(&self, other: &Self) -> bool {

Check warning on line 311 in grovedb/src/element/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

methods `eq_no_backreferences` and `cut_backreferences` are never used

warning: methods `eq_no_backreferences` and `cut_backreferences` are never used --> grovedb/src/element/mod.rs:311:19 | 285 | impl Element { | ------------ methods in this implementation ... 311 | pub(crate) fn eq_no_backreferences(&self, other: &Self) -> bool { | ^^^^^^^^^^^^^^^^^^^^ ... 346 | pub(crate) fn cut_backreferences(self) -> Self { | ^^^^^^^^^^^^^^^^^^
use Element::*;
Expand Down
Loading

0 comments on commit fda42a1

Please sign in to comment.