diff --git a/Cargo.lock b/Cargo.lock index 67a97f72c3e..33c27003ca7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3263,6 +3263,7 @@ dependencies = [ "ethnum", "fuel-core", "fuel-core-chain-config", + "fuel-core-client", "fuel-core-database", "fuel-core-services", "fuel-core-storage", diff --git a/benches/Cargo.toml b/benches/Cargo.toml index e4ee7db927b..a44309a48d0 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -23,6 +23,7 @@ fuel-core = { path = "../crates/fuel-core", default-features = false, features = "rocksdb-production", ] } fuel-core-chain-config = { workspace = true } +fuel-core-client = { path = "./../crates/client" } fuel-core-database = { path = "./../crates/database" } fuel-core-services = { path = "./../crates/services" } fuel-core-storage = { path = "./../crates/storage", features = ["smt"] } @@ -76,3 +77,7 @@ name = "transaction_throughput" [[bench]] harness = false name = "db_lookup_times" + +[[bench]] +harness = false +name = "read_view_get_coins" diff --git a/benches/benches/read_view_get_coins.rs b/benches/benches/read_view_get_coins.rs new file mode 100644 index 00000000000..0327b889371 --- /dev/null +++ b/benches/benches/read_view_get_coins.rs @@ -0,0 +1,156 @@ +use fuel_core::{ + combined_database::CombinedDatabase, + fuel_core_graphql_api::database::ReadDatabase, +}; + +use fuel_core_chain_config::Randomize; +use fuel_core_storage::{ + tables::{ + Coins, + FuelBlocks, + }, + transactional::WriteTransaction, + StorageAsMut, +}; +use fuel_core_types::{ + blockchain::block::CompressedBlock, + entities::coins::coin::CompressedCoin, + fuel_tx::{ + Address, + AssetId, + UtxoId, + }, + fuel_types::BlockHeight, +}; +use rand::{ + rngs::StdRng, + SeedableRng, +}; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + println!("Setting up bench harness."); + let mut harness = Harness::new(StdRng::seed_from_u64(2322)).await?; + + println!("Populating storage with transactions."); + let utxo_ids = harness.populate_database().await?; + + println!("Querying transactions from storage."); + let total = harness + .query_database_many_times_concurrent(&utxo_ids) + .await?; + + println!("Queried a total of {total} coins"); + + Ok(()) +} + +struct Harness { + rng: Rng, + params: Parameters, + db: CombinedDatabase, +} + +impl Harness { + async fn new(rng: Rng) -> anyhow::Result { + let params = Parameters::hard_coded(); + let db = CombinedDatabase::default(); + + Ok(Self { rng, params, db }) + } + + async fn populate_database(&mut self) -> anyhow::Result> { + let mut utxo_ids = Vec::new(); + + let mut coins: Vec<_> = (0..(self.params.utxo_count_per_block + * self.params.num_blocks)) + .map(|_| self.generate_random_coin()) + .collect(); + + for block_height in 0..self.params.num_blocks { + let block_height = BlockHeight::from(block_height as u32); + let mut compressed_block = CompressedBlock::default(); + compressed_block.header_mut().set_block_height(block_height); + + let mut transaction = self.db.on_chain_mut().write_transaction(); + + transaction + .storage::() + .insert(&block_height, &compressed_block) + .unwrap(); + + for _ in 0..self.params.utxo_count_per_block { + let (utxo_id, coin) = coins.pop().unwrap(); // TODO: Cleanup + + transaction + .storage::() + .insert(&utxo_id, &coin) + .unwrap(); + + utxo_ids.push(utxo_id); + } + + transaction.commit().unwrap(); + } + + Ok(utxo_ids) + } + + async fn query_database_many_times_concurrent( + &mut self, + utxo_ids: &[UtxoId], + ) -> anyhow::Result { + let mut total = 0; + let mut handles = Vec::new(); + for _ in 0..self.params.num_queries { + let on_chain_db = self.db.on_chain().clone(); + let off_chain_db = self.db.off_chain().clone(); + let utxo_ids = utxo_ids.to_vec(); + + let handle = tokio::spawn(async move { + let read_database = + ReadDatabase::new(0, BlockHeight::new(0), on_chain_db, off_chain_db); + + let read_view = read_database.view().unwrap(); + let res: Vec<_> = read_view.coins(utxo_ids).await.collect(); + res.len() + }); + + handles.push(handle); + } + + for handle in handles { + let res = handle.await.unwrap(); + total += res; + } + + Ok(total) + } + + fn generate_random_coin(&mut self) -> (UtxoId, CompressedCoin) { + let utxo_id = UtxoId::randomize(&mut self.rng); + + let mut coin = CompressedCoin::default(); + coin.set_amount(self.rng.next_u64()); + coin.set_asset_id(AssetId::randomize(&mut self.rng)); + coin.set_owner(Address::randomize(&mut self.rng)); + + (utxo_id, coin) + } +} + +struct Parameters { + num_queries: usize, + num_blocks: usize, + utxo_count_per_block: usize, +} + +impl Parameters { + fn hard_coded() -> Self { + Self { + num_queries: 100, + num_blocks: 100, + utxo_count_per_block: 1000, + } + } +} diff --git a/crates/fuel-core/src/state/rocks_db.rs b/crates/fuel-core/src/state/rocks_db.rs index c751ebe7c55..8bde0eb979d 100644 --- a/crates/fuel-core/src/state/rocks_db.rs +++ b/crates/fuel-core/src/state/rocks_db.rs @@ -227,7 +227,7 @@ where opener: F, path: P, columns: Vec, - capacity: Option, + _capacity: Option, max_fds: i32, ) -> DatabaseResult where @@ -252,6 +252,8 @@ where // See https://github.com/facebook/rocksdb/blob/a1523efcdf2f0e8133b9a9f6e170a0dad49f928f/include/rocksdb/table.h#L246-L271 for details on what the format versions are/do. block_opts.set_format_version(5); + let capacity = Some(100 * 1024 * 1024); + if let Some(capacity) = capacity { // Set cache size 1/3 of the capacity as recommended by // https://github.com/facebook/rocksdb/wiki/Setup-Options-and-Basic-Tuning#block-cache-size