From 3fb9d739f3ae2f9fa4cf1c2dca08b69dc4005cf6 Mon Sep 17 00:00:00 2001 From: Al Liu Date: Mon, 15 Apr 2024 01:16:40 +0800 Subject: [PATCH] Implement `redb` based `StateStorage` (#1040) --- Cargo.lock | 121 ++---------------- crates/core/Cargo.toml | 5 +- crates/core/src/contract/executor.rs | 3 +- .../src/contract/executor/mock_runtime.rs | 7 +- crates/core/src/contract/storages/mod.rs | 18 +-- crates/core/src/contract/storages/redb.rs | 79 ++++++++++++ crates/core/src/contract/storages/rocks_db.rs | 97 -------------- crates/core/src/contract/storages/sqlite.rs | 2 +- crates/core/src/wasm_runtime/state_store.rs | 22 ++-- crates/fdev/src/commands.rs | 2 +- crates/fdev/src/wasm_runtime/state.rs | 2 +- 11 files changed, 123 insertions(+), 235 deletions(-) create mode 100644 crates/core/src/contract/storages/redb.rs delete mode 100644 crates/core/src/contract/storages/rocks_db.rs diff --git a/Cargo.lock b/Cargo.lock index 1c55e4fe6..41641ab54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -538,26 +538,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bindgen" -version = "0.69.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" -dependencies = [ - "bitflags 2.5.0", - "cexpr", - "clang-sys", - "itertools", - "lazy_static", - "lazycell", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.58", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -672,17 +652,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "cache-padded" version = "1.3.0" @@ -700,19 +669,6 @@ name = "cc" version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" -dependencies = [ - "jobserver", - "libc", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] [[package]] name = "cfg-if" @@ -777,17 +733,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "clang-sys" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "4.5.4" @@ -1785,8 +1730,8 @@ dependencies = [ "pav_regression", "pico-args", "rand", + "redb", "reqwest", - "rocksdb", "rsa", "serde", "serde_json", @@ -2643,15 +2588,6 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" -[[package]] -name = "jobserver" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685a7d121ee3f65ae4fddd72b25a04bb36b6af81bc0828f7d5434c0fe60fa3a2" -dependencies = [ - "libc", -] - [[package]] name = "js-sys" version = "0.3.69" @@ -2701,12 +2637,6 @@ dependencies = [ "spin 0.5.2", ] -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "leb128" version = "0.2.5" @@ -2719,16 +2649,6 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "libloading" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" -dependencies = [ - "cfg-if", - "windows-targets 0.52.5", -] - [[package]] name = "libm" version = "0.2.8" @@ -3130,20 +3050,6 @@ dependencies = [ "libc", ] -[[package]] -name = "librocksdb-sys" -version = "0.16.0+8.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce3d60bc059831dc1c83903fb45c103f75db65c5a7bf22272764d9cc683e348c" -dependencies = [ - "bindgen", - "bzip2-sys", - "cc", - "glob", - "libc", - "libz-sys", -] - [[package]] name = "libsqlite3-sys" version = "0.27.0" @@ -4465,6 +4371,15 @@ dependencies = [ "yasna", ] +[[package]] +name = "redb" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1100a056c5dcdd4e5513d5333385223b26ef1bf92f31eb38f407e8c20549256" +dependencies = [ + "libc", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -4674,16 +4589,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "rocksdb" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd13e55d6d7b8cd0ea569161127567cd587676c99f4472f779a0279aa60a7a7" -dependencies = [ - "libc", - "librocksdb-sys", -] - [[package]] name = "ron" version = "0.8.1" @@ -5106,12 +5011,6 @@ dependencies = [ "memmap2 0.6.2", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "signal-hook-registry" version = "1.4.1" diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 069f51c33..4fd2c10fe 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -48,7 +48,7 @@ ordered-float = "4.2" pav_regression = "0.4.0" parking_lot = "0.12" rand = { features = ["small_rng"], workspace = true } -rocksdb = { default-features = false, optional = true, version = "0.22" } +redb = { optional = true, version = "2" } serde = { features = ["derive", "rc"], workspace = true } serde_json = { workspace = true } serde_with = { workspace = true } @@ -90,10 +90,9 @@ tempfile = "3.8" tracing = "0.1" [features] -default = ["sqlite", "trace", "websocket"] +default = ["redb", "trace", "websocket"] local-mode = [] network-mode = [] -rocks_db = ["rocksdb"] sqlite = ["sqlx"] trace = ["tracing-subscriber"] trace-ot = ["opentelemetry-jaeger", "trace", "tracing-opentelemetry", "opentelemetry-otlp"] diff --git a/crates/core/src/contract/executor.rs b/crates/core/src/contract/executor.rs index 7a0fa7410..3f9f88455 100644 --- a/crates/core/src/contract/executor.rs +++ b/crates/core/src/contract/executor.rs @@ -498,8 +498,7 @@ impl Executor { let static_conf = crate::config::Config::conf(); let db_path = crate::config::Config::conf().db_dir(); - let state_store = - StateStore::new(Storage::new(Some(&db_path)).await?, MAX_MEM_CACHE).unwrap(); + let state_store = StateStore::new(Storage::new(&db_path).await?, MAX_MEM_CACHE).unwrap(); let contract_dir = config .node_data_dir diff --git a/crates/core/src/contract/executor/mock_runtime.rs b/crates/core/src/contract/executor/mock_runtime.rs index 62d0e9c10..8f7671e02 100644 --- a/crates/core/src/contract/executor/mock_runtime.rs +++ b/crates/core/src/contract/executor/mock_runtime.rs @@ -17,8 +17,7 @@ impl Executor { let db_path = data_dir.join("db"); std::fs::create_dir_all(&db_path).expect("directory created"); - let state_store = - StateStore::new(Storage::new(Some(&db_path)).await?, u16::MAX as u32).unwrap(); + let state_store = StateStore::new(Storage::new(&db_path).await?, u16::MAX as u32).unwrap(); let executor = Executor::new( state_store, @@ -125,8 +124,10 @@ mod test { const MAX_SIZE: i64 = 10 * 1024 * 1024; const MAX_MEM_CACHE: u32 = 10_000_000; let tmp_dir = tempfile::tempdir()?; + let state_store_path = tmp_dir.path().join("state_store"); let contract_store = ContractStore::new(tmp_dir.path().join("executor-test"), MAX_SIZE)?; - let state_store = StateStore::new(Storage::new(None).await?, MAX_MEM_CACHE).unwrap(); + let state_store = + StateStore::new(Storage::new(&state_store_path).await?, MAX_MEM_CACHE).unwrap(); let mut counter = 0; Executor::new( state_store, diff --git a/crates/core/src/contract/storages/mod.rs b/crates/core/src/contract/storages/mod.rs index 121531307..6ef266f0f 100644 --- a/crates/core/src/contract/storages/mod.rs +++ b/crates/core/src/contract/storages/mod.rs @@ -1,15 +1,17 @@ +/// State storage implementation based on the `sqlite` #[cfg(feature = "sqlite")] pub mod sqlite; -#[cfg(feature = "sqlite")] +#[cfg(all(feature = "sqlite", not(feature = "redb")))] pub use sqlite::Pool as SqlitePool; -#[cfg(feature = "sqlite")] +#[cfg(all(feature = "sqlite", not(feature = "redb")))] pub type Storage = SqlitePool; -#[cfg(feature = "rocks_db")] -pub mod rocks_db; -#[cfg(all(feature = "rocks_db", not(feature = "sqlite")))] -use self::rocks_db::RocksDb; +/// State storage implementation based on the [`redb`] +#[cfg(feature = "redb")] +pub mod redb; +#[cfg(feature = "redb")] +use self::redb::ReDb; -#[cfg(all(feature = "rocks_db", not(feature = "sqlite")))] -pub type Storage = RocksDb; +#[cfg(feature = "redb")] +pub type Storage = ReDb; diff --git a/crates/core/src/contract/storages/redb.rs b/crates/core/src/contract/storages/redb.rs new file mode 100644 index 000000000..fc67530f4 --- /dev/null +++ b/crates/core/src/contract/storages/redb.rs @@ -0,0 +1,79 @@ +use std::path::Path; + +use freenet_stdlib::prelude::*; +use redb::{Database, TableDefinition}; + +use crate::wasm_runtime::StateStorage; + +const CONTRACT_PARAMS_TABLE: TableDefinition<&[u8], &[u8]> = + TableDefinition::new("contract_params"); +const STATE_TABLE: TableDefinition<&[u8], &[u8]> = TableDefinition::new("state"); + +pub struct ReDb(Database); + +impl ReDb { + pub async fn new(db_path: &Path) -> Result { + tracing::info!("loading contract store from {db_path:?}"); + + Database::create(db_path).map(Self).map_err(Into::into) + } +} + +impl StateStorage for ReDb { + type Error = redb::Error; + + async fn store(&mut self, key: ContractKey, state: WrappedState) -> Result<(), Self::Error> { + let txn = self.0.begin_write()?; + + { + let mut tbl = txn.open_table(STATE_TABLE)?; + tbl.insert(key.as_bytes(), state.as_ref())?; + } + txn.commit().map_err(Into::into) + } + + async fn get(&self, key: &ContractKey) -> Result, Self::Error> { + let txn = self.0.begin_read()?; + + let val = { + let tbl = txn.open_table(STATE_TABLE)?; + tbl.get(key.as_bytes())? + }; + + match val { + Some(v) => Ok(Some(WrappedState::new(v.value().to_vec()))), + None => Ok(None), + } + } + + async fn store_params( + &mut self, + key: ContractKey, + params: Parameters<'static>, + ) -> Result<(), Self::Error> { + let txn = self.0.begin_write()?; + + { + let mut tbl = txn.open_table(CONTRACT_PARAMS_TABLE)?; + tbl.insert(key.as_bytes(), params.as_ref())?; + } + txn.commit().map_err(Into::into) + } + + async fn get_params<'a>( + &'a self, + key: &'a ContractKey, + ) -> Result>, Self::Error> { + let txn = self.0.begin_read()?; + + let val = { + let tbl = txn.open_table(CONTRACT_PARAMS_TABLE)?; + tbl.get(key.as_bytes())? + }; + + match val { + Some(v) => Ok(Some(Parameters::from(v.value().to_vec()))), + None => Ok(None), + } + } +} diff --git a/crates/core/src/contract/storages/rocks_db.rs b/crates/core/src/contract/storages/rocks_db.rs deleted file mode 100644 index e6d9dd2ea..000000000 --- a/crates/core/src/contract/storages/rocks_db.rs +++ /dev/null @@ -1,97 +0,0 @@ -use std::path::Path; - -use freenet_stdlib::prelude::*; -use rocksdb::{Options, DB}; - -use crate::wasm_runtime::StateStorage; - -pub struct RocksDb(DB); - -impl RocksDb { - #[cfg_attr(feature = "sqlite", allow(unused))] - pub async fn new(db_path: &Path) -> Result { - tracing::info!("loading contract store from {db_path:?}"); - - let mut opts = Options::default(); - opts.create_if_missing(true); - opts.set_log_level(rocksdb::LogLevel::Debug); - - let db = DB::open(&opts, db_path).unwrap(); - - Ok(Self(db)) - } -} - -impl RocksDb { - const STATE_SUFFIX: &'static [u8] = "_key".as_bytes(); - const PARAMS_SUFFIX: &'static [u8] = "_params".as_bytes(); -} - -#[async_trait::async_trait] -impl StateStorage for RocksDb { - type Error = rocksdb::Error; - - async fn store(&mut self, key: ContractKey, state: WrappedState) -> Result<(), Self::Error> { - self.0 - .put([key.as_bytes(), RocksDb::STATE_SUFFIX].concat(), state)?; - - Ok(()) - } - - async fn get(&self, key: &ContractKey) -> Result, Self::Error> { - match self.0.get([key.as_bytes(), RocksDb::STATE_SUFFIX].concat()) { - Ok(result) => { - if let Some(r) = result.map(|r| Some(WrappedState::new(r))) { - Ok(r) - } else { - tracing::debug!( - "failed getting contract: `{key}` {}", - key.encoded_code_hash() - .map(|ch| format!("(with code hash: `{ch}`)")) - .unwrap_or_default() - ); - Ok(None) - } - } - Err(e) => { - if rocksdb::ErrorKind::NotFound == e.kind() { - Ok(None) - } else { - Err(e) - } - } - } - } - - async fn store_params( - &mut self, - key: ContractKey, - params: Parameters<'static>, - ) -> Result<(), Self::Error> { - self.0 - .put([key.as_bytes(), RocksDb::PARAMS_SUFFIX].concat(), params)?; - - Ok(()) - } - - async fn get_params<'a>( - &'a self, - key: &'a ContractKey, - ) -> Result>, Self::Error> { - match self - .0 - .get([key.as_bytes(), RocksDb::PARAMS_SUFFIX].concat()) - { - Ok(result) => Ok(result - .map(|r| Some(Parameters::from(r))) - .expect("vec bytes")), - Err(e) => { - if rocksdb::ErrorKind::NotFound == e.kind() { - Ok(None) - } else { - Err(e) - } - } - } - } -} diff --git a/crates/core/src/contract/storages/sqlite.rs b/crates/core/src/contract/storages/sqlite.rs index a52aed2f2..14a6df635 100644 --- a/crates/core/src/contract/storages/sqlite.rs +++ b/crates/core/src/contract/storages/sqlite.rs @@ -25,6 +25,7 @@ async fn create_contracts_table(pool: &SqlitePool) -> Result<(), SqlDbError> { pub struct Pool(SqlitePool); impl Pool { + #[cfg_attr(feature = "redb", allow(unused))] pub async fn new(db_dir: Option<&Path>) -> Result { let opts = if let Some(db_dir) = db_dir { let file = db_dir.join("freenet.db"); @@ -46,7 +47,6 @@ impl Pool { } } -#[async_trait::async_trait] impl StateStorage for Pool { type Error = SqlDbError; diff --git a/crates/core/src/wasm_runtime/state_store.rs b/crates/core/src/wasm_runtime/state_store.rs index 3019adf0e..e765bfc0a 100644 --- a/crates/core/src/wasm_runtime/state_store.rs +++ b/crates/core/src/wasm_runtime/state_store.rs @@ -1,3 +1,4 @@ +use core::future::Future; use freenet_stdlib::prelude::*; use stretto::AsyncCache; @@ -22,21 +23,26 @@ impl From for crate::wasm_runtime::ContractError { } } -#[async_trait::async_trait] -#[allow(clippy::type_complexity)] pub trait StateStorage { type Error; - async fn store(&mut self, key: ContractKey, state: WrappedState) -> Result<(), Self::Error>; - async fn store_params( + fn store( + &mut self, + key: ContractKey, + state: WrappedState, + ) -> impl Future> + Send; + fn store_params( &mut self, key: ContractKey, state: Parameters<'static>, - ) -> Result<(), Self::Error>; - async fn get(&self, key: &ContractKey) -> Result, Self::Error>; - async fn get_params<'a>( + ) -> impl Future> + Send; + fn get( + &self, + key: &ContractKey, + ) -> impl Future, Self::Error>> + Send; + fn get_params<'a>( &'a self, key: &'a ContractKey, - ) -> Result>, Self::Error>; + ) -> impl Future>, Self::Error>> + Send + 'a; } pub struct StateStore { diff --git a/crates/fdev/src/commands.rs b/crates/fdev/src/commands.rs index 9057ed652..ada6c306f 100644 --- a/crates/fdev/src/commands.rs +++ b/crates/fdev/src/commands.rs @@ -165,7 +165,7 @@ async fn execute_command( let contract_store = ContractStore::new(contracts_data_path, DEFAULT_MAX_CONTRACT_SIZE)?; let delegate_store = DelegateStore::new(delegates_data_path, DEFAULT_MAX_DELEGATE_SIZE)?; let secret_store = SecretsStore::new(secrets_data_path)?; - let state_store = StateStore::new(Storage::new(Some(&database_path)).await?, MAX_MEM_CACHE)?; + let state_store = StateStore::new(Storage::new(&database_path).await?, MAX_MEM_CACHE)?; let rt = freenet::dev_tool::Runtime::build(contract_store, delegate_store, secret_store, false)?; let mut executor = Executor::new(state_store, || Ok(()), OperationMode::Local, rt, None) diff --git a/crates/fdev/src/wasm_runtime/state.rs b/crates/fdev/src/wasm_runtime/state.rs index 735ed823c..6d454114c 100644 --- a/crates/fdev/src/wasm_runtime/state.rs +++ b/crates/fdev/src/wasm_runtime/state.rs @@ -31,7 +31,7 @@ impl AppState { )?; let secrets_store = SecretsStore::new(Config::conf().secrets_dir())?; let state_store = StateStore::new( - Storage::new(Some(&Config::conf().db_dir())).await?, + Storage::new(&Config::conf().db_dir()).await?, Self::MAX_MEM_CACHE, )?; let rt = freenet::dev_tool::Runtime::build(