Skip to content

Commit 045d549

Browse files
committed
Fetch only the lower bound (highest block range) in db
Instead of also fetching the upper bound (that was the highest stored transaction block number). Now the upper bound is given as a parameter from the importer caller, so the previous request can be simplified.
1 parent 28c68f3 commit 045d549

File tree

4 files changed

+101
-28
lines changed

4 files changed

+101
-28
lines changed

internal/mithril-persistence/src/database/query/block_range_root/get_block_range_root.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ impl GetBlockRangeRootQuery {
2222
condition: WhereCondition::new("start < ?*", vec![Value::Integer(block_number as i64)]),
2323
}
2424
}
25+
26+
pub fn highest() -> Self {
27+
Self {
28+
condition: WhereCondition::new("end = (select max(end) from block_range_root)", vec![]),
29+
}
30+
}
2531
}
2632

2733
impl Query for GetBlockRangeRootQuery {
@@ -134,4 +140,26 @@ mod tests {
134140

135141
assert_eq!(dataset, cursor);
136142
}
143+
144+
#[test]
145+
fn test_get_highest_with_empty_db() {
146+
let connection = cardano_tx_db_connection().unwrap();
147+
148+
let cursor: Option<BlockRangeRootRecord> = connection
149+
.fetch_first(GetBlockRangeRootQuery::highest())
150+
.unwrap();
151+
assert_eq!(None, cursor);
152+
}
153+
154+
#[test]
155+
fn test_get_highest() {
156+
let connection = cardano_tx_db_connection().unwrap();
157+
let dataset = block_range_root_dataset();
158+
insert_block_range_roots(&connection, dataset.clone());
159+
160+
let cursor: Option<BlockRangeRootRecord> = connection
161+
.fetch_first(GetBlockRangeRootQuery::highest())
162+
.unwrap();
163+
assert_eq!(dataset.last().cloned(), cursor);
164+
}
137165
}

internal/mithril-persistence/src/database/repository/cardano_transaction_repository.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,15 @@ impl CardanoTransactionRepository {
164164
Ok(Box::new(block_range_roots.into_iter()))
165165
}
166166

167+
/// Retrieve the block range root with the highest bounds in the database.
168+
pub async fn retrieve_highest_block_range_root(
169+
&self,
170+
) -> StdResult<Option<BlockRangeRootRecord>> {
171+
self.connection_pool
172+
.connection()?
173+
.fetch_first(GetBlockRangeRootQuery::highest())
174+
}
175+
167176
/// Retrieve all the [CardanoTransaction] in database.
168177
pub async fn get_all(&self) -> StdResult<Vec<CardanoTransaction>> {
169178
let records = self
@@ -980,6 +989,38 @@ mod tests {
980989
);
981990
}
982991

992+
#[tokio::test]
993+
async fn repository_retrieve_highest_block_range_roots() {
994+
let connection = cardano_tx_db_connection().unwrap();
995+
let repository = CardanoTransactionRepository::new(Arc::new(
996+
SqliteConnectionPool::build_from_connection(connection),
997+
));
998+
let block_range_roots = vec![
999+
BlockRangeRootRecord {
1000+
range: BlockRange::from_block_number(15),
1001+
merkle_root: MKTreeNode::from_hex("AAAA").unwrap(),
1002+
},
1003+
BlockRangeRootRecord {
1004+
range: BlockRange::from_block_number(30),
1005+
merkle_root: MKTreeNode::from_hex("BBBB").unwrap(),
1006+
},
1007+
BlockRangeRootRecord {
1008+
range: BlockRange::from_block_number(45),
1009+
merkle_root: MKTreeNode::from_hex("CCCC").unwrap(),
1010+
},
1011+
];
1012+
repository
1013+
.create_block_range_roots(block_range_roots.clone())
1014+
.await
1015+
.unwrap();
1016+
1017+
let retrieved_block_range = repository
1018+
.retrieve_highest_block_range_root()
1019+
.await
1020+
.unwrap();
1021+
assert_eq!(block_range_roots.last().cloned(), retrieved_block_range);
1022+
}
1023+
9831024
#[tokio::test]
9841025
async fn repository_prune_transactions() {
9851026
let connection = cardano_tx_db_connection().unwrap();

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ impl TransactionStore for CardanoTransactionRepository {
1717
self.get_transaction_highest_chain_point().await
1818
}
1919

20+
async fn get_highest_block_range(&self) -> StdResult<Option<BlockRange>> {
21+
let record = self.retrieve_highest_block_range_root().await?;
22+
Ok(record.map(|record| record.range))
23+
}
24+
2025
async fn store_transactions(&self, transactions: Vec<CardanoTransaction>) -> StdResult<()> {
2126
self.store_transactions(transactions).await
2227
}

mithril-aggregator/src/services/cardano_transactions_importer.rs

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ pub trait TransactionStore: Send + Sync {
2222
/// Get the highest known transaction beacon
2323
async fn get_highest_beacon(&self) -> StdResult<Option<ChainPoint>>;
2424

25+
/// Get the highest stored block range root bounds
26+
async fn get_highest_block_range(&self) -> StdResult<Option<BlockRange>>;
27+
2528
/// Store list of transactions
2629
async fn store_transactions(&self, transactions: Vec<CardanoTransaction>) -> StdResult<()>;
2730

@@ -130,15 +133,15 @@ impl CardanoTransactionsImporter {
130133
}
131134

132135
async fn import_block_ranges(&self, until: BlockNumber) -> StdResult<()> {
133-
let block_ranges = match self
134-
.transaction_store
135-
.get_block_interval_without_block_range_root()
136-
.await?
137-
// .map(|range| BlockRange::all_block_ranges_in(BlockRange::start(range.start)..range.end))
138-
.map(|range| BlockRange::all_block_ranges_in(BlockRange::start(range.start)..=(until)))
139-
{
140-
// Everything is already computed
141-
None => return Ok(()),
136+
let block_ranges = match self.transaction_store.get_highest_block_range().await?.map(
137+
|highest_stored_block_range| {
138+
BlockRange::all_block_ranges_in(
139+
BlockRange::start(highest_stored_block_range.end)..=(until),
140+
)
141+
},
142+
) {
143+
// No block range root stored yet, start from the beginning
144+
None => BlockRange::all_block_ranges_in(0..=(until)),
142145
// Not enough block to form at least one block range
143146
Some(ranges) if ranges.is_empty() => return Ok(()),
144147
Some(ranges) => ranges,
@@ -602,8 +605,7 @@ mod tests {
602605
SqliteConnectionPool::build_from_connection(connection),
603606
)));
604607

605-
// Transactions for all blocks in the (15..=25) interval, meaning that the last blocks in
606-
// the block range (15..30) don't have transactions
608+
// For the block range (15..=29) we only have transactions in the 10 first blocks (15..=24)
607609
let blocks = build_blocks(BlockRange::LENGTH, 10);
608610
let transactions = into_transactions(&blocks);
609611
repository.store_transactions(transactions).await.unwrap();
@@ -632,31 +634,29 @@ mod tests {
632634
async fn block_range_root_retrieves_only_strictly_required_transactions() {
633635
fn transactions_for_block(range: Range<BlockNumber>) -> StdResult<Vec<CardanoTransaction>> {
634636
Ok(build_blocks(range.start, range.count() as BlockNumber)
635-
.iter()
636-
.flat_map(|b| b.clone().into_transactions())
637+
.into_iter()
638+
.flat_map(|b| b.into_transactions())
637639
.collect())
638640
}
641+
const HIGHEST_BLOCK_RANGE_START: BlockNumber = BlockRange::LENGTH;
642+
const UP_TO_BLOCK_NUMBER: BlockNumber = BlockRange::LENGTH * 5;
639643

640644
let importer = {
641645
let mut store_mock = MockTransactionStore::new();
642646
store_mock
643-
.expect_get_block_interval_without_block_range_root()
644-
// Specification of the interval without block range root
645-
// Note: in reality the lower bound will always be a multiple of BlockRange::LENGTH
646-
// since it's computed from the `block_range_root` table
647-
.returning(|| Ok(Some((BlockRange::LENGTH + 2)..(BlockRange::LENGTH * 5))))
647+
.expect_get_highest_block_range()
648+
.returning(|| {
649+
Ok(Some(BlockRange::from_block_number(
650+
HIGHEST_BLOCK_RANGE_START,
651+
)))
652+
})
648653
.once();
649654
store_mock
650655
.expect_get_transactions_in_range()
651-
// Lower bound should be the block number that start after the last known block range end
652-
//
653-
// if it's not a multiple of BlockRange::LENGTH, it should be the start block number
654-
// of the block range that contains the end of the last known block range.
655-
//
656-
// Upper bound should be the block number of the highest transaction in a db that can be
657-
// included in a block range
656+
// Lower bound should be the end block number of the last known block range
657+
// Upper bound should be the block number provided to `import_block_ranges`
658658
.withf(|range| {
659-
BlockRangesSequence::new(BlockRange::LENGTH..=(BlockRange::LENGTH * 5))
659+
BlockRangesSequence::new(HIGHEST_BLOCK_RANGE_START..=UP_TO_BLOCK_NUMBER)
660660
.contains(range)
661661
})
662662
.returning(transactions_for_block);
@@ -670,9 +670,8 @@ mod tests {
670670
)
671671
};
672672

673-
// todo: update this test after reworking expect_get_block_interval_without_block_range_root
674673
importer
675-
.import_block_ranges(BlockRange::LENGTH * 5)
674+
.import_block_ranges(UP_TO_BLOCK_NUMBER)
676675
.await
677676
.expect("Transactions Importer should succeed");
678677
}

0 commit comments

Comments
 (0)