feat(prune): take PruneMode::Full into account when validating the config (#3810)

This commit is contained in:
Alexey Shekhirin
2023-07-25 18:20:37 +01:00
committed by GitHub
parent 19aa11834f
commit 9a07f57762
4 changed files with 63 additions and 27 deletions

View File

@@ -1,4 +1,4 @@
use crate::{serde_helper::deserialize_opt_prune_mode_with_min_distance, BlockNumber, PruneMode};
use crate::{serde_helper::deserialize_opt_prune_mode_with_min_blocks, BlockNumber, PruneMode};
use paste::paste;
use serde::{Deserialize, Serialize};
@@ -15,7 +15,7 @@ pub struct PruneModes {
/// Receipts pruning configuration.
#[serde(
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_opt_prune_mode_with_min_distance::<64, _>"
deserialize_with = "deserialize_opt_prune_mode_with_min_blocks::<64, _>"
)]
pub receipts: Option<PruneMode>,
/// Account History pruning configuration.
@@ -27,7 +27,7 @@ pub struct PruneModes {
}
macro_rules! impl_prune_parts {
($(($part:ident, $human_part:expr)),+) => {
($(($part:ident, $human_part:expr, $min_blocks:expr)),+) => {
$(
paste! {
#[doc = concat!(
@@ -51,8 +51,12 @@ macro_rules! impl_prune_parts {
$human_part,
" pruning needs to be done, inclusive, according to the provided tip."
)]
pub fn [<prune_to_block_ $part>](&self, tip: BlockNumber) -> Option<(BlockNumber, PruneMode)> {
self.$part.as_ref().map(|mode| (self.prune_to_block(mode, tip), *mode))
pub fn [<prune_target_block_ $part>](&self, tip: BlockNumber) -> Option<(BlockNumber, PruneMode)> {
self.$part.as_ref().and_then(|mode| {
self.prune_target_block(mode, tip, $min_blocks).map(|block| {
(block, *mode)
})
})
}
}
)+
@@ -90,20 +94,30 @@ impl PruneModes {
}
/// Returns block up to which pruning needs to be done, inclusive, according to the provided
/// prune mode and tip.
pub fn prune_to_block(&self, mode: &PruneMode, tip: BlockNumber) -> BlockNumber {
/// prune mode, tip block number and minimum number of blocks allowed to be pruned.
pub fn prune_target_block(
&self,
mode: &PruneMode,
tip: BlockNumber,
min_blocks: Option<u64>,
) -> Option<BlockNumber> {
match mode {
PruneMode::Full => tip,
PruneMode::Distance(distance) => tip.saturating_sub(*distance),
PruneMode::Before(n) => *n,
PruneMode::Full if min_blocks.unwrap_or_default() == 0 => Some(tip),
PruneMode::Distance(distance) if *distance >= min_blocks.unwrap_or_default() => {
Some(tip.saturating_sub(*distance))
}
PruneMode::Before(n) if tip.saturating_sub(*n) >= min_blocks.unwrap_or_default() => {
Some(*n)
}
_ => None,
}
}
impl_prune_parts!(
(sender_recovery, "Sender Recovery"),
(transaction_lookup, "Transaction Lookup"),
(receipts, "Receipts"),
(account_history, "Account History"),
(storage_history, "Storage History")
(sender_recovery, "Sender Recovery", None),
(transaction_lookup, "Transaction Lookup", None),
(receipts, "Receipts", Some(64)),
(account_history, "Account History", None),
(storage_history, "Storage History", None)
);
}

View File

@@ -11,7 +11,7 @@ pub use jsonu256::*;
pub mod num;
mod prune;
pub use prune::deserialize_opt_prune_mode_with_min_distance;
pub use prune::deserialize_opt_prune_mode_with_min_blocks;
/// serde functions for handling primitive `u64` as [U64](crate::U64)
pub mod u64_hex {

View File

@@ -1,11 +1,17 @@
use crate::PruneMode;
use serde::{Deserialize, Deserializer};
/// Deserializes [`Option<PruneMode>`] and validates that the value contained in
/// [PruneMode::Distance] (if any) is not less than the const generic parameter `MIN_DISTANCE`.
pub fn deserialize_opt_prune_mode_with_min_distance<
/// Deserializes [`Option<PruneMode>`] and validates that the value is not less than the const
/// generic parameter `MIN_BLOCKS`. This parameter represents the number of blocks that needs to be
/// left in database after the pruning.
///
/// 1. For [PruneMode::Full], it fails if `MIN_BLOCKS > 0`.
/// 2. For [PruneMode::Distance(distance)], it fails if `distance < MIN_BLOCKS + 1`. `+ 1` is needed
/// because `PruneMode::Distance(0)` means that we leave zero blocks from the latest, meaning we
/// have one block in the database.
pub fn deserialize_opt_prune_mode_with_min_blocks<
'de,
const MIN_DISTANCE: u64,
const MIN_BLOCKS: u64,
D: Deserializer<'de>,
>(
deserializer: D,
@@ -13,11 +19,20 @@ pub fn deserialize_opt_prune_mode_with_min_distance<
let prune_mode = Option::<PruneMode>::deserialize(deserializer)?;
match prune_mode {
Some(PruneMode::Distance(distance)) if distance < MIN_DISTANCE => {
Some(PruneMode::Full) if MIN_BLOCKS > 0 => {
Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str("full"),
// This message should have "expected" wording
&format!("prune mode that leaves at least {MIN_BLOCKS} blocks in the database")
.as_str(),
))
}
Some(PruneMode::Distance(distance)) if distance < MIN_BLOCKS => {
Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Unsigned(distance),
// This message should have "expected" wording, so we say "not less than"
&format!("prune mode distance not less than {MIN_DISTANCE} blocks").as_str(),
// This message should have "expected" wording
&format!("prune mode that leaves at least {MIN_BLOCKS} blocks in the database")
.as_str(),
))
}
_ => Ok(prune_mode),
@@ -31,11 +46,11 @@ mod test {
use serde::Deserialize;
#[test]
fn deserialize_opt_prune_mode_with_min_distance() {
fn deserialize_opt_prune_mode_with_min_blocks() {
#[derive(Debug, Deserialize, PartialEq, Eq)]
struct V(
#[serde(
deserialize_with = "super::deserialize_opt_prune_mode_with_min_distance::<10, _>"
deserialize_with = "super::deserialize_opt_prune_mode_with_min_blocks::<10, _>"
)]
Option<PruneMode>,
);
@@ -43,7 +58,12 @@ mod test {
assert!(serde_json::from_str::<V>(r#"{"distance": 10}"#).is_ok());
assert_matches!(
serde_json::from_str::<V>(r#"{"distance": 9}"#),
Err(err) if err.to_string() == "invalid value: integer `9`, expected prune mode distance not less than 10 blocks"
Err(err) if err.to_string() == "invalid value: integer `9`, expected prune mode that leaves at least 10 blocks in the database"
);
assert_matches!(
serde_json::from_str::<V>(r#""full""#),
Err(err) if err.to_string() == "invalid value: string \"full\", expected prune mode that leaves at least 10 blocks in the database"
);
}
}

View File

@@ -64,7 +64,9 @@ impl<DB: Database> Pruner<DB> {
pub fn run(&mut self, tip_block_number: BlockNumber) -> PrunerResult {
let provider = self.provider_factory.provider_rw()?;
if let Some((to_block, prune_mode)) = self.modes.prune_to_block_receipts(tip_block_number) {
if let Some((to_block, prune_mode)) =
self.modes.prune_target_block_receipts(tip_block_number)
{
self.prune_receipts(&provider, to_block, prune_mode)?;
}