Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add lmdb store #10

Merged
merged 30 commits into from
Oct 14, 2019
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8d1a698
lmdb store passing tests
willemolding Oct 8, 2019
ccc068b
add reusable benchmarking
willemolding Oct 8, 2019
05d845d
use async writes with lmdb for significnat speed boost
willemolding Oct 8, 2019
b0cffa0
convert cas implementation to use kv
willemolding Oct 9, 2019
65511af
add flags to cas to use async file writes
willemolding Oct 9, 2019
03c962f
switch to kv crate for eav also
willemolding Oct 9, 2019
536ee0c
finish spike using lmdb
willemolding Oct 9, 2019
a0c4f6a
update EAV filter to have be an enum to allow optimizing in particula…
willemolding Oct 9, 2019
aa5062b
migrate cas to rkv
willemolding Oct 10, 2019
212e01a
move benchmarks from holochain rust to here
willemolding Oct 10, 2019
2e0184c
add optimization for exact entity queries
willemolding Oct 10, 2019
a08a45e
fix benchmarks
willemolding Oct 10, 2019
500c6b2
update lmdb crate readme
willemolding Oct 10, 2019
bf22cc8
remove un needed HashString conversions
willemolding Oct 10, 2019
e5e9cc2
Update crates/holochain_persistence_lmdb/README.md
willemolding Oct 10, 2019
ac107d9
Update crates/holochain_persistence_lmdb/README.md
willemolding Oct 10, 2019
0e56f3e
fmt
willemolding Oct 10, 2019
3fd6ce1
Merge branch 'add-lmdb-store' of github.com:holochain/holochain-persi…
willemolding Oct 10, 2019
5e8e89b
add conversions back we actually did need
willemolding Oct 10, 2019
0f31e7c
removes unused bitflags crate
willemolding Oct 10, 2019
d0bb31d
Update crates/holochain_persistence_lmdb/src/eav/lmdb.rs
willemolding Oct 11, 2019
8bbe686
Update crates/holochain_persistence_lmdb/src/eav/lmdb.rs
willemolding Oct 11, 2019
403e674
change MAX_SIZE to INITIAL_SIZE
willemolding Oct 11, 2019
6decb84
dry up the lmdb crate
willemolding Oct 11, 2019
f99803e
format
willemolding Oct 11, 2019
b36d53b
refactors out add logic to common
willemolding Oct 13, 2019
d474ae5
add mmap growth logic and test
willemolding Oct 13, 2019
71e76cc
fmt
willemolding Oct 13, 2019
7a2ab1d
change EAV implementation to return the first error when running a query
willemolding Oct 14, 2019
67efac1
add check for duplicate EAVI and will bump timestamp to avoid collisions
willemolding Oct 14, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ members = [
"crates/holochain_persistence_mem",
"crates/holochain_persistence_file",
"crates/holochain_persistence_pickle",
"crates/holochain_persistence_lmdb",
# "benchmarks",
]
19 changes: 19 additions & 0 deletions benchmarks/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "benchmarks"
version = "0.0.8"
authors = ["Holochain Core Dev Team <devcore@holochain.org>"]

[dependencies]
holochain_persistence_api = { path = "../crates/holochain_persistence_api" }
holochain_persistence_file = { path = "../crates/holochain_persistence_file" }
holochain_persistence_mem = { path = "../crates/holochain_persistence_mem" }
holochain_persistence_pickle = { path = "../crates/holochain_persistence_pickle" }
holochain_persistence_lmdb = { path = "../crates/holochain_persistence_lmdb" }
bencher = "=0.1.5"
tempfile = "=3.0.7"



[[bench]]
name = "my_benchmark"
harness = false
143 changes: 143 additions & 0 deletions benchmarks/benches/my_benchmark.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#[macro_use]
extern crate bencher;
extern crate holochain_persistence_api;
extern crate holochain_persistence_file;
extern crate holochain_persistence_mem;
extern crate holochain_persistence_pickle;
extern crate holochain_persistence_lmdb;
extern crate tempfile;

use self::tempfile::tempdir;
use bencher::Bencher;
use holochain_persistence_api::{
cas::{content::ExampleAddressableContent, storage::EavTestSuite},
eav::eavi::ExampleAttribute,
};
use holochain_persistence_file::eav::file::EavFileStorage;
use holochain_persistence_mem::eav::memory::EavMemoryStorage;
use holochain_persistence_pickle::eav::pickle::EavPickleStorage;
use holochain_persistence_lmdb::eav::lmdb::EavLmdbStorage;

/*---------- Memory Storage ----------*/


fn bench_memory_eav_one_to_many(b: &mut Bencher) {
b.iter(|| {
let eav_storage = EavMemoryStorage::new();
EavTestSuite::test_one_to_many::<
ExampleAddressableContent,
ExampleAttribute,
EavMemoryStorage<ExampleAttribute>,
>(eav_storage.clone(), &ExampleAttribute::WithoutPayload)
})
}

fn bench_memory_eav_many_to_one(b: &mut Bencher) {
b.iter(|| {
let eav_storage = EavMemoryStorage::new();
EavTestSuite::test_one_to_many::<
ExampleAddressableContent,
ExampleAttribute,
EavMemoryStorage<ExampleAttribute>,
>(eav_storage.clone(), &ExampleAttribute::WithoutPayload)
})
}

/*---------- File Storage ----------*/


fn bench_file_eav_one_to_many(b: &mut Bencher) {
b.iter(|| {
let temp = tempdir().expect("test was supposed to create temp dir");
let temp_path = String::from(temp.path().to_str().expect("temp dir could not be string"));
let eav_storage = EavFileStorage::new(temp_path).unwrap();
EavTestSuite::test_one_to_many::<
ExampleAddressableContent,
ExampleAttribute,
EavFileStorage<ExampleAttribute>,
>(eav_storage.clone(), &&ExampleAttribute::WithoutPayload)
})
}

fn bench_file_eav_many_to_one(b: &mut Bencher) {
b.iter(|| {
let temp = tempdir().expect("test was supposed to create temp dir");
let temp_path = String::from(temp.path().to_str().expect("temp dir could not be string"));
let eav_storage = EavFileStorage::new(temp_path).unwrap();
EavTestSuite::test_many_to_one::<
ExampleAddressableContent,
ExampleAttribute,
EavFileStorage<ExampleAttribute>,
>(eav_storage.clone(), &ExampleAttribute::WithoutPayload)
})
}

/*---------- Pickle Storage ----------*/


fn bench_pickle_eav_one_to_many(b: &mut Bencher) {
b.iter(|| {
let temp = tempdir().expect("test was supposed to create temp dir");
let temp_path = String::from(temp.path().to_str().expect("temp dir could not be string"));
let eav_storage = EavPickleStorage::new(temp_path);
EavTestSuite::test_one_to_many::<
ExampleAddressableContent,
ExampleAttribute,
EavPickleStorage<ExampleAttribute>,
>(eav_storage.clone(), &ExampleAttribute::WithoutPayload)
})
}

fn bench_pickle_eav_many_to_one(b: &mut Bencher) {
b.iter(|| {
let temp = tempdir().expect("test was supposed to create temp dir");
let temp_path = String::from(temp.path().to_str().expect("temp dir could not be string"));
let eav_storage = EavPickleStorage::new(temp_path);
EavTestSuite::test_many_to_one::<
ExampleAddressableContent,
ExampleAttribute,
EavPickleStorage<ExampleAttribute>,
>(eav_storage.clone(), &ExampleAttribute::WithoutPayload)
})
}

/*---------- LMDB Storage ----------*/

fn bench_lmdb_eav_one_to_many(b: &mut Bencher) {
b.iter(|| {
let temp = tempdir().expect("test was supposed to create temp dir");
let temp_path = String::from(temp.path().to_str().expect("temp dir could not be string"));
let eav_storage = EavLmdbStorage::new(temp_path);
EavTestSuite::test_one_to_many::<
ExampleAddressableContent,
ExampleAttribute,
EavLmdbStorage<ExampleAttribute>,
>(eav_storage.clone(), &ExampleAttribute::WithoutPayload)
})
}

fn bench_lmdb_eav_many_to_one(b: &mut Bencher) {
b.iter(|| {
let temp = tempdir().expect("test was supposed to create temp dir");
let temp_path = String::from(temp.path().to_str().expect("temp dir could not be string"));
let eav_storage = EavLmdbStorage::new(temp_path);
EavTestSuite::test_many_to_one::<
ExampleAddressableContent,
ExampleAttribute,
EavLmdbStorage<ExampleAttribute>,
>(eav_storage.clone(), &ExampleAttribute::WithoutPayload)
})
}

benchmark_group!(
benches,
bench_memory_eav_many_to_one,
bench_memory_eav_one_to_many,
bench_file_eav_one_to_many,
bench_file_eav_many_to_one,
bench_pickle_eav_many_to_one,
bench_pickle_eav_one_to_many,
bench_lmdb_eav_many_to_one,
bench_lmdb_eav_one_to_many,
);
benchmark_main!(benches);
1 change: 1 addition & 0 deletions crates/holochain_persistence_api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ objekt= "=0.1.2"
holochain_json_api = "=0.0.17"
holochain_json_derive = "=0.0.17"
uuid = { version = "=0.7.1", features = ["v4"] }
rand = "0.7.2"

[dev-dependencies]
maplit = "=1.0.1"
38 changes: 37 additions & 1 deletion crates/holochain_persistence_api/src/cas/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! A test suite for CAS is also implemented here.

use crate::{
cas::content::{Address, AddressableContent, Content},
cas::content::{Address, AddressableContent, Content, ExampleAddressableContent},
eav::{
Attribute, EavFilter, EaviQuery, EntityAttributeValueIndex, EntityAttributeValueStorage,
IndexFilter,
Expand Down Expand Up @@ -811,6 +811,42 @@ impl EavTestSuite {
}
}

pub struct CasBencher;

impl CasBencher {

fn random_addressable_content() -> ExampleAddressableContent {
let s: String = (0..4).map(|_| rand::random::<char>()).collect();
ExampleAddressableContent::try_from_content(&RawString::from(s).into()).unwrap()
}

pub fn bench_add(
b: &mut test::Bencher,
mut store: impl ContentAddressableStorage,
) {
b.iter(|| {
store.add(&CasBencher::random_addressable_content())
})
}

pub fn bench_fetch(
b: &mut test::Bencher,
mut store: impl ContentAddressableStorage,
) {
// add some values to make it realistic
for _ in 0..100 {
store.add(&CasBencher::random_addressable_content()).unwrap();
}

let test_content = CasBencher::random_addressable_content();
store.add(&test_content).unwrap();

b.iter(|| {
store.fetch(&test_content.address())
})
}
}

#[cfg(test)]
pub mod tests {
use crate::cas::{
Expand Down
21 changes: 14 additions & 7 deletions crates/holochain_persistence_api/src/eav/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,32 +154,39 @@ impl<'a, A: Attribute> EaviQuery<'a, A> {
}

/// Represents a filter type which takes in a function to match on
pub struct EavFilter<'a, T: 'a + Eq>(Box<dyn Fn(T) -> bool + 'a>);
// pub struct EavFilter<'a, T: 'a + Eq>(Box<dyn Fn(T) -> bool + 'a>);
pub enum EavFilter<'a, T: 'a + Eq> {
Exact(T),
willemolding marked this conversation as resolved.
Show resolved Hide resolved
Predicate(Box<dyn Fn(T) -> bool + 'a>),
}

impl<'a, T: 'a + Eq> EavFilter<'a, T> {
pub fn single(val: T) -> Self {
Self(Box::new(move |v| v == val))
Self::Exact(val)
}

pub fn multiple(vals: Vec<T>) -> Self {
Self(Box::new(move |val| vals.iter().any(|v| *v == val)))
Self::Predicate(Box::new(move |val| vals.iter().any(|v| *v == val)))
}

pub fn predicate<F>(predicate: F) -> Self
where
F: Fn(T) -> bool + 'a,
{
Self(Box::new(predicate))
Self::Predicate(Box::new(predicate))
}

pub fn check(&self, val: T) -> bool {
self.0(val)
pub fn check(&self, b: T) -> bool {
match self {
Self::Exact(a) => a == &b,
Self::Predicate(f) => f(b)
}
}
}

impl<'a, T: Eq> Default for EavFilter<'a, T> {
fn default() -> EavFilter<'a, T> {
Self(Box::new(|_| true))
Self::Predicate(Box::new(|_| true))
}
}

Expand Down
92 changes: 91 additions & 1 deletion crates/holochain_persistence_api/src/eav/storage.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
use eav::{eavi::EntityAttributeValueIndex, query::EaviQuery, Attribute};
use eav::{
eavi::{
EntityAttributeValueIndex,
ExampleAttribute,
},
query::EaviQuery, Attribute, EavFilter, IndexFilter,
};
use crate::holochain_json_api::{
json::{RawString},
};
use cas::content::{
ExampleAddressableContent,
AddressableContent
};
use error::PersistenceResult;
use objekt;
use reporting::ReportStorage;
Expand Down Expand Up @@ -99,3 +112,80 @@ pub fn increment_key_till_no_collision<A: Attribute>(
Ok(eav)
}
}

pub struct EavBencher;

impl EavBencher {

fn random_addressable_content() -> ExampleAddressableContent {
let s: String = (0..4).map(|_| rand::random::<char>()).collect();
ExampleAddressableContent::try_from_content(&RawString::from(s).into()).unwrap()
}

pub fn bench_add(
b: &mut test::Bencher,
mut store: impl EntityAttributeValueStorage<ExampleAttribute>,
) {
b.iter(|| {
let eav = EntityAttributeValueIndex::new(
&Self::random_addressable_content().address(),
&ExampleAttribute::WithPayload("favourite-color".to_string()),
&Self::random_addressable_content().address(),
)
.expect("Could create entityAttributeValue");
store.add_eavi(&eav)
})
}

pub fn bench_fetch_all(
b: &mut test::Bencher,
mut store: impl EntityAttributeValueStorage<ExampleAttribute>,
) {
// add some values to make it realistic
for _ in 0..100 {
let eav = EntityAttributeValueIndex::new(
&Self::random_addressable_content().address(),
&ExampleAttribute::WithPayload("favourite-color".to_string()),
&Self::random_addressable_content().address(),
).expect("Could create entityAttributeValue");
store.add_eavi(&eav).unwrap();
}

b.iter(|| {
store.fetch_eavi(&EaviQuery::default())
})
}

pub fn bench_fetch_exact(
b: &mut test::Bencher,
mut store: impl EntityAttributeValueStorage<ExampleAttribute>,
) {
// add some values to make it realistic
for _ in 0..100 {
let eav = EntityAttributeValueIndex::new(
&Self::random_addressable_content().address(),
&ExampleAttribute::WithPayload("favourite-color".to_string()),
&Self::random_addressable_content().address(),
).expect("Could create entityAttributeValue");
store.add_eavi(&eav).unwrap();
}

// add the one entry we want to test retrieval of
let eav = EntityAttributeValueIndex::new(
&Self::random_addressable_content().address(),
&ExampleAttribute::WithPayload("favourite-color".to_string()),
&Self::random_addressable_content().address(),
).expect("Could create entityAttributeValue");
store.add_eavi(&eav).unwrap();

b.iter(|| {
store.fetch_eavi(&EaviQuery::new(
EavFilter::single(eav.entity()),
EavFilter::default(),
EavFilter::default(),
IndexFilter::LatestByAttribute,
None,
))
})
}
}
2 changes: 2 additions & 0 deletions crates/holochain_persistence_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#![feature(try_trait)]
#![feature(never_type)]
#![warn(unused_extern_crates)]
#![feature(test)]
extern crate test;
#[macro_use]
extern crate lazy_static;

Expand Down