Repository: dbpunk-labs/db3 (language: rust)

Issue:
WIP:the benchmark report for db3
## Motivations

<!--
If your feature request is related to a problem, please describe it.
-->

- Would you like to implement this feature? [y]

## Pre-Request
- Ramp up Rust bench
  - Guide: https://bheisler.github.io/criterion.rs/book/getting_started.html
  - Example 1: https://github.com/denoland/deno_doc/blob/main/benches/doc_parser.rs
  - Example 2: https://github.com/mmalmi/rod/blob/master/benches/my_benchmark.rs
  - Benchmark compare: https://github.com/BurntSushi/cargo-benchcmp
  - 
- Typescript benchmark
- db3 js-sdk benchmark POC
- db3 rust-sdk benchmark poc


## Solution

<!-- Describe the solution you'd like. -->
We decide to use Criterion as our rust benchmark framework.

- [x] research rust benchmark framework
- [x] integrate criterion into db3 and add first benchmark into db3 #257 
- [x] add rust benchmark stage into workflow #266 
- [x] research js/ts benchmark framework https://github.com/caderek/benny
- [x] add first js SDK benchmark into db3 https://github.com/dbpunk-labs/db3/pull/258


## Alternatives

<!-- Describe any alternative solutions or features you've considered. -->

## Additional context

<!-- Add any other context or screenshots about the feature request here. -->
- https://www.notamonadtutorial.com/benchmarking-and-analyzing-rust-performance-with-criterion-and-iai/
- https://www.cs.brandeis.edu/~cs146a/rust/rustbyexample-02-21-2015/bench.html
- https://github.com/caderek/benny
- https://github.com/bestiejs/benchmark.js
- https://github.com/timostamm/protobuf-ts/tree/master/packages/benchmarks  typescript benchmark 


Discussion / hints:
@jingchen2222 https://github.com/timostamm/protobuf-ts/tree/master/packages/benchmarks typescript benchmark

Fix patch:
diff --git a/src/node/src/storage_node_impl.rs b/src/node/src/storage_node_impl.rs
index c1d9da6f..c594e5f9 100644
--- a/src/node/src/storage_node_impl.rs
+++ b/src/node/src/storage_node_impl.rs
@@ -343,7 +343,6 @@ impl StorageNode for StorageNodeImpl {
         &self,
         request: Request<GetKeyRequest>,
     ) -> std::result::Result<Response<GetKeyResponse>, Status> {
-        info!("get key");
         if let Some(batch_get_key) = request.into_inner().batch_get {
             match self.context.node_store.lock() {
                 Ok(mut node_store) => {
@@ -370,7 +369,6 @@ impl StorageNode for StorageNodeImpl {
                             &batch_get_key.session_token
                         )));
                     }
-                    info!("batch get key {:?}", &batch_get_key);
                     let values = node_store
                         .get_auth_store()
                         .batch_get(&addr.unwrap(), &batch_get_key)
@@ -382,7 +380,6 @@ impl StorageNode for StorageNodeImpl {
                         .get_session_mut(&batch_get_key.session_token)
                         .unwrap()
                         .increase_query(1);
-                    info!("get key ok");
                     Ok(Response::new(GetKeyResponse {
                         batch_get_values: Some(values.to_owned()),
                     }))
@@ -446,7 +443,6 @@ impl StorageNode for StorageNodeImpl {
         &self,
         request: Request<BroadcastRequest>,
     ) -> std::result::Result<Response<BroadcastResponse>, Status> {
-        info!("receive the broadcast request");
         let r = request.into_inner();
         let response = self
             .context
diff --git a/src/sdk/Cargo.toml b/src/sdk/Cargo.toml
index 05327593..3d8ab2ab 100644
--- a/src/sdk/Cargo.toml
+++ b/src/sdk/Cargo.toml
@@ -21,16 +21,20 @@ tonic-web = "0.5.0"
 prost = "0.11"
 prost-types = "0.11"
 ethereum-types = { version = "0.14.0", default-features = false }
-subtle-encoding = { version = "0.5", default-features = false, features = ["bech32-preview"] }
+subtle-encoding = { version = "0.5", default-features = false, features = ["bech32-preview", "base64"] }
 chrono = "0.4.22"
 [dev-dependencies]
 rand = "0.8.5"
 db3-base={path="../base", version="0.1.0"}
 db3-cmd={path="../cmd", version="0.1.0"}
+criterion = { version = "0.3.4", default-features = false,features = ["async_futures", "async_tokio"]}
 [dependencies.uuid]
 version = "1.2.2"
 features = [
     "v4",                # Lets you generate random UUIDs
     "fast-rng",          # Use a faster (but still sufficiently random) RNG
     "macro-diagnostics", # Enable better diagnostics for compile-time UUIDs
-]
\ No newline at end of file
+]
+[[bench]]
+name = "sdk_benchmark"
+harness = false
\ No newline at end of file
diff --git a/src/sdk/benches/sdk_benchmark.rs b/src/sdk/benches/sdk_benchmark.rs
new file mode 100644
index 00000000..7ccd57e0
--- /dev/null
+++ b/src/sdk/benches/sdk_benchmark.rs
@@ -0,0 +1,112 @@
+use bytes::BytesMut;
+use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
+use db3_base::{get_a_random_nonce, get_a_static_keypair, get_address_from_pk};
+use db3_crypto::signer::Db3Signer;
+use db3_proto::db3_base_proto::{ChainId, ChainRole};
+use db3_proto::db3_mutation_proto::KvPair;
+use db3_proto::db3_mutation_proto::{Mutation, MutationAction};
+use db3_proto::db3_node_proto::storage_node_client::StorageNodeClient;
+use db3_sdk::mutation_sdk::MutationSDK;
+use db3_sdk::store_sdk::StoreSDK;
+use db3_session::session_manager::DEFAULT_SESSION_QUERY_LIMIT;
+use std::sync::Arc;
+use std::time;
+use tokio::runtime::Runtime;
+use tokio::time::{sleep, Duration};
+use tonic::transport::Endpoint;
+
+// Here we have an async function to benchmark
+// run batch_get_key 1000 during a session
+async fn run_batch_get_key(keys: &Vec<Vec<u8>>) {
+    let nonce = get_a_random_nonce();
+    let ep = "http://127.0.0.1:26659";
+    let rpc_endpoint = Endpoint::new(ep.to_string()).unwrap();
+    let channel = rpc_endpoint.connect_lazy();
+    let client = Arc::new(StorageNodeClient::new(channel));
+    let ns_vec = "my_twitter".as_bytes().to_vec();
+    let kp = get_a_static_keypair();
+    let addr = get_address_from_pk(&kp.public);
+    let signer = Db3Signer::new(kp);
+    let mut sdk = StoreSDK::new(client, signer);
+    let res = sdk.open_session().await;
+    assert!(res.is_ok());
+    let session_info = res.unwrap();
+    for i in 0..DEFAULT_SESSION_QUERY_LIMIT {
+        if let Ok(Some(values)) = sdk
+            .batch_get(&ns_vec, keys.clone(), &session_info.session_token)
+            .await
+        {
+            assert_eq!(values.values.len(), keys.len());
+        } else {
+            println!("fail to query keys");
+        }
+    }
+    let res = sdk.close_session(&session_info.session_token).await;
+}
+
+async fn init_kv_store(kv_size: i32) {
+    let nonce = get_a_random_nonce();
+
+    let ep = "http://127.0.0.1:26659";
+    let rpc_endpoint = Endpoint::new(ep.to_string()).unwrap();
+    let channel = rpc_endpoint.connect_lazy();
+    let client = Arc::new(StorageNodeClient::new(channel));
+    let mclient = client.clone();
+    let sclient = client.clone();
+
+    let ns_vec = "my_twitter".as_bytes().to_vec();
+    let kp = get_a_static_keypair();
+    let signer = Db3Signer::new(kp);
+    let msdk = MutationSDK::new(mclient, signer);
+    let mut kv_pairs = vec![];
+    for i in 0..kv_size {
+        kv_pairs.push(KvPair {
+            key: format!("bm_key_{}", i).as_bytes().to_vec(),
+            value: format!("bm_value_{}", i).as_bytes().to_vec(),
+            action: MutationAction::InsertKv.into(),
+        });
+    }
+    let mutation = Mutation {
+        ns: ns_vec.clone(),
+        kv_pairs,
+        nonce,
+        chain_id: ChainId::MainNet.into(),
+        chain_role: ChainRole::StorageShardChain.into(),
+        gas_price: None,
+        gas: 10,
+    };
+    println!("start submit mutation");
+    let result = msdk.submit_mutation(&mutation).await;
+    assert!(result.is_ok(), "{}", result.err().unwrap());
+    let two_sec = Duration::from_millis(2000);
+    println!("done submit mutation");
+    sleep(two_sec).await;
+    println!("wake up after submit");
+}
+fn criterion_benchmark(c: &mut Criterion) {
+    println!("criterion_benchmark....");
+
+    let rt = Runtime::new().unwrap();
+    rt.block_on(async {
+        init_kv_store(100).await;
+    });
+    let mut group = c.benchmark_group("batch get key 1000 requests per session");
+    for key_size in [1, 10, 100].iter() {
+        group.bench_with_input(
+            BenchmarkId::new("keys size", key_size),
+            key_size,
+            |b, &key_size| {
+                let mut keys = vec![];
+                for i in 0..key_size {
+                    keys.push(format!("bm_key_{}", i).as_bytes().to_vec());
+                }
+                b.to_async(&rt).iter(|| async {
+                    run_batch_get_key(&keys).await;
+                })
+            },
+        );
+    }
+}
+
+criterion_group!(benches, criterion_benchmark);
+criterion_main!(benches);
diff --git a/tools/start_localnet.sh b/tools/start_localnet.sh
index 2a918dc8..54198dc9 100644
--- a/tools/start_localnet.sh
+++ b/tools/start_localnet.sh
@@ -3,6 +3,12 @@
 # start_localnet.sh
 killall db3 tendermint
 test_dir=`pwd`
+BUILD_MODE='debug'
+if [[ $1 == 'release' ]] ; then
+  BUILD_MODE='release'
+fi
+
+echo "BUILD MODE: ${BUILD_MODE}"
 if [ -e ./tendermint ]
 then
     echo "tendermint exist"
@@ -27,7 +33,7 @@ then
     rm -rf db
 fi
 ./tendermint init
-../target/debug/db3 node >db3.log 2>&1  &
+../target/${BUILD_MODE}/db3 node >db3.log 2>&1  &
 sleep 1
 ./tendermint unsafe_reset_all && ./tendermint start
 sleep 1


Test patch: