Skip to content

Commit c611e45

Browse files
dlachaumesfauvel
authored andcommitted
First implementation of benchmark for store_transactions
This compare the behavior of 'store_transactions' with differents chunks sizes and leaving the transactions 'collect()' after the insert or not.
1 parent 204c7e4 commit c611e45

File tree

4 files changed

+145
-1
lines changed

4 files changed

+145
-1
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mithril-signer/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ homepage = { workspace = true }
99
license = { workspace = true }
1010
repository = { workspace = true }
1111

12+
[[bench]]
13+
name = "cardano_transactions"
14+
harness = false
15+
1216
[dependencies]
1317
anyhow = "1.0.79"
1418
async-trait = "0.1.77"
@@ -42,6 +46,7 @@ tokio = { version = "1.37.0", features = ["full"] }
4246
tikv-jemallocator = { version = "0.5.4", optional = true }
4347

4448
[dev-dependencies]
49+
criterion = { version = "0.5.1", features = ["html_reports", "async_tokio"] }
4550
httpmock = "0.7.0"
4651
mithril-common = { path = "../mithril-common" }
4752
mockall = "0.12.1"
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use std::{path::Path, sync::Arc};
2+
3+
use criterion::{criterion_group, criterion_main, Criterion};
4+
use mithril_common::{entities::CardanoTransaction, StdResult};
5+
use mithril_persistence::sqlite::ConnectionBuilder;
6+
use mithril_signer::{database::repository::CardanoTransactionRepository, TransactionStore};
7+
use sqlite::ConnectionThreadSafe;
8+
9+
fn cardano_tx_db_connection() -> StdResult<ConnectionThreadSafe> {
10+
let db_path = Path::new("./cardano_tx.db");
11+
12+
if db_path.exists() {
13+
std::fs::remove_file(db_path)?;
14+
}
15+
16+
let connection = ConnectionBuilder::open_file(db_path)
17+
.with_migrations(mithril_signer::database::cardano_transaction_migration::get_migrations())
18+
.build()?;
19+
Ok(connection)
20+
}
21+
22+
fn generate_transactions(nb_transactions: usize) -> Vec<CardanoTransaction> {
23+
(0..nb_transactions)
24+
.map(|i| {
25+
CardanoTransaction::new(
26+
format!("tx_hash-{}", i),
27+
i as u64,
28+
i as u64 + 1,
29+
format!("block_hash-{}", i),
30+
i as u64 + 2,
31+
)
32+
})
33+
.collect()
34+
}
35+
36+
fn bench_store_transactions_transactions(c: &mut Criterion) {
37+
const NB_CARDANO_TRANSACTIONS: usize = 100000;
38+
let runtime = tokio::runtime::Runtime::new().unwrap();
39+
40+
let mut group = c.benchmark_group("Create transactions");
41+
42+
group.bench_function("store_transactions", |bencher| {
43+
bencher.to_async(&runtime).iter(|| async {
44+
let connection = Arc::new(cardano_tx_db_connection().unwrap());
45+
let repository = CardanoTransactionRepository::new(connection);
46+
repository
47+
.store_transactions(generate_transactions(NB_CARDANO_TRANSACTIONS))
48+
.await
49+
});
50+
});
51+
52+
for chunks_size in [10, 50, 100, 200] {
53+
group.bench_function(
54+
format!("store_transactions_with_chunks_size = {}", chunks_size),
55+
|bencher| {
56+
bencher.to_async(&runtime).iter(|| async {
57+
let connection = Arc::new(cardano_tx_db_connection().unwrap());
58+
let repository = CardanoTransactionRepository::new(connection.clone());
59+
repository
60+
.store_transactions_with_chunks_size(
61+
generate_transactions(NB_CARDANO_TRANSACTIONS),
62+
chunks_size,
63+
)
64+
.await
65+
});
66+
},
67+
);
68+
}
69+
70+
for chunks_size in [10, 50, 100, 200] {
71+
group.bench_function(
72+
format!(
73+
"store_transactions_with_chunks_size_and_collect = {}",
74+
chunks_size
75+
),
76+
|bencher| {
77+
bencher.to_async(&runtime).iter(|| async {
78+
let connection = Arc::new(cardano_tx_db_connection().unwrap());
79+
let repository = CardanoTransactionRepository::new(connection.clone());
80+
repository
81+
.store_transactions_with_chunks_size_and_collect(
82+
generate_transactions(NB_CARDANO_TRANSACTIONS),
83+
chunks_size,
84+
)
85+
.await
86+
});
87+
},
88+
);
89+
}
90+
91+
group.finish();
92+
}
93+
94+
criterion_group!(benches, bench_store_transactions_transactions);
95+
criterion_main!(benches);

mithril-signer/src/database/repository/cardano_transaction_repository.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,21 @@ impl CardanoTransactionRepository {
116116
let records: Vec<CardanoTransactionRecord> =
117117
transactions.into_iter().map(|tx| tx.into()).collect();
118118

119+
let provider = InsertCardanoTransactionProvider::new(&self.connection);
120+
let filters = provider.get_insert_many_condition(records)?;
121+
provider.find(filters)?.next();
122+
123+
Ok(vec![])
124+
}
125+
126+
/// Create new [CardanoTransactionRecord]s in the database.
127+
pub async fn create_transactions_and_collect<T: Into<CardanoTransactionRecord>>(
128+
&self,
129+
transactions: Vec<T>,
130+
) -> StdResult<Vec<CardanoTransactionRecord>> {
131+
let records: Vec<CardanoTransactionRecord> =
132+
transactions.into_iter().map(|tx| tx.into()).collect();
133+
119134
let provider = InsertCardanoTransactionProvider::new(&self.connection);
120135
let filters = provider.get_insert_many_condition(records)?;
121136
let cursor = provider.find(filters)?;
@@ -138,6 +153,34 @@ impl CardanoTransactionRepository {
138153
Ok(cursor.collect())
139154
}
140155

156+
pub async fn store_transactions_with_chunks_size(
157+
&self,
158+
transactions: Vec<CardanoTransaction>,
159+
chunks_size: usize,
160+
) -> StdResult<()> {
161+
// Chunk transactions to avoid an error when we exceed sqlite binding limitations
162+
for transactions_in_chunk in transactions.chunks(chunks_size) {
163+
self.create_transactions(transactions_in_chunk.to_vec())
164+
.await
165+
.with_context(|| "CardanoTransactionRepository can not store transactions")?;
166+
}
167+
Ok(())
168+
}
169+
170+
pub async fn store_transactions_with_chunks_size_and_collect(
171+
&self,
172+
transactions: Vec<CardanoTransaction>,
173+
chunks_size: usize,
174+
) -> StdResult<()> {
175+
// Chunk transactions to avoid an error when we exceed sqlite binding limitations
176+
for transactions_in_chunk in transactions.chunks(chunks_size) {
177+
self.create_transactions_and_collect(transactions_in_chunk.to_vec())
178+
.await
179+
.with_context(|| "CardanoTransactionRepository can not store transactions")?;
180+
}
181+
Ok(())
182+
}
183+
141184
// TODO: remove this function when the Cardano transaction signature is based on block number instead of immutable number
142185
async fn get_highest_block_number_for_immutable_number(
143186
&self,
@@ -238,7 +281,7 @@ impl TransactionStore for CardanoTransactionRepository {
238281
async fn store_transactions(&self, transactions: Vec<CardanoTransaction>) -> StdResult<()> {
239282
// Chunk transactions to avoid an error when we exceed sqlite binding limitations
240283
for transactions_in_chunk in transactions.chunks(100) {
241-
self.create_transactions(transactions_in_chunk.to_vec())
284+
self.create_transactions_and_collect(transactions_in_chunk.to_vec())
242285
.await
243286
.with_context(|| "CardanoTransactionRepository can not store transactions")?;
244287
}

0 commit comments

Comments
 (0)