Skip to content

Commit 38b5e4f

Browse files
committed
Add TransactionsImporterWithVacuum decorator
To execute a sqlite vacuum after a transaction importer work.
1 parent ad69945 commit 38b5e4f

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

mithril-signer/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod runtime;
1717
mod single_signer;
1818
mod transactions_importer_by_chunk;
1919
mod transactions_importer_with_pruner;
20+
mod transactions_importer_with_vacuum;
2021

2122
#[cfg(test)]
2223
pub use aggregator_client::dumb::DumbAggregatorClient;
@@ -32,6 +33,7 @@ pub use runtime::*;
3233
pub use single_signer::*;
3334
pub use transactions_importer_by_chunk::*;
3435
pub use transactions_importer_with_pruner::*;
36+
pub use transactions_importer_with_vacuum::*;
3537

3638
/// HTTP request timeout duration in milliseconds
3739
const HTTP_REQUEST_TIMEOUT_DURATION: u64 = 30000;
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
use std::sync::Arc;
2+
3+
use async_trait::async_trait;
4+
use slog::{debug, Logger};
5+
6+
use mithril_common::entities::BlockNumber;
7+
use mithril_common::signable_builder::TransactionsImporter;
8+
use mithril_common::StdResult;
9+
use mithril_persistence::sqlite::{vacuum_database, SqliteConnectionPool};
10+
11+
/// A decorator of [TransactionsImporter] that vacuums the database after running the import.
12+
pub struct TransactionsImporterWithVacuum {
13+
connection_pool: Arc<SqliteConnectionPool>,
14+
wrapped_importer: Arc<dyn TransactionsImporter>,
15+
logger: Logger,
16+
}
17+
18+
impl TransactionsImporterWithVacuum {
19+
/// Create a new instance of [TransactionsImporterWithVacuum].
20+
pub fn new(
21+
connection_pool: Arc<SqliteConnectionPool>,
22+
wrapped_importer: Arc<dyn TransactionsImporter>,
23+
logger: Logger,
24+
) -> Self {
25+
Self {
26+
connection_pool,
27+
wrapped_importer,
28+
logger,
29+
}
30+
}
31+
}
32+
33+
#[async_trait]
34+
impl TransactionsImporter for TransactionsImporterWithVacuum {
35+
async fn import(&self, up_to_beacon: BlockNumber) -> StdResult<()> {
36+
self.wrapped_importer.import(up_to_beacon).await?;
37+
38+
debug!(
39+
self.logger,
40+
"Transaction Import finished - Vacuuming database to reclaim disk space"
41+
);
42+
let connection = self.connection_pool.connection()?;
43+
vacuum_database(&connection)?;
44+
45+
Ok(())
46+
}
47+
}
48+
49+
#[cfg(test)]
50+
mod tests {
51+
use mockall::mock;
52+
use sqlite::Connection;
53+
54+
use mithril_common::test_utils::TempDir;
55+
use mithril_persistence::sqlite::SqliteConnection;
56+
57+
use super::*;
58+
59+
mock! {
60+
pub TransactionImporterImpl {}
61+
62+
#[async_trait]
63+
impl TransactionsImporter for TransactionImporterImpl {
64+
async fn import(&self, up_to_beacon: BlockNumber) -> StdResult<()>;
65+
}
66+
}
67+
68+
impl TransactionsImporterWithVacuum {
69+
pub fn new_with_mock<I>(
70+
connection_pool: Arc<SqliteConnectionPool>,
71+
importer_mock_config: I,
72+
) -> Self
73+
where
74+
I: FnOnce(&mut MockTransactionImporterImpl),
75+
{
76+
let mut transaction_importer = MockTransactionImporterImpl::new();
77+
importer_mock_config(&mut transaction_importer);
78+
79+
Self::new(
80+
connection_pool,
81+
Arc::new(transaction_importer),
82+
crate::test_tools::logger_for_tests(),
83+
)
84+
}
85+
}
86+
87+
/// Create a table, insert a lot of data and drop the table to make the database size grow.
88+
fn mangle_db(connection: &SqliteConnection) {
89+
connection
90+
.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, text TEXT);")
91+
.unwrap();
92+
connection
93+
.execute(format!(
94+
"INSERT INTO test (id, text) VALUES {}",
95+
(0..10_000)
96+
.map(|i| format!("({}, 'some text to fill the db')", i))
97+
.collect::<Vec<String>>()
98+
.join(", ")
99+
))
100+
.unwrap();
101+
connection.execute("DROP TABLE test").unwrap();
102+
}
103+
104+
#[tokio::test]
105+
async fn test_database_size_shrink_after_import() {
106+
let db_path = TempDir::create("mithril-persistence", "test_vacuum").join("test.db");
107+
let connection = Connection::open_thread_safe(&db_path).unwrap();
108+
// make the database size grow
109+
mangle_db(&connection);
110+
111+
let importer = TransactionsImporterWithVacuum::new_with_mock(
112+
Arc::new(SqliteConnectionPool::build_from_connection(connection)),
113+
|mock| {
114+
mock.expect_import().once().returning(|_| Ok(()));
115+
},
116+
);
117+
118+
let initial_size = db_path.metadata().unwrap().len();
119+
120+
importer.import(100).await.expect("Import should not fail");
121+
122+
let after_import_size = db_path.metadata().unwrap().len();
123+
124+
assert!(
125+
initial_size > after_import_size,
126+
"Database size did not shrink after import: \
127+
initial_size: {initial_size} -> after_import_size: {after_import_size}"
128+
);
129+
}
130+
}

0 commit comments

Comments
 (0)