forked from input-output-hk/mithril
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request input-output-hk#1748 from input-output-hk/ensemble…
…/1724/handle_rollbacks Handle rollbacks when reading blocks from the chain
- Loading branch information
Showing
28 changed files
with
1,245 additions
and
166 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
139 changes: 139 additions & 0 deletions
139
internal/mithril-persistence/src/database/query/block_range_root/delete_block_range_root.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
use anyhow::Context; | ||
use sqlite::Value; | ||
|
||
use mithril_common::entities::{BlockNumber, BlockRange}; | ||
use mithril_common::StdResult; | ||
|
||
use crate::database::record::BlockRangeRootRecord; | ||
use crate::sqlite::{Query, SourceAlias, SqLiteEntity, WhereCondition}; | ||
|
||
/// Query to delete old [BlockRangeRootRecord] from the sqlite database | ||
pub struct DeleteBlockRangeRootQuery { | ||
condition: WhereCondition, | ||
} | ||
|
||
impl Query for DeleteBlockRangeRootQuery { | ||
type Entity = BlockRangeRootRecord; | ||
|
||
fn filters(&self) -> WhereCondition { | ||
self.condition.clone() | ||
} | ||
|
||
fn get_definition(&self, condition: &str) -> String { | ||
// it is important to alias the fields with the same name as the table | ||
// since the table cannot be aliased in a RETURNING statement in SQLite. | ||
let aliases = SourceAlias::new(&[("{:block_range_root:}", "block_range_root")]); | ||
let projection = Self::Entity::get_projection().expand(aliases); | ||
|
||
format!("delete from block_range_root where {condition} returning {projection}") | ||
} | ||
} | ||
|
||
impl DeleteBlockRangeRootQuery { | ||
pub fn contains_or_above_block_number_threshold( | ||
block_number_threshold: BlockNumber, | ||
) -> StdResult<Self> { | ||
let block_range = BlockRange::from_block_number(block_number_threshold); | ||
let threshold = Value::Integer(block_range.start.try_into().with_context(|| { | ||
format!("Failed to convert threshold `{block_number_threshold}` to i64") | ||
})?); | ||
|
||
Ok(Self { | ||
condition: WhereCondition::new("start >= ?*", vec![threshold]), | ||
}) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use mithril_common::crypto_helper::MKTreeNode; | ||
use mithril_common::entities::BlockRange; | ||
|
||
use crate::database::query::{GetBlockRangeRootQuery, InsertBlockRangeRootQuery}; | ||
use crate::database::test_helper::cardano_tx_db_connection; | ||
use crate::sqlite::{ConnectionExtensions, SqliteConnection}; | ||
|
||
use super::*; | ||
|
||
fn insert_block_range_roots(connection: &SqliteConnection, records: Vec<BlockRangeRootRecord>) { | ||
connection | ||
.fetch_first(InsertBlockRangeRootQuery::insert_many(records).unwrap()) | ||
.unwrap(); | ||
} | ||
|
||
fn block_range_root_dataset() -> Vec<BlockRangeRootRecord> { | ||
[ | ||
( | ||
BlockRange::from_block_number(BlockRange::LENGTH), | ||
MKTreeNode::from_hex("AAAA").unwrap(), | ||
), | ||
( | ||
BlockRange::from_block_number(BlockRange::LENGTH * 2), | ||
MKTreeNode::from_hex("BBBB").unwrap(), | ||
), | ||
( | ||
BlockRange::from_block_number(BlockRange::LENGTH * 3), | ||
MKTreeNode::from_hex("CCCC").unwrap(), | ||
), | ||
] | ||
.into_iter() | ||
.map(BlockRangeRootRecord::from) | ||
.collect() | ||
} | ||
|
||
#[test] | ||
fn test_prune_work_even_without_block_range_root_in_db() { | ||
let connection = cardano_tx_db_connection().unwrap(); | ||
|
||
let cursor = connection | ||
.fetch( | ||
DeleteBlockRangeRootQuery::contains_or_above_block_number_threshold(100).unwrap(), | ||
) | ||
.expect("pruning shouldn't crash without block range root stored"); | ||
assert_eq!(0, cursor.count()); | ||
} | ||
|
||
#[test] | ||
fn test_prune_all_data_if_given_block_number_is_lower_than_stored_number_of_block() { | ||
parameterized_test_prune_block_range(0, block_range_root_dataset().len()); | ||
} | ||
|
||
#[test] | ||
fn test_prune_keep_all_block_range_root_if_given_number_of_block_is_greater_than_the_highest_one( | ||
) { | ||
parameterized_test_prune_block_range(100_000, 0); | ||
} | ||
|
||
#[test] | ||
fn test_prune_block_range_when_block_number_is_block_range_start() { | ||
parameterized_test_prune_block_range(BlockRange::LENGTH * 2, 2); | ||
} | ||
|
||
#[test] | ||
fn test_prune_block_range_when_block_number_is_in_block_range() { | ||
parameterized_test_prune_block_range(BlockRange::LENGTH * 2 + 1, 2); | ||
} | ||
|
||
#[test] | ||
fn test_keep_block_range_when_block_number_is_just_before_range_start() { | ||
parameterized_test_prune_block_range(BlockRange::LENGTH * 2 - 1, 3); | ||
} | ||
|
||
fn parameterized_test_prune_block_range( | ||
block_threshold: BlockNumber, | ||
delete_record_number: usize, | ||
) { | ||
let connection = cardano_tx_db_connection().unwrap(); | ||
let dataset = block_range_root_dataset(); | ||
insert_block_range_roots(&connection, dataset.clone()); | ||
|
||
let query = | ||
DeleteBlockRangeRootQuery::contains_or_above_block_number_threshold(block_threshold) | ||
.unwrap(); | ||
let cursor = connection.fetch(query).unwrap(); | ||
assert_eq!(delete_record_number, cursor.count()); | ||
|
||
let cursor = connection.fetch(GetBlockRangeRootQuery::all()).unwrap(); | ||
assert_eq!(dataset.len() - delete_record_number, cursor.count()); | ||
} | ||
} |
2 changes: 2 additions & 0 deletions
2
internal/mithril-persistence/src/database/query/block_range_root/mod.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
mod delete_block_range_root; | ||
mod get_block_range_root; | ||
mod get_interval_without_block_range; | ||
mod insert_block_range; | ||
|
||
pub use delete_block_range_root::*; | ||
pub use get_block_range_root::*; | ||
pub use get_interval_without_block_range::*; | ||
pub use insert_block_range::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.