Skip to content

Commit d6bc0a7

Browse files
committed
Add a decorator of TransactionImporter to add tx pruning capability
1 parent 52ed633 commit d6bc0a7

File tree

4 files changed

+149
-4
lines changed

4 files changed

+149
-4
lines changed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use mithril_common::entities::{BlockNumber, BlockRange, CardanoTransaction, Immu
77
use mithril_common::StdResult;
88
use mithril_persistence::database::repository::CardanoTransactionRepository;
99

10-
use crate::TransactionStore;
10+
use crate::{TransactionPruner, TransactionStore};
1111

1212
#[async_trait]
1313
impl TransactionStore for CardanoTransactionRepository {
@@ -46,3 +46,10 @@ impl TransactionStore for CardanoTransactionRepository {
4646
Ok(())
4747
}
4848
}
49+
50+
#[async_trait]
51+
impl TransactionPruner for CardanoTransactionRepository {
52+
async fn prune(&self, number_of_blocks_to_keep: BlockNumber) -> StdResult<()> {
53+
self.prune_transaction(number_of_blocks_to_keep).await
54+
}
55+
}

mithril-signer/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub mod metrics;
1515
mod protocol_initializer_store;
1616
mod runtime;
1717
mod single_signer;
18+
mod transactions_importer_with_prune_decorator;
1819

1920
#[cfg(test)]
2021
pub use aggregator_client::dumb::DumbAggregatorClient;
@@ -28,6 +29,7 @@ pub use metrics::*;
2829
pub use protocol_initializer_store::{ProtocolInitializerStore, ProtocolInitializerStorer};
2930
pub use runtime::*;
3031
pub use single_signer::*;
32+
pub use transactions_importer_with_prune_decorator::*;
3133

3234
/// HTTP request timeout duration in milliseconds
3335
const HTTP_REQUEST_TIMEOUT_DURATION: u64 = 30000;

mithril-signer/src/runtime/signer_services.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,17 +265,23 @@ impl<'a> ServiceBuilder for ProductionServiceBuilder<'a> {
265265
let transaction_store = Arc::new(CardanoTransactionRepository::new(
266266
transaction_sqlite_connection,
267267
));
268-
let transactions_importer = CardanoTransactionsImporter::new(
268+
let transactions_importer = Arc::new(CardanoTransactionsImporter::new(
269269
block_scanner,
270270
transaction_store.clone(),
271271
&self.config.db_directory,
272272
// Rescan the last immutable when importing transactions, it may have been partially imported
273273
Some(1),
274274
slog_scope::logger(),
275-
);
275+
));
276+
// Wrap the transaction importer with decorator to prune the transactions after import
277+
let transactions_importer = Arc::new(crate::TransactionsImporterWithPruneDecorator::new(
278+
None,
279+
transaction_store.clone(),
280+
transactions_importer,
281+
));
276282
let block_range_root_retriever = transaction_store.clone();
277283
let cardano_transactions_builder = Arc::new(CardanoTransactionsSignableBuilder::new(
278-
Arc::new(transactions_importer),
284+
transactions_importer,
279285
block_range_root_retriever,
280286
slog_scope::logger(),
281287
));
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+
5+
use mithril_common::entities::{BlockNumber, ImmutableFileNumber};
6+
use mithril_common::signable_builder::TransactionsImporter;
7+
use mithril_common::StdResult;
8+
9+
/// Cardano transactions pruner
10+
#[cfg_attr(test, mockall::automock)]
11+
#[async_trait]
12+
pub trait TransactionPruner: Send + Sync {
13+
/// Prune the transactions older than the given number of blocks (based on the block range root
14+
/// stored).
15+
async fn prune(&self, number_of_blocks_to_keep: BlockNumber) -> StdResult<()>;
16+
}
17+
18+
/// A decorator of [TransactionsImporter] that prunes the transactions older than a given number of
19+
/// blocks after running the import.
20+
///
21+
/// If the number of blocks to keep is not provided, no pruning is performed.
22+
pub struct TransactionsImporterWithPruneDecorator {
23+
number_of_blocks_to_keep: Option<BlockNumber>,
24+
transaction_pruner: Arc<dyn TransactionPruner>,
25+
wrapped_importer: Arc<dyn TransactionsImporter>,
26+
}
27+
28+
impl TransactionsImporterWithPruneDecorator {
29+
/// Create a new instance of [TransactionsImporterWithPruneDecorator].
30+
pub fn new(
31+
number_of_blocks_to_keep: Option<BlockNumber>,
32+
transaction_pruner: Arc<dyn TransactionPruner>,
33+
wrapped_importer: Arc<dyn TransactionsImporter>,
34+
) -> Self {
35+
Self {
36+
number_of_blocks_to_keep,
37+
transaction_pruner,
38+
wrapped_importer,
39+
}
40+
}
41+
}
42+
43+
#[async_trait]
44+
impl TransactionsImporter for TransactionsImporterWithPruneDecorator {
45+
async fn import(&self, up_to_beacon: ImmutableFileNumber) -> StdResult<()> {
46+
self.wrapped_importer.import(up_to_beacon).await?;
47+
48+
if let Some(number_of_blocks_to_keep) = self.number_of_blocks_to_keep {
49+
self.transaction_pruner
50+
.prune(number_of_blocks_to_keep)
51+
.await?;
52+
}
53+
54+
Ok(())
55+
}
56+
}
57+
58+
#[cfg(test)]
59+
mod tests {
60+
use mockall::mock;
61+
use mockall::predicate::eq;
62+
63+
use super::*;
64+
65+
mock! {
66+
pub TransactionImporterImpl {}
67+
68+
#[async_trait]
69+
impl TransactionsImporter for TransactionImporterImpl {
70+
async fn import(&self, up_to_beacon: ImmutableFileNumber) -> StdResult<()>;
71+
}
72+
}
73+
74+
impl TransactionsImporterWithPruneDecorator {
75+
pub fn new_with_mock<Ti, Pr>(
76+
number_of_blocks_to_keep: Option<BlockNumber>,
77+
transaction_pruner_mock_config: Pr,
78+
importer_mock_config: Ti,
79+
) -> Self
80+
where
81+
Pr: FnOnce(&mut MockTransactionPruner),
82+
Ti: FnOnce(&mut MockTransactionImporterImpl),
83+
{
84+
let mut transaction_pruner = MockTransactionPruner::new();
85+
transaction_pruner_mock_config(&mut transaction_pruner);
86+
let mut transaction_importer = MockTransactionImporterImpl::new();
87+
importer_mock_config(&mut transaction_importer);
88+
89+
Self::new(
90+
number_of_blocks_to_keep,
91+
Arc::new(transaction_pruner),
92+
Arc::new(transaction_importer),
93+
)
94+
}
95+
}
96+
97+
#[tokio::test]
98+
async fn test_does_not_prune_if_none_is_configured() {
99+
let importer = TransactionsImporterWithPruneDecorator::new_with_mock(
100+
None,
101+
|mock| {
102+
mock.expect_prune().never();
103+
},
104+
|mock| {
105+
mock.expect_import().once().returning(|_| Ok(()));
106+
},
107+
);
108+
109+
importer.import(10).await.expect("Import should not fail");
110+
}
111+
112+
#[tokio::test]
113+
async fn test_does_prune_if_a_block_number_is_configured() {
114+
let expected_block_number: BlockNumber = 5;
115+
let importer = TransactionsImporterWithPruneDecorator::new_with_mock(
116+
Some(expected_block_number),
117+
|mock| {
118+
mock.expect_prune()
119+
.with(eq(expected_block_number))
120+
.once()
121+
.returning(|_| Ok(()));
122+
},
123+
|mock| {
124+
mock.expect_import().once().returning(|_| Ok(()));
125+
},
126+
);
127+
128+
importer.import(10).await.expect("Import should not fail");
129+
}
130+
}

0 commit comments

Comments
 (0)