Skip to content

Commit

Permalink
[feat]: Added a pattern matching iterator to the db
Browse files Browse the repository at this point in the history
  • Loading branch information
batconjurer committed Mar 7, 2024
1 parent 2535c9c commit f352a10
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 3 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 66 additions & 1 deletion crates/apps/src/lib/node/ledger/storage/rocksdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,11 @@ use namada::replay_protection;
use namada::state::merkle_tree::{base_tree_key_prefix, subtree_key_prefix};
use namada::state::{
BlockStateRead, BlockStateWrite, DBIter, DBWriteBatch, DbError as Error,
DbResult as Result, MerkleTreeStoresRead, PrefixIterator, StoreType, DB,
DbResult as Result, MerkleTreeStoresRead, PrefixIterator, PatternIterator, StoreType, DB,
};
use namada::token::ConversionState;
use rayon::prelude::*;
use regex::Regex;
use rocksdb::{
BlockBasedOptions, ColumnFamily, ColumnFamilyDescriptor, DBCompactionStyle,
DBCompressionType, Direction, FlushOptions, IteratorMode, Options,
Expand Down Expand Up @@ -1537,6 +1538,7 @@ impl DB for RocksDB {

impl<'iter> DBIter<'iter> for RocksDB {
type PrefixIter = PersistentPrefixIterator<'iter>;
type PatternIter = PersistentPatternIterator<'iter>;

fn iter_prefix(
&'iter self,
Expand All @@ -1545,6 +1547,14 @@ impl<'iter> DBIter<'iter> for RocksDB {
iter_subspace_prefix(self, prefix)
}

fn iter_pattern(
&'iter self,
prefix: Option<&Key>,
pattern: Regex,
) -> PersistentPatternIterator<'iter> {
iter_subspace_pattern(self, prefix, pattern)
}

fn iter_results(&'iter self) -> PersistentPrefixIterator<'iter> {
let db_prefix = "results/".to_owned();
let prefix = "results".to_owned();
Expand Down Expand Up @@ -1598,6 +1608,18 @@ fn iter_subspace_prefix<'iter>(
iter_prefix(db, subspace_cf, stripped_prefix, prefix)
}

fn iter_subspace_pattern<'iter>(
db: &'iter RocksDB,
prefix: Option<&Key>,
pattern: Regex,
) -> PersistentPatternIterator<'iter> {
let subspace_cf = db
.get_column_family(SUBSPACE_CF)
.expect("{SUBSPACE_CF} column family should exist");
let stripped_prefix = None;
iter_pattern(db, subspace_cf, stripped_prefix, prefix, pattern)
}

fn iter_diffs_prefix<'a>(
db: &'a RocksDB,
height: BlockHeight,
Expand Down Expand Up @@ -1650,6 +1672,24 @@ fn iter_prefix<'a>(
PersistentPrefixIterator(PrefixIterator::new(iter, stripped_prefix))
}

/// Create an iterator over key-vals in the given CF matching the given
/// pattern(s).
fn iter_pattern<'a>(
db: &'a RocksDB,
cf: &'a ColumnFamily,
stripped_prefix: Option<&Key>,
prefix: Option<&Key>,
pattern: Regex,
) -> PersistentPatternIterator<'a> {
PersistentPatternIterator {
inner: PatternIterator {
iter: iter_prefix(db, cf, stripped_prefix, prefix),
pattern,
},
finished: false,
}
}

#[derive(Debug)]
pub struct PersistentPrefixIterator<'a>(
PrefixIterator<rocksdb::DBIterator<'a>>,
Expand Down Expand Up @@ -1684,6 +1724,31 @@ impl<'a> Iterator for PersistentPrefixIterator<'a> {
}
}

#[derive(Debug)]
pub struct PersistentPatternIterator<'a> {
inner: PatternIterator<PersistentPrefixIterator<'a > >,
finished: bool,
}

impl<'a> Iterator for PersistentPatternIterator<'a> {
type Item = (String, Vec<u8>, u64);

/// Returns the next pair and the gas cost
fn next(&mut self) -> Option<(String, Vec<u8>, u64)> {
if self.finished {
return None;
}
loop {
let next_result = self.inner.iter.next()?;
if self.inner.pattern.is_match(&next_result.0) {
return Some(next_result)
} else {
self.finished = true;
}
}
}
}

/// Make read options for RocksDB iterator with the given prefix
fn make_iter_read_opts(prefix: Option<String>) -> ReadOptions {
let mut read_opts = ReadOptions::default();
Expand Down
1 change: 1 addition & 0 deletions crates/namada/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ prost.workspace = true
rand.workspace = true
rand_core.workspace = true
rayon = { version = "=1.5.3", optional = true }
regex.workspace = true
ripemd.workspace = true
serde.workspace = true
serde_json.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion crates/state/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub use namada_storage as storage;
pub use namada_storage::conversion_state::{
ConversionState, WithConversionState,
};
pub use namada_storage::types::{KVBytes, PrefixIterator};
pub use namada_storage::types::{KVBytes, PrefixIterator, PatternIterator};
pub use namada_storage::{
collections, iter_prefix, iter_prefix_bytes, iter_prefix_with_filter,
mockdb, tx_queue, BlockStateRead, BlockStateWrite, DBIter, DBWriteBatch,
Expand Down
1 change: 1 addition & 0 deletions crates/storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ borsh.workspace = true
itertools.workspace = true
thiserror.workspace = true
tracing.workspace = true
regex = "1.10.2"

[dev-dependencies]
namada_core = { path = "../core", features = ["testing"] }
10 changes: 10 additions & 0 deletions crates/storage/src/db.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::fmt::Debug;
use regex::Regex;

use namada_core::address::EstablishedAddressGen;
use namada_core::hash::{Error as HashError, Hash};
Expand Down Expand Up @@ -266,6 +267,7 @@ pub trait DB: Debug {
pub trait DBIter<'iter> {
/// The concrete type of the iterator
type PrefixIter: Debug + Iterator<Item = (String, Vec<u8>, u64)>;
type PatternIter: Debug + Iterator<Item = (String, Vec<u8>, u64)>;

/// WARNING: This only works for values that have been committed to DB.
/// To be able to see values written or deleted, but not yet committed,
Expand All @@ -275,6 +277,14 @@ pub trait DBIter<'iter> {
/// ordered by the storage keys.
fn iter_prefix(&'iter self, prefix: Option<&Key>) -> Self::PrefixIter;

/// WARNING: This only works for values that have been committed to DB.
/// To be able to see values written or deleted, but not yet committed,
/// use the `StorageWithWriteLog`.
///
/// Read account subspace key value pairs with the given pattern from the DB,
/// ordered by the storage keys.
fn iter_pattern(&'iter self, prefix: Option<&Key>, pattern: Regex) -> Self::PatternIter;

/// Read results subspace key value pairs from the DB
fn iter_results(&'iter self) -> Self::PrefixIter;

Expand Down
37 changes: 36 additions & 1 deletion crates/storage/src/mockdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::path::Path;
use std::str::FromStr;

use itertools::Either;
use regex::Regex;
use namada_core::borsh::{BorshDeserialize, BorshSerializeExt};
use namada_core::hash::Hash;
use namada_core::storage::{
Expand All @@ -25,7 +26,7 @@ use crate::db::{
BlockStateRead, BlockStateWrite, DBIter, DBWriteBatch, Error, Result, DB,
};
use crate::tx_queue::TxQueue;
use crate::types::{KVBytes, PrefixIterator};
use crate::types::{KVBytes, PatternIterator, PrefixIterator};

const SUBSPACE_CF: &str = "subspace";

Expand Down Expand Up @@ -678,6 +679,7 @@ impl DB for MockDB {

impl<'iter> DBIter<'iter> for MockDB {
type PrefixIter = MockPrefixIterator;
type PatternIter = MockPatternIterator;

fn iter_prefix(&'iter self, prefix: Option<&Key>) -> MockPrefixIterator {
let stripped_prefix = "subspace/".to_owned();
Expand All @@ -699,6 +701,14 @@ impl<'iter> DBIter<'iter> for MockDB {
MockPrefixIterator::new(MockIterator { prefix, iter }, stripped_prefix)
}

fn iter_pattern(&'iter self, prefix: Option<&Key>, pattern: Regex) -> Self::PatternIter {
MockPatternIterator {
inner: PatternIterator {iter: self.iter_prefix(prefix), pattern},
finished: false,
}
}


fn iter_results(&'iter self) -> MockPrefixIterator {
let stripped_prefix = "results/".to_owned();
let prefix = "results".to_owned();
Expand Down Expand Up @@ -808,6 +818,31 @@ impl Iterator for PrefixIterator<MockIterator> {
}
}

#[derive(Debug)]
pub struct MockPatternIterator {
inner: PatternIterator<MockPrefixIterator>,
finished: bool,
}

impl Iterator for MockPatternIterator {
type Item = (String, Vec<u8>, u64);

/// Returns the next pair and the gas cost
fn next(&mut self) -> Option<(String, Vec<u8>, u64)> {
if self.finished {
return None;
}
loop {
let next_result = self.inner.iter.next()?;
if self.inner.pattern.is_match(&next_result.0) {
return Some(next_result)
} else {
self.finished = true;
}
}
}
}

impl DBWriteBatch for MockDBWriteBatch {}

fn unknown_key_error(key: &str) -> Result<()> {
Expand Down
30 changes: 30 additions & 0 deletions crates/storage/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! The key and values that may be persisted in a DB.
use regex::Regex;

/// A key-value pair as raw bytes
pub type KVBytes = (Box<[u8]>, Box<[u8]>);

Expand Down Expand Up @@ -31,3 +33,31 @@ impl<I> std::fmt::Debug for PrefixIterator<I> {
f.write_str("PrefixIterator")
}
}

/// Storage prefix iterator generic wrapper type.
pub struct PatternIterator<I> {
/// The concrete iterator implementation
pub iter: I,
/// The pattern we are matching keys against.
pub pattern: Regex,
}

impl<I> PatternIterator<I> {
/// Initialize a new prefix iterator
pub fn new<E>(iter: I, pattern: Regex) -> Self
where
E: std::error::Error,
I: Iterator<Item = Result<KVBytes, E>>,
{
Self {
iter,
pattern,
}
}
}

impl<I> std::fmt::Debug for PatternIterator<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("PatternIterator")
}
}
2 changes: 2 additions & 0 deletions wasm/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f352a10

Please sign in to comment.