From 6cfc4e9d8529d3291323aebed8ec70807417e60f Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 14 Oct 2022 13:21:05 +0000 Subject: [PATCH 01/24] client/db: Prune state/blocks with delay Signed-off-by: Alexandru Vasile --- client/db/src/lib.rs | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index ae777ad154f6d..35beaedfa76e0 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -328,6 +328,14 @@ pub enum BlocksPruning { KeepFinalized, /// Keep N recent finalized blocks. Some(u32), + /// Delay the pruning of blocks by a given number of finalizations. + /// + /// The blocks that were supposed to get pruned at the finalization N + /// will get pruned at N + delay. + /// + /// The feature is introduced to satisfy the block pinning required + /// by the RPC spec V2. + Delayed(u32), } /// Where to find the database.. @@ -1077,7 +1085,7 @@ impl Backend { let state_pruning = match blocks_pruning { BlocksPruning::KeepAll => PruningMode::ArchiveAll, BlocksPruning::KeepFinalized => PruningMode::ArchiveCanonical, - BlocksPruning::Some(n) => PruningMode::blocks_pruning(n), + BlocksPruning::Some(n) | BlocksPruning::Delayed(n) => PruningMode::blocks_pruning(n), }; let db_setting = DatabaseSettings { trie_cache_maximum_size: Some(16 * 1024 * 1024), @@ -1674,18 +1682,33 @@ impl Backend { &self, transaction: &mut Transaction, f_header: &Block::Header, - f_hash: Block::Hash, + mut f_hash: Block::Hash, displaced: &mut Option>>, with_state: bool, ) -> ClientResult<()> { - let f_num = *f_header.number(); - + let mut f_num = *f_header.number(); let lookup_key = utils::number_and_hash_to_lookup_key(f_num, f_hash)?; if with_state { transaction.set_from_vec(columns::META, meta_keys::FINALIZED_STATE, lookup_key.clone()); } transaction.set_from_vec(columns::META, meta_keys::FINALIZED_BLOCK, lookup_key); + // Update the "finalized" number and hash for pruning of N - delay. + // This implies handling both cases: + // - pruning in the state-db via `canonicalize_block` + // - pruning in db via displaced leaves and `prune_blocks` + if let BlocksPruning::Delayed(delayed) = self.blocks_pruning { + // No blocks to prune in this window. + if f_num < delayed.into() { + return Ok(()) + } + + f_num = f_num.saturating_sub(delayed.into()); + f_hash = self.blockchain.hash(f_num)?.ok_or_else(|| { + sp_blockchain::Error::UnknownBlock(format!("Unknown block number {}", f_num)) + })?; + } + if sc_client_api::Backend::have_state_at(self, &f_hash, f_num) && self.storage .state_db @@ -1731,6 +1754,11 @@ impl Backend { BlocksPruning::KeepFinalized => { self.prune_displaced_branches(transaction, finalized, displaced)?; }, + BlocksPruning::Delayed(_) => { + // Proper offset and valid displaced set of leaves provided by `note_finalized`. + self.prune_block(transaction, BlockId::::number(finalized))?; + self.prune_displaced_branches(transaction, finalized, displaced)?; + }, } Ok(()) } From 69cb59092d0dab0e2068ca0f28ed433468aeb51d Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 14 Oct 2022 13:22:13 +0000 Subject: [PATCH 02/24] tests: Delay prune blocks on finalization Signed-off-by: Alexandru Vasile --- client/db/src/lib.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 35beaedfa76e0..726a2653a18e1 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -3819,4 +3819,44 @@ pub(crate) mod tests { assert_eq!(backend.blockchain().leaves().unwrap(), vec![block2]); assert_eq!(backend.blockchain().info().best_hash, block2); } + + #[test] + fn delayed_prune_blocks_on_finalize() { + let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Delayed(2), 0); + let ext = Default::default(); + let hash_0 = + insert_block(&backend, 0, Default::default(), None, ext, vec![0.into()], None).unwrap(); + let hash_1 = insert_block(&backend, 1, hash_0, None, ext, vec![1.into()], None).unwrap(); + + // Block tree: + // 0 -> 1 + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, BlockId::Hash(hash_1)).unwrap(); + op.mark_finalized(BlockId::Hash(hash_0), None).unwrap(); + op.mark_finalized(BlockId::Hash(hash_1), None).unwrap(); + backend.commit_operation(op).unwrap(); + + let bc = backend.blockchain(); + // Delayed pruning must keep both blocks around. + assert_eq!(Some(vec![0.into()]), bc.body(BlockId::hash(hash_0)).unwrap()); + assert_eq!(Some(vec![1.into()]), bc.body(BlockId::hash(hash_1)).unwrap()); + + // Block tree: + // 0 -> 1 -> 2 -> 3 + let hash_2 = insert_block(&backend, 2, hash_1, None, ext, vec![2.into()], None).unwrap(); + let hash_3 = insert_block(&backend, 3, hash_2, None, ext, vec![3.into()], None).unwrap(); + + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, BlockId::Hash(hash_3)).unwrap(); + op.mark_finalized(BlockId::Hash(hash_2), None).unwrap(); + op.mark_finalized(BlockId::Hash(hash_3), None).unwrap(); + backend.commit_operation(op).unwrap(); + + // Blocks 0 and 1 are pruned. + assert!(bc.body(BlockId::hash(hash_0)).unwrap().is_none()); + assert!(bc.body(BlockId::hash(hash_1)).unwrap().is_none()); + + assert_eq!(Some(vec![2.into()]), bc.body(BlockId::hash(hash_2)).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(BlockId::hash(hash_3)).unwrap()); + } } From d18d071b03c4c4cb2f1402122c420e15cee67e3a Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 14 Oct 2022 13:23:10 +0000 Subject: [PATCH 03/24] tests: Delay prune blocks with fork Signed-off-by: Alexandru Vasile --- client/db/src/lib.rs | 112 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 726a2653a18e1..bd5714eab313a 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -3859,4 +3859,116 @@ pub(crate) mod tests { assert_eq!(Some(vec![2.into()]), bc.body(BlockId::hash(hash_2)).unwrap()); assert_eq!(Some(vec![3.into()]), bc.body(BlockId::hash(hash_3)).unwrap()); } + + #[test] + fn delayed_prune_blocks_on_finalize_with_fork() { + let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Delayed(2), 10); + let mut blocks = Vec::new(); + let mut prev_hash = Default::default(); + + // Block tree: + // 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 + for i in 0..7 { + let hash = insert_block( + &backend, + i, + prev_hash, + None, + Default::default(), + vec![i.into()], + None, + ) + .unwrap(); + blocks.push(hash); + prev_hash = hash; + } + + // Insert a fork at the third block. + // Block tree: + // 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 + // 2 -> 3 + let fork_hash_root = + insert_block(&backend, 3, blocks[2], None, H256::random(), vec![31.into()], None) + .unwrap(); + + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, BlockId::hash(blocks[4])).unwrap(); + op.mark_head(BlockId::hash(blocks[4])).unwrap(); + backend.commit_operation(op).unwrap(); + + // Mark blocks 0, 1, 2 as finalized. + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, BlockId::hash(blocks[2])).unwrap(); + op.mark_finalized(BlockId::hash(blocks[0]), None).unwrap(); + op.mark_finalized(BlockId::hash(blocks[1]), None).unwrap(); + op.mark_finalized(BlockId::hash(blocks[2]), None).unwrap(); + backend.commit_operation(op).unwrap(); + + let bc = backend.blockchain(); + // Block 0 is pruned. + assert!(bc.body(BlockId::hash(blocks[0])).unwrap().is_none()); + assert_eq!(Some(vec![1.into()]), bc.body(BlockId::hash(blocks[1])).unwrap()); + assert_eq!(Some(vec![2.into()]), bc.body(BlockId::hash(blocks[2])).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(BlockId::hash(blocks[3])).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(BlockId::hash(blocks[4])).unwrap()); + assert_eq!(Some(vec![5.into()]), bc.body(BlockId::hash(blocks[5])).unwrap()); + assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); + assert_eq!(Some(vec![31.into()]), bc.body(BlockId::hash(fork_hash_root)).unwrap()); + + // Mark block 3 as finalized. + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, BlockId::hash(blocks[3])).unwrap(); + op.mark_finalized(BlockId::hash(blocks[3]), None).unwrap(); + backend.commit_operation(op).unwrap(); + + // Block 1 is pruned. + assert!(bc.body(BlockId::hash(blocks[1])).unwrap().is_none()); + assert_eq!(Some(vec![2.into()]), bc.body(BlockId::hash(blocks[2])).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(BlockId::hash(blocks[3])).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(BlockId::hash(blocks[4])).unwrap()); + assert_eq!(Some(vec![5.into()]), bc.body(BlockId::hash(blocks[5])).unwrap()); + assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); + assert_eq!(Some(vec![31.into()]), bc.body(BlockId::hash(fork_hash_root)).unwrap()); + + // Mark block 4 as finalized. + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, BlockId::hash(blocks[4])).unwrap(); + op.mark_finalized(BlockId::hash(blocks[4]), None).unwrap(); + backend.commit_operation(op).unwrap(); + + // Block 2 is pruned along with its fork. + assert!(bc.body(BlockId::hash(blocks[2])).unwrap().is_none()); + assert_eq!(Some(vec![31.into()]), bc.body(BlockId::hash(fork_hash_root)).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(BlockId::hash(blocks[3])).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(BlockId::hash(blocks[4])).unwrap()); + assert_eq!(Some(vec![5.into()]), bc.body(BlockId::hash(blocks[5])).unwrap()); + assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); + + // Mark block 5 as finalized. + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, BlockId::hash(blocks[5])).unwrap(); + op.mark_finalized(BlockId::hash(blocks[5]), None).unwrap(); + backend.commit_operation(op).unwrap(); + + assert!(bc.body(BlockId::hash(blocks[3])).unwrap().is_none()); + assert_eq!(Some(vec![31.into()]), bc.body(BlockId::hash(fork_hash_root)).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(BlockId::hash(blocks[4])).unwrap()); + assert_eq!(Some(vec![5.into()]), bc.body(BlockId::hash(blocks[5])).unwrap()); + assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); + + // Mark block 6 as finalized. + // Because we delay prune by 2, when we finalize block 6 we are actually + // pruning at block 4. The displaced leaves for block 4 are computed + // at hight (block number - 1) = 3. This is the time when the fork + // is picked up for pruning. + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, BlockId::hash(blocks[6])).unwrap(); + op.mark_finalized(BlockId::hash(blocks[6]), None).unwrap(); + backend.commit_operation(op).unwrap(); + + assert!(bc.body(BlockId::hash(blocks[4])).unwrap().is_none()); + assert!(bc.body(BlockId::hash(fork_hash_root)).unwrap().is_none()); + assert_eq!(Some(vec![5.into()]), bc.body(BlockId::hash(blocks[5])).unwrap()); + assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); + } } From a5fb1ccfe732dd706ad61c3df08e489a81ec462c Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 14 Oct 2022 13:24:06 +0000 Subject: [PATCH 04/24] client/cli: Add delayed pruning mode and make it default Signed-off-by: Alexandru Vasile --- client/cli/src/config.rs | 14 +++++++++++++- client/cli/src/params/pruning_params.rs | 15 ++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index 77689708a231f..9f53c0378cce1 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -42,6 +42,18 @@ pub(crate) const NODE_NAME_MAX_LENGTH: usize = 64; /// Default sub directory to store network config. pub(crate) const DEFAULT_NETWORK_CONFIG_PATH: &str = "network"; +/// Delay the pruning of blocks by a given number of finalizations. +/// +/// This value should be set to a sensible amount to accommodate the RPC +/// Spec V2 requirements of block pinning. +/// +/// The user derives no benefits from controlling this, as the RPC API +/// should be uniform across the nodes. +/// +/// This ensures that users have roughly 32 * 6 seconds (block finalization) +/// ~ 3 minutes to fetch the details of blocks. +pub(crate) const DELAYED_PRUNING: u32 = 32; + /// The recommended open file descriptor limit to be configured for the process. const RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT: u64 = 10_000; @@ -255,7 +267,7 @@ pub trait CliConfiguration: Sized { fn blocks_pruning(&self) -> Result { self.pruning_params() .map(|x| x.blocks_pruning()) - .unwrap_or_else(|| Ok(BlocksPruning::KeepFinalized)) + .unwrap_or_else(|| Ok(BlocksPruning::Delayed(DELAYED_PRUNING))) } /// Get the chain ID (string). diff --git a/client/cli/src/params/pruning_params.rs b/client/cli/src/params/pruning_params.rs index b764e4722e94d..6895119201f91 100644 --- a/client/cli/src/params/pruning_params.rs +++ b/client/cli/src/params/pruning_params.rs @@ -30,12 +30,16 @@ pub struct PruningParams { /// or for all of the canonical blocks (i.e 'archive-canonical'). #[clap(alias = "pruning", long, value_name = "PRUNING_MODE")] pub state_pruning: Option, - /// Specify the blocks pruning mode, a number of blocks to keep or 'archive'. + /// Specify the blocks pruning mode. /// - /// Default is to keep all finalized blocks. - /// otherwise, all blocks can be kept (i.e 'archive'), - /// or for all canonical blocks (i.e 'archive-canonical'), - /// or for the last N blocks (i.e a number). + /// The options are as follows: + /// 'delayed' Pruning of blocks is delayed for a sensible amount of time to + /// satisfy the RPC Spec V2. + /// 'archive' Keep all blocks. + /// 'archive-canonical' Keep all finalized (canonical) blocks. + /// [number] Keep the last N finalized (canonical) blocks. + /// + /// Default is the 'delayed' option. /// /// NOTE: only finalized blocks are subject for removal! #[clap(alias = "keep-blocks", long, value_name = "COUNT")] @@ -66,6 +70,7 @@ impl PruningParams { Some(bp) => match bp.as_str() { "archive" => Ok(BlocksPruning::KeepAll), "archive-canonical" => Ok(BlocksPruning::KeepFinalized), + "delayed" => Ok(BlocksPruning::Delayed(crate::DELAYED_PRUNING)), bc => bc .parse() .map_err(|_| { From 4f2f54e529093e7d5b6492e9b7d8862d806d0901 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 14 Oct 2022 13:40:37 +0000 Subject: [PATCH 05/24] client/db: Announce proper leaves for delayed pruning Signed-off-by: Alexandru Vasile --- client/db/src/lib.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index bd5714eab313a..054eb319557fb 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -489,10 +489,11 @@ pub struct BlockchainDb { leaves: RwLock>>, header_metadata_cache: Arc>, header_cache: Mutex>>, + delayed_pruning: Option, } impl BlockchainDb { - fn new(db: Arc>) -> ClientResult { + fn new(db: Arc>, delayed_pruning: Option) -> ClientResult { let meta = read_meta::(&*db, columns::HEADER)?; let leaves = LeafSet::read_from_db(&*db, columns::META, meta_keys::LEAF_PREFIX)?; Ok(BlockchainDb { @@ -501,6 +502,7 @@ impl BlockchainDb { meta: Arc::new(RwLock::new(meta)), header_metadata_cache: Arc::new(HeaderMetadataCache::default()), header_cache: Default::default(), + delayed_pruning, }) } @@ -667,8 +669,17 @@ impl sc_client_api::blockchain::Backend for BlockchainDb, + mut block_number: NumberFor, ) -> ClientResult> { + if let Some(delayed) = self.delayed_pruning { + // No displaced leaves + if block_number < delayed.into() { + return Ok(Default::default()) + } + + block_number = block_number.saturating_sub(delayed.into()); + } + Ok(self .leaves .read() @@ -1138,7 +1149,13 @@ impl Backend { let state_pruning_used = state_db.pruning_mode(); let is_archive_pruning = state_pruning_used.is_archive(); - let blockchain = BlockchainDb::new(db.clone())?; + let delayed_pruning = if let BlocksPruning::Delayed(delayed_pruning) = config.blocks_pruning + { + Some(delayed_pruning) + } else { + None + }; + let blockchain = BlockchainDb::new(db.clone(), delayed_pruning)?; let storage_db = StorageDb { db: db.clone(), state_db, prefix_keys: !db.supports_ref_counting() }; From 490c0c9b9f57640c6840282f0839ebafbcfff61c Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 14 Oct 2022 13:50:53 +0000 Subject: [PATCH 06/24] tests: Verify `displaced_leaves_after_finalizing` with delayed pruning Signed-off-by: Alexandru Vasile --- client/db/src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 054eb319557fb..80e8ad0f60b11 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -3973,6 +3973,11 @@ pub(crate) mod tests { assert_eq!(Some(vec![5.into()]), bc.body(BlockId::hash(blocks[5])).unwrap()); assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); + // Ensure the forked leaf 3 is properly stated here. + let displaced = backend.blockchain().displaced_leaves_after_finalizing(6).unwrap(); + assert_eq!(1, displaced.len()); + assert_eq!(fork_hash_root, displaced[0]); + // Mark block 6 as finalized. // Because we delay prune by 2, when we finalize block 6 we are actually // pruning at block 4. The displaced leaves for block 4 are computed @@ -3987,5 +3992,9 @@ pub(crate) mod tests { assert!(bc.body(BlockId::hash(fork_hash_root)).unwrap().is_none()); assert_eq!(Some(vec![5.into()]), bc.body(BlockId::hash(blocks[5])).unwrap()); assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); + + // No leaves to report for theoretical node 7. + let displaced = backend.blockchain().displaced_leaves_after_finalizing(7).unwrap(); + assert!(displaced.is_empty()); } } From 17a1bb9c8b1b76b5eba05b539185a3f1fb38f94b Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 18 Oct 2022 15:00:20 +0000 Subject: [PATCH 07/24] client/db: Rename delayed_pruning to delay_canonicalization Signed-off-by: Alexandru Vasile --- client/db/src/lib.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 80e8ad0f60b11..ad2782f27199f 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -489,11 +489,14 @@ pub struct BlockchainDb { leaves: RwLock>>, header_metadata_cache: Arc>, header_cache: Mutex>>, - delayed_pruning: Option, + delay_canonicalization: Option, } impl BlockchainDb { - fn new(db: Arc>, delayed_pruning: Option) -> ClientResult { + fn new( + db: Arc>, + delay_canonicalization: Option, + ) -> ClientResult { let meta = read_meta::(&*db, columns::HEADER)?; let leaves = LeafSet::read_from_db(&*db, columns::META, meta_keys::LEAF_PREFIX)?; Ok(BlockchainDb { @@ -502,7 +505,7 @@ impl BlockchainDb { meta: Arc::new(RwLock::new(meta)), header_metadata_cache: Arc::new(HeaderMetadataCache::default()), header_cache: Default::default(), - delayed_pruning, + delay_canonicalization, }) } @@ -671,7 +674,7 @@ impl sc_client_api::blockchain::Backend for BlockchainDb, ) -> ClientResult> { - if let Some(delayed) = self.delayed_pruning { + if let Some(delayed) = self.delay_canonicalization { // No displaced leaves if block_number < delayed.into() { return Ok(Default::default()) @@ -1149,13 +1152,13 @@ impl Backend { let state_pruning_used = state_db.pruning_mode(); let is_archive_pruning = state_pruning_used.is_archive(); - let delayed_pruning = if let BlocksPruning::Delayed(delayed_pruning) = config.blocks_pruning - { - Some(delayed_pruning) - } else { - None - }; - let blockchain = BlockchainDb::new(db.clone(), delayed_pruning)?; + let delay_canonicalization = + if let BlocksPruning::Delayed(delay_canonicalization) = config.blocks_pruning { + Some(delay_canonicalization) + } else { + None + }; + let blockchain = BlockchainDb::new(db.clone(), delay_canonicalization)?; let storage_db = StorageDb { db: db.clone(), state_db, prefix_keys: !db.supports_ref_counting() }; From 835abe9d74b6731d527316715f5909cb7b08c408 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 18 Oct 2022 18:31:36 +0000 Subject: [PATCH 08/24] client/cli: Fix rustdoc Signed-off-by: Alexandru Vasile --- client/cli/src/params/pruning_params.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cli/src/params/pruning_params.rs b/client/cli/src/params/pruning_params.rs index 6895119201f91..e9942963d6b58 100644 --- a/client/cli/src/params/pruning_params.rs +++ b/client/cli/src/params/pruning_params.rs @@ -37,7 +37,7 @@ pub struct PruningParams { /// satisfy the RPC Spec V2. /// 'archive' Keep all blocks. /// 'archive-canonical' Keep all finalized (canonical) blocks. - /// [number] Keep the last N finalized (canonical) blocks. + /// number Keep the last N finalized (canonical) blocks. /// /// Default is the 'delayed' option. /// From 80a78dcbd6397ecdce3424e0029c449f025584ae Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 28 Oct 2022 22:31:12 +0000 Subject: [PATCH 09/24] client/db: Handle canonization gap Signed-off-by: Alexandru Vasile --- client/db/src/lib.rs | 82 ++++++++++++++++++++++++++++- client/state-db/src/lib.rs | 2 + client/state-db/src/noncanonical.rs | 2 +- 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index ad2782f27199f..8e710fa7895a0 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -63,7 +63,7 @@ use sc_client_api::{ utils::is_descendent_of, IoInfo, MemoryInfo, MemorySize, UsageInfo, }; -use sc_state_db::{IsPruned, StateDb}; +use sc_state_db::{IsPruned, StateDb, LAST_CANONICAL}; use sp_arithmetic::traits::Saturating; use sp_blockchain::{ well_known_cache_keys, Backend as _, CachedHeaderMetadata, Error as ClientError, HeaderBackend, @@ -1128,6 +1128,84 @@ impl Backend { self.storage.clone() } + /// Ensure that the gap between the canonicalized and + /// finalized block is filled by canonicalizing all the blocks + /// up to the finalized block. + /// + /// The canonicalized block could be behind the finalized block + /// in the case of delayed pruning. This inconsistency could + /// cause the database to miss-behave if started without this + /// option. + fn startup_canonicalization_gap( + &self, + transaction: &mut Transaction, + ) -> ClientResult<()> { + // Read the finalized block from the database. + let finalized_num = + match self.storage.db.get(columns::META, meta_keys::FINALIZED_BLOCK).and_then( + |block_key| { + self.storage + .db + .get(columns::HEADER, &block_key) + .map(|bytes| Block::Header::decode(&mut &bytes[..]).ok()) + }, + ) { + Some(Some(header)) => (*header.number()).saturated_into::(), + _ => return Ok(()), + }; + + // The last canonicalized block is stored in the state-db meta section. + let canonicalized_num = match self + .storage + .db + .get(columns::STATE_META, LAST_CANONICAL) + .and_then(|bytes| <(Block::Hash, u64)>::decode(&mut &bytes[..]).ok()) + { + Some((_hash, num)) => num, + _ => return Ok(()), + }; + trace!(target: "db", "Last canonicalized block #{} and last finalized #{}", canonicalized_num, finalized_num); + + // Canonicalized every block from the last canonicalized + // to the finalized block. + for num in canonicalized_num..=finalized_num { + self.startup_canonicalize_block(transaction, num)?; + } + + Ok(()) + } + + /// Canonicalize the given block number. + fn startup_canonicalize_block( + &self, + transaction: &mut Transaction, + number: u64, + ) -> ClientResult<()> { + let hash = sc_client_api::blockchain::HeaderBackend::hash( + &self.blockchain, + number.saturated_into(), + )? + .ok_or_else(|| { + sp_blockchain::Error::Backend(format!( + "Can't canonicalize missing block number #{} on startup", + number, + )) + })?; + + if !sc_client_api::Backend::have_state_at(self, &hash, number.saturated_into()) { + return Ok(()) + } + + trace!(target: "db", "Canonicalize block #{} ({:?}) on startup ", number, hash); + let commit = self.storage.state_db.canonicalize_block(&hash).map_err( + sp_blockchain::Error::from_state_db::< + sc_state_db::Error, + >, + )?; + apply_state_commit(transaction, commit); + Ok(()) + } + fn from_database( db: Arc>, canonicalization_delay: u64, @@ -1199,6 +1277,8 @@ impl Backend { }); } + backend.startup_canonicalization_gap(&mut db_init_transaction)?; + db.commit(db_init_transaction)?; Ok(backend) diff --git a/client/state-db/src/lib.rs b/client/state-db/src/lib.rs index f21b707a489f0..aeb9370c6e985 100644 --- a/client/state-db/src/lib.rs +++ b/client/state-db/src/lib.rs @@ -58,6 +58,8 @@ use std::{ fmt, }; +pub use noncanonical::LAST_CANONICAL; + const PRUNING_MODE: &[u8] = b"mode"; const PRUNING_MODE_ARCHIVE: &[u8] = b"archive"; const PRUNING_MODE_ARCHIVE_CANON: &[u8] = b"archive_canonical"; diff --git a/client/state-db/src/noncanonical.rs b/client/state-db/src/noncanonical.rs index 559fc7ca023fe..abad531cd1d46 100644 --- a/client/state-db/src/noncanonical.rs +++ b/client/state-db/src/noncanonical.rs @@ -28,7 +28,7 @@ use log::trace; use std::collections::{hash_map::Entry, HashMap, VecDeque}; const NON_CANONICAL_JOURNAL: &[u8] = b"noncanonical_journal"; -pub(crate) const LAST_CANONICAL: &[u8] = b"last_canonical"; +pub const LAST_CANONICAL: &[u8] = b"last_canonical"; const MAX_BLOCKS_PER_LEVEL: u64 = 32; /// See module documentation. From 52b0430debf7908ffcdc4cbe76be26c4fe7ab3f9 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 28 Oct 2022 22:31:41 +0000 Subject: [PATCH 10/24] Revert "client/cli: Fix rustdoc" This reverts commit 835abe9d74b6731d527316715f5909cb7b08c408. --- client/cli/src/params/pruning_params.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cli/src/params/pruning_params.rs b/client/cli/src/params/pruning_params.rs index e9942963d6b58..6895119201f91 100644 --- a/client/cli/src/params/pruning_params.rs +++ b/client/cli/src/params/pruning_params.rs @@ -37,7 +37,7 @@ pub struct PruningParams { /// satisfy the RPC Spec V2. /// 'archive' Keep all blocks. /// 'archive-canonical' Keep all finalized (canonical) blocks. - /// number Keep the last N finalized (canonical) blocks. + /// [number] Keep the last N finalized (canonical) blocks. /// /// Default is the 'delayed' option. /// From 2fb4030d8f092a407b90f0c9cc8a18ade3e9867c Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 28 Oct 2022 22:31:49 +0000 Subject: [PATCH 11/24] Revert "client/cli: Add delayed pruning mode and make it default" This reverts commit a5fb1ccfe732dd706ad61c3df08e489a81ec462c. --- client/cli/src/config.rs | 14 +------------- client/cli/src/params/pruning_params.rs | 15 +++++---------- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index 9f53c0378cce1..77689708a231f 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -42,18 +42,6 @@ pub(crate) const NODE_NAME_MAX_LENGTH: usize = 64; /// Default sub directory to store network config. pub(crate) const DEFAULT_NETWORK_CONFIG_PATH: &str = "network"; -/// Delay the pruning of blocks by a given number of finalizations. -/// -/// This value should be set to a sensible amount to accommodate the RPC -/// Spec V2 requirements of block pinning. -/// -/// The user derives no benefits from controlling this, as the RPC API -/// should be uniform across the nodes. -/// -/// This ensures that users have roughly 32 * 6 seconds (block finalization) -/// ~ 3 minutes to fetch the details of blocks. -pub(crate) const DELAYED_PRUNING: u32 = 32; - /// The recommended open file descriptor limit to be configured for the process. const RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT: u64 = 10_000; @@ -267,7 +255,7 @@ pub trait CliConfiguration: Sized { fn blocks_pruning(&self) -> Result { self.pruning_params() .map(|x| x.blocks_pruning()) - .unwrap_or_else(|| Ok(BlocksPruning::Delayed(DELAYED_PRUNING))) + .unwrap_or_else(|| Ok(BlocksPruning::KeepFinalized)) } /// Get the chain ID (string). diff --git a/client/cli/src/params/pruning_params.rs b/client/cli/src/params/pruning_params.rs index 6895119201f91..b764e4722e94d 100644 --- a/client/cli/src/params/pruning_params.rs +++ b/client/cli/src/params/pruning_params.rs @@ -30,16 +30,12 @@ pub struct PruningParams { /// or for all of the canonical blocks (i.e 'archive-canonical'). #[clap(alias = "pruning", long, value_name = "PRUNING_MODE")] pub state_pruning: Option, - /// Specify the blocks pruning mode. + /// Specify the blocks pruning mode, a number of blocks to keep or 'archive'. /// - /// The options are as follows: - /// 'delayed' Pruning of blocks is delayed for a sensible amount of time to - /// satisfy the RPC Spec V2. - /// 'archive' Keep all blocks. - /// 'archive-canonical' Keep all finalized (canonical) blocks. - /// [number] Keep the last N finalized (canonical) blocks. - /// - /// Default is the 'delayed' option. + /// Default is to keep all finalized blocks. + /// otherwise, all blocks can be kept (i.e 'archive'), + /// or for all canonical blocks (i.e 'archive-canonical'), + /// or for the last N blocks (i.e a number). /// /// NOTE: only finalized blocks are subject for removal! #[clap(alias = "keep-blocks", long, value_name = "COUNT")] @@ -70,7 +66,6 @@ impl PruningParams { Some(bp) => match bp.as_str() { "archive" => Ok(BlocksPruning::KeepAll), "archive-canonical" => Ok(BlocksPruning::KeepFinalized), - "delayed" => Ok(BlocksPruning::Delayed(crate::DELAYED_PRUNING)), bc => bc .parse() .map_err(|_| { From ec24de511874825dd1c0b9f5b6d1ca7e59bb9c6e Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Thu, 3 Nov 2022 09:02:22 +0000 Subject: [PATCH 12/24] client/cli: Add `delayed_canonicalization` flag Signed-off-by: Alexandru Vasile --- bin/node/cli/benches/block_production.rs | 1 + bin/node/cli/benches/transaction_pool.rs | 1 + bin/node/testing/src/bench.rs | 1 + client/cli/src/commands/chain_info_cmd.rs | 1 + client/cli/src/config.rs | 9 ++ client/cli/src/params/pruning_params.rs | 13 +++ client/db/benches/state_access.rs | 1 + client/db/src/lib.rs | 121 +++++++++++----------- client/service/src/builder.rs | 5 +- client/service/src/config.rs | 2 + client/service/test/src/lib.rs | 1 + test-utils/client/src/lib.rs | 7 +- 12 files changed, 99 insertions(+), 64 deletions(-) diff --git a/bin/node/cli/benches/block_production.rs b/bin/node/cli/benches/block_production.rs index 4fcebb123d9e3..3b02a028f2e1f 100644 --- a/bin/node/cli/benches/block_production.rs +++ b/bin/node/cli/benches/block_production.rs @@ -75,6 +75,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { trie_cache_maximum_size: Some(64 * 1024 * 1024), state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, + delayed_canonicalization: true, chain_spec: spec, wasm_method: WasmExecutionMethod::Compiled { instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, diff --git a/bin/node/cli/benches/transaction_pool.rs b/bin/node/cli/benches/transaction_pool.rs index a8839642ddc26..71e16835ebdd9 100644 --- a/bin/node/cli/benches/transaction_pool.rs +++ b/bin/node/cli/benches/transaction_pool.rs @@ -69,6 +69,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { trie_cache_maximum_size: Some(64 * 1024 * 1024), state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, + delayed_canonicalization: true, chain_spec: spec, wasm_method: WasmExecutionMethod::Interpreted, // NOTE: we enforce the use of the native runtime to make the errors more debuggable diff --git a/bin/node/testing/src/bench.rs b/bin/node/testing/src/bench.rs index 59f1fa94c9b20..9e9d6d45e8055 100644 --- a/bin/node/testing/src/bench.rs +++ b/bin/node/testing/src/bench.rs @@ -393,6 +393,7 @@ impl BenchDb { state_pruning: Some(PruningMode::ArchiveAll), source: database_type.into_settings(dir.into()), blocks_pruning: sc_client_db::BlocksPruning::KeepAll, + delayed_pruning: true, }; let task_executor = TaskExecutor::new(); diff --git a/client/cli/src/commands/chain_info_cmd.rs b/client/cli/src/commands/chain_info_cmd.rs index cbc22cc4d52d9..cdac424604199 100644 --- a/client/cli/src/commands/chain_info_cmd.rs +++ b/client/cli/src/commands/chain_info_cmd.rs @@ -77,6 +77,7 @@ impl ChainInfoCmd { state_pruning: config.state_pruning.clone(), source: config.database.clone(), blocks_pruning: config.blocks_pruning, + delayed_pruning: config.delayed_canonicalization, }; let backend = sc_service::new_db_backend::(db_config)?; let info: ChainInfo = backend.blockchain().info().into(); diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index 77689708a231f..865116645f80f 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -248,6 +248,14 @@ pub trait CliConfiguration: Sized { .unwrap_or_else(|| Ok(Default::default())) } + /// Get the delayed canonicalization mode. + /// + /// By default this is retrieved from `delayed_canonicalization` if it is available. + /// Otherwise its true. + fn delayed_canonicalization(&self) -> Result { + Ok(self.pruning_params().map(|x| x.delayed_canonicalization()).unwrap_or(true)) + } + /// Get the block pruning mode. /// /// By default this is retrieved from `block_pruning` if it is available. Otherwise its @@ -530,6 +538,7 @@ pub trait CliConfiguration: Sized { trie_cache_maximum_size: self.trie_cache_maximum_size()?, state_pruning: self.state_pruning()?, blocks_pruning: self.blocks_pruning()?, + delayed_canonicalization: self.delayed_canonicalization()?, wasm_method: self.wasm_method()?, wasm_runtime_overrides: self.wasm_runtime_overrides(), execution_strategies: self.execution_strategies(is_dev, is_validator)?, diff --git a/client/cli/src/params/pruning_params.rs b/client/cli/src/params/pruning_params.rs index b764e4722e94d..b1c48a33b6b7c 100644 --- a/client/cli/src/params/pruning_params.rs +++ b/client/cli/src/params/pruning_params.rs @@ -40,6 +40,14 @@ pub struct PruningParams { /// NOTE: only finalized blocks are subject for removal! #[clap(alias = "keep-blocks", long, value_name = "COUNT")] pub blocks_pruning: Option, + /// Specify the delayed canonicalization of blocks. + /// + /// The blocks that were supposed to get pruned at the finalization N + /// will get pruned after a number of finalizations. + /// + /// This option is enabled by default. + #[clap(alias = "delayed-pruning", long)] + pub delayed_canonicalization: Option, } impl PruningParams { @@ -76,4 +84,9 @@ impl PruningParams { None => Ok(BlocksPruning::KeepFinalized), } } + + /// Get the block delayed canonicalization value from the parameters. + pub fn delayed_canonicalization(&self) -> bool { + self.delayed_canonicalization.unwrap_or(true) + } } diff --git a/client/db/benches/state_access.rs b/client/db/benches/state_access.rs index 912a9b028f638..3453b54458b24 100644 --- a/client/db/benches/state_access.rs +++ b/client/db/benches/state_access.rs @@ -123,6 +123,7 @@ fn create_backend(config: BenchmarkConfig, temp_dir: &TempDir) -> Backend state_pruning: Some(PruningMode::ArchiveAll), source: DatabaseSource::ParityDb { path }, blocks_pruning: BlocksPruning::KeepAll, + delayed_pruning: false, }; Backend::new(settings, 100).expect("Creates backend") diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 8e710fa7895a0..cbfb3577072e3 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -317,6 +317,8 @@ pub struct DatabaseSettings { /// /// NOTE: only finalized blocks are subject for removal! pub blocks_pruning: BlocksPruning, + /// The pruning of blocks is delayed for a number of finalizations. + pub delayed_pruning: bool, } /// Block pruning settings. @@ -328,14 +330,6 @@ pub enum BlocksPruning { KeepFinalized, /// Keep N recent finalized blocks. Some(u32), - /// Delay the pruning of blocks by a given number of finalizations. - /// - /// The blocks that were supposed to get pruned at the finalization N - /// will get pruned at N + delay. - /// - /// The feature is introduced to satisfy the block pinning required - /// by the RPC spec V2. - Delayed(u32), } /// Where to find the database.. @@ -1050,6 +1044,7 @@ pub struct Backend { offchain_storage: offchain::LocalStorage, blockchain: BlockchainDb, canonicalization_delay: u64, + delayed_pruning: Option, import_lock: Arc>, is_archive: bool, blocks_pruning: BlocksPruning, @@ -1063,7 +1058,11 @@ impl Backend { /// Create a new instance of database backend. /// /// The pruning window is how old a block must be before the state is pruned. - pub fn new(db_config: DatabaseSettings, canonicalization_delay: u64) -> ClientResult { + pub fn new( + db_config: DatabaseSettings, + canonicalization_delay: u64, + delayed_pruning: Option, + ) -> ClientResult { use utils::OpenDbError; let db_source = &db_config.source; @@ -1079,13 +1078,23 @@ impl Backend { Err(as_is) => return Err(as_is.into()), }; - Self::from_database(db as Arc<_>, canonicalization_delay, &db_config, needs_init) + Self::from_database( + db as Arc<_>, + canonicalization_delay, + delayed_pruning, + &db_config, + needs_init, + ) } /// Create new memory-backed client backend for tests. #[cfg(any(test, feature = "test-helpers"))] pub fn new_test(blocks_pruning: u32, canonicalization_delay: u64) -> Self { - Self::new_test_with_tx_storage(BlocksPruning::Some(blocks_pruning), canonicalization_delay) + Self::new_test_with_tx_storage( + BlocksPruning::Some(blocks_pruning), + canonicalization_delay, + None, + ) } /// Create new memory-backed client backend for tests. @@ -1093,22 +1102,25 @@ impl Backend { pub fn new_test_with_tx_storage( blocks_pruning: BlocksPruning, canonicalization_delay: u64, + delayed_pruning: Option, ) -> Self { let db = kvdb_memorydb::create(crate::utils::NUM_COLUMNS); let db = sp_database::as_database(db); let state_pruning = match blocks_pruning { BlocksPruning::KeepAll => PruningMode::ArchiveAll, BlocksPruning::KeepFinalized => PruningMode::ArchiveCanonical, - BlocksPruning::Some(n) | BlocksPruning::Delayed(n) => PruningMode::blocks_pruning(n), + BlocksPruning::Some(n) => PruningMode::blocks_pruning(n), }; let db_setting = DatabaseSettings { trie_cache_maximum_size: Some(16 * 1024 * 1024), state_pruning: Some(state_pruning), source: DatabaseSource::Custom { db, require_create_flag: true }, blocks_pruning, + delayed_pruning: true, }; - Self::new(db_setting, canonicalization_delay).expect("failed to create test-db") + Self::new(db_setting, canonicalization_delay, delayed_pruning) + .expect("failed to create test-db") } /// Expose the Database that is used by this backend. @@ -1209,6 +1221,7 @@ impl Backend { fn from_database( db: Arc>, canonicalization_delay: u64, + delayed_pruning: Option, config: &DatabaseSettings, should_init: bool, ) -> ClientResult { @@ -1230,13 +1243,7 @@ impl Backend { let state_pruning_used = state_db.pruning_mode(); let is_archive_pruning = state_pruning_used.is_archive(); - let delay_canonicalization = - if let BlocksPruning::Delayed(delay_canonicalization) = config.blocks_pruning { - Some(delay_canonicalization) - } else { - None - }; - let blockchain = BlockchainDb::new(db.clone(), delay_canonicalization)?; + let blockchain = BlockchainDb::new(db.clone(), delayed_pruning)?; let storage_db = StorageDb { db: db.clone(), state_db, prefix_keys: !db.supports_ref_counting() }; @@ -1248,6 +1255,7 @@ impl Backend { offchain_storage, blockchain, canonicalization_delay, + delayed_pruning, import_lock: Default::default(), is_archive: is_archive_pruning, io_stats: FrozenForDuration::new(std::time::Duration::from_secs(1)), @@ -1797,7 +1805,7 @@ impl Backend { // This implies handling both cases: // - pruning in the state-db via `canonicalize_block` // - pruning in db via displaced leaves and `prune_blocks` - if let BlocksPruning::Delayed(delayed) = self.blocks_pruning { + if let Some(delayed) = self.delayed_pruning { // No blocks to prune in this window. if f_num < delayed.into() { return Ok(()) @@ -1854,11 +1862,6 @@ impl Backend { BlocksPruning::KeepFinalized => { self.prune_displaced_branches(transaction, finalized, displaced)?; }, - BlocksPruning::Delayed(_) => { - // Proper offset and valid displaced set of leaves provided by `note_finalized`. - self.prune_block(transaction, BlockId::::number(finalized))?; - self.prune_displaced_branches(transaction, finalized, displaced)?; - }, } Ok(()) } @@ -2654,8 +2657,10 @@ pub(crate) mod tests { state_pruning: Some(PruningMode::blocks_pruning(1)), source: DatabaseSource::Custom { db: backing, require_create_flag: false }, blocks_pruning: BlocksPruning::KeepFinalized, + delayed_pruning: true, }, 0, + None, ) .unwrap(); assert_eq!(backend.blockchain().info().best_number, 9); @@ -3316,7 +3321,7 @@ pub(crate) mod tests { #[test] fn prune_blocks_on_finalize() { - let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(2), 0); + let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(2), 0, None); let mut blocks = Vec::new(); let mut prev_hash = Default::default(); for i in 0..5 { @@ -3352,7 +3357,7 @@ pub(crate) mod tests { #[test] fn prune_blocks_on_finalize_in_keep_all() { - let backend = Backend::::new_test_with_tx_storage(BlocksPruning::KeepAll, 0); + let backend = Backend::::new_test_with_tx_storage(BlocksPruning::KeepAll, 0, None); let mut blocks = Vec::new(); let mut prev_hash = Default::default(); for i in 0..5 { @@ -3387,7 +3392,7 @@ pub(crate) mod tests { #[test] fn prune_blocks_on_finalize_with_fork_in_keep_all() { - let backend = Backend::::new_test_with_tx_storage(BlocksPruning::KeepAll, 10); + let backend = Backend::::new_test_with_tx_storage(BlocksPruning::KeepAll, 10, None); let mut blocks = Vec::new(); let mut prev_hash = Default::default(); for i in 0..5 { @@ -3457,7 +3462,7 @@ pub(crate) mod tests { #[test] fn prune_blocks_on_finalize_with_fork() { - let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(2), 10); + let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(2), 10, None); let mut blocks = Vec::new(); let mut prev_hash = Default::default(); for i in 0..5 { @@ -3518,7 +3523,7 @@ pub(crate) mod tests { #[test] fn indexed_data_block_body() { - let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(1), 10); + let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(1), 10, None); let x0 = ExtrinsicWrapper::from(0u64).encode(); let x1 = ExtrinsicWrapper::from(1u64).encode(); @@ -3560,7 +3565,7 @@ pub(crate) mod tests { #[test] fn index_invalid_size() { - let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(1), 10); + let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(1), 10, None); let x0 = ExtrinsicWrapper::from(0u64).encode(); let x1 = ExtrinsicWrapper::from(1u64).encode(); @@ -3595,7 +3600,7 @@ pub(crate) mod tests { #[test] fn renew_transaction_storage() { - let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(2), 10); + let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(2), 10, None); let mut blocks = Vec::new(); let mut prev_hash = Default::default(); let x1 = ExtrinsicWrapper::from(0u64).encode(); @@ -3642,7 +3647,7 @@ pub(crate) mod tests { #[test] fn remove_leaf_block_works() { - let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(2), 10); + let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(2), 10, None); let mut blocks = Vec::new(); let mut prev_hash = Default::default(); for i in 0..2 { @@ -3922,7 +3927,8 @@ pub(crate) mod tests { #[test] fn delayed_prune_blocks_on_finalize() { - let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Delayed(2), 0); + let backend = + Backend::::new_test_with_tx_storage(BlocksPruning::Some(1), 0, Some(2)); let ext = Default::default(); let hash_0 = insert_block(&backend, 0, Default::default(), None, ext, vec![0.into()], None).unwrap(); @@ -3942,27 +3948,30 @@ pub(crate) mod tests { assert_eq!(Some(vec![1.into()]), bc.body(BlockId::hash(hash_1)).unwrap()); // Block tree: - // 0 -> 1 -> 2 -> 3 + // 0 -> 1 -> 2 -> 3 -> 4 let hash_2 = insert_block(&backend, 2, hash_1, None, ext, vec![2.into()], None).unwrap(); let hash_3 = insert_block(&backend, 3, hash_2, None, ext, vec![3.into()], None).unwrap(); + let hash_4 = insert_block(&backend, 4, hash_3, None, ext, vec![4.into()], None).unwrap(); let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, BlockId::Hash(hash_3)).unwrap(); + backend.begin_state_operation(&mut op, BlockId::Hash(hash_4)).unwrap(); op.mark_finalized(BlockId::Hash(hash_2), None).unwrap(); op.mark_finalized(BlockId::Hash(hash_3), None).unwrap(); + op.mark_finalized(BlockId::Hash(hash_4), None).unwrap(); backend.commit_operation(op).unwrap(); - // Blocks 0 and 1 are pruned. + // We keep 3 blocks around: 1 from `BlocksPruning` mode and 2 from delayed pruning. assert!(bc.body(BlockId::hash(hash_0)).unwrap().is_none()); assert!(bc.body(BlockId::hash(hash_1)).unwrap().is_none()); - assert_eq!(Some(vec![2.into()]), bc.body(BlockId::hash(hash_2)).unwrap()); assert_eq!(Some(vec![3.into()]), bc.body(BlockId::hash(hash_3)).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(BlockId::hash(hash_4)).unwrap()); } #[test] fn delayed_prune_blocks_on_finalize_with_fork() { - let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Delayed(2), 10); + let backend = + Backend::::new_test_with_tx_storage(BlocksPruning::Some(1), 10, Some(2)); let mut blocks = Vec::new(); let mut prev_hash = Default::default(); @@ -3996,12 +4005,13 @@ pub(crate) mod tests { op.mark_head(BlockId::hash(blocks[4])).unwrap(); backend.commit_operation(op).unwrap(); - // Mark blocks 0, 1, 2 as finalized. + // Mark blocks 0, 1, 2, 3 as finalized. let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, BlockId::hash(blocks[2])).unwrap(); + backend.begin_state_operation(&mut op, BlockId::hash(blocks[3])).unwrap(); op.mark_finalized(BlockId::hash(blocks[0]), None).unwrap(); op.mark_finalized(BlockId::hash(blocks[1]), None).unwrap(); op.mark_finalized(BlockId::hash(blocks[2]), None).unwrap(); + op.mark_finalized(BlockId::hash(blocks[3]), None).unwrap(); backend.commit_operation(op).unwrap(); let bc = backend.blockchain(); @@ -4015,10 +4025,10 @@ pub(crate) mod tests { assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); assert_eq!(Some(vec![31.into()]), bc.body(BlockId::hash(fork_hash_root)).unwrap()); - // Mark block 3 as finalized. + // Mark block 4 as finalized. let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, BlockId::hash(blocks[3])).unwrap(); - op.mark_finalized(BlockId::hash(blocks[3]), None).unwrap(); + backend.begin_state_operation(&mut op, BlockId::hash(blocks[4])).unwrap(); + op.mark_finalized(BlockId::hash(blocks[4]), None).unwrap(); backend.commit_operation(op).unwrap(); // Block 1 is pruned. @@ -4030,28 +4040,16 @@ pub(crate) mod tests { assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); assert_eq!(Some(vec![31.into()]), bc.body(BlockId::hash(fork_hash_root)).unwrap()); - // Mark block 4 as finalized. - let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, BlockId::hash(blocks[4])).unwrap(); - op.mark_finalized(BlockId::hash(blocks[4]), None).unwrap(); - backend.commit_operation(op).unwrap(); - - // Block 2 is pruned along with its fork. - assert!(bc.body(BlockId::hash(blocks[2])).unwrap().is_none()); - assert_eq!(Some(vec![31.into()]), bc.body(BlockId::hash(fork_hash_root)).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(BlockId::hash(blocks[3])).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(BlockId::hash(blocks[4])).unwrap()); - assert_eq!(Some(vec![5.into()]), bc.body(BlockId::hash(blocks[5])).unwrap()); - assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); - // Mark block 5 as finalized. let mut op = backend.begin_operation().unwrap(); backend.begin_state_operation(&mut op, BlockId::hash(blocks[5])).unwrap(); op.mark_finalized(BlockId::hash(blocks[5]), None).unwrap(); backend.commit_operation(op).unwrap(); - assert!(bc.body(BlockId::hash(blocks[3])).unwrap().is_none()); + // Block 2 is pruned along with its fork. + assert!(bc.body(BlockId::hash(blocks[2])).unwrap().is_none()); assert_eq!(Some(vec![31.into()]), bc.body(BlockId::hash(fork_hash_root)).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(BlockId::hash(blocks[3])).unwrap()); assert_eq!(Some(vec![4.into()]), bc.body(BlockId::hash(blocks[4])).unwrap()); assert_eq!(Some(vec![5.into()]), bc.body(BlockId::hash(blocks[5])).unwrap()); assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); @@ -4071,8 +4069,9 @@ pub(crate) mod tests { op.mark_finalized(BlockId::hash(blocks[6]), None).unwrap(); backend.commit_operation(op).unwrap(); - assert!(bc.body(BlockId::hash(blocks[4])).unwrap().is_none()); + assert!(bc.body(BlockId::hash(blocks[3])).unwrap().is_none()); assert!(bc.body(BlockId::hash(fork_hash_root)).unwrap().is_none()); + assert_eq!(Some(vec![4.into()]), bc.body(BlockId::hash(blocks[4])).unwrap()); assert_eq!(Some(vec![5.into()]), bc.body(BlockId::hash(blocks[5])).unwrap()); assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 987198d4b7f48..90b345da45ef4 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -213,6 +213,7 @@ where state_pruning: config.state_pruning.clone(), source: config.database.clone(), blocks_pruning: config.blocks_pruning, + delayed_pruning: config.delayed_canonicalization, }; let backend = new_db_backend(db_config)?; @@ -276,8 +277,10 @@ where Block: BlockT, { const CANONICALIZATION_DELAY: u64 = 4096; + const DELAYED_PRUNING: u32 = 32; - Ok(Arc::new(Backend::new(settings, CANONICALIZATION_DELAY)?)) + let delayed_pruning = if settings.delayed_pruning { Some(DELAYED_PRUNING) } else { None }; + Ok(Arc::new(Backend::new(settings, CANONICALIZATION_DELAY, delayed_pruning)?)) } /// Create an instance of client backed by given backend. diff --git a/client/service/src/config.rs b/client/service/src/config.rs index bca0697bcbd08..40889ce154308 100644 --- a/client/service/src/config.rs +++ b/client/service/src/config.rs @@ -77,6 +77,8 @@ pub struct Configuration { /// /// NOTE: only finalized blocks are subject for removal! pub blocks_pruning: BlocksPruning, + /// Enable the delayed pruning of blocks. + pub delayed_canonicalization: bool, /// Chain configuration. pub chain_spec: Box, /// Wasm execution method. diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index 5d29d34a3cbf2..de52832d6187a 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -235,6 +235,7 @@ fn node_config< trie_cache_maximum_size: Some(16 * 1024 * 1024), state_pruning: Default::default(), blocks_pruning: BlocksPruning::KeepFinalized, + delayed_canonicalization: true, chain_spec: Box::new((*spec).clone()), wasm_method: sc_service::config::WasmExecutionMethod::Interpreted, wasm_runtime_overrides: Default::default(), diff --git a/test-utils/client/src/lib.rs b/test-utils/client/src/lib.rs index d3e71f0ad28d6..f836c704a06c2 100644 --- a/test-utils/client/src/lib.rs +++ b/test-utils/client/src/lib.rs @@ -102,8 +102,11 @@ impl /// Create new `TestClientBuilder` with default backend and storage chain mode pub fn with_tx_storage(blocks_pruning: u32) -> Self { - let backend = - Arc::new(Backend::new_test_with_tx_storage(BlocksPruning::Some(blocks_pruning), 0)); + let backend = Arc::new(Backend::new_test_with_tx_storage( + BlocksPruning::Some(blocks_pruning), + 0, + None, + )); Self::with_backend(backend) } } From 5bfc4fadcefc1e2462cbb83bb85c75a7597407c8 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Thu, 3 Nov 2022 09:14:33 +0000 Subject: [PATCH 13/24] client/service: Fix tests Signed-off-by: Alexandru Vasile --- client/service/test/src/client/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index c6ac1fc7d73d9..b1b2ba9d27c39 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -1210,6 +1210,7 @@ fn doesnt_import_blocks_that_revert_finality() { state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, source: DatabaseSource::RocksDb { path: tmp.path().into(), cache_size: 1024 }, + delayed_pruning: false, }, u64::MAX, ) @@ -1436,6 +1437,7 @@ fn returns_status_for_pruned_blocks() { state_pruning: Some(PruningMode::blocks_pruning(1)), blocks_pruning: BlocksPruning::KeepFinalized, source: DatabaseSource::RocksDb { path: tmp.path().into(), cache_size: 1024 }, + delayed_pruning: false, }, u64::MAX, ) From fdee4cc9e3e91fcb5a5fa020727fa5c592289f36 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Thu, 3 Nov 2022 09:48:19 +0000 Subject: [PATCH 14/24] client/db: Move `delayed` param on the database settings Signed-off-by: Alexandru Vasile --- bin/node/cli/benches/block_production.rs | 2 +- bin/node/cli/benches/transaction_pool.rs | 2 +- bin/node/testing/src/bench.rs | 2 +- client/cli/src/config.rs | 11 +++++++-- client/cli/src/params/pruning_params.rs | 10 +++++--- client/db/benches/state_access.rs | 2 +- client/db/src/lib.rs | 29 +++++++----------------- client/service/src/builder.rs | 5 +--- client/service/src/config.rs | 2 +- client/service/test/src/client/mod.rs | 4 ++-- client/service/test/src/lib.rs | 2 +- 11 files changed, 33 insertions(+), 38 deletions(-) diff --git a/bin/node/cli/benches/block_production.rs b/bin/node/cli/benches/block_production.rs index 3b02a028f2e1f..bc0e02d9c6412 100644 --- a/bin/node/cli/benches/block_production.rs +++ b/bin/node/cli/benches/block_production.rs @@ -75,7 +75,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { trie_cache_maximum_size: Some(64 * 1024 * 1024), state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, - delayed_canonicalization: true, + delayed_canonicalization: Some(32), chain_spec: spec, wasm_method: WasmExecutionMethod::Compiled { instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, diff --git a/bin/node/cli/benches/transaction_pool.rs b/bin/node/cli/benches/transaction_pool.rs index 71e16835ebdd9..c2f9699b1bb54 100644 --- a/bin/node/cli/benches/transaction_pool.rs +++ b/bin/node/cli/benches/transaction_pool.rs @@ -69,7 +69,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { trie_cache_maximum_size: Some(64 * 1024 * 1024), state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, - delayed_canonicalization: true, + delayed_canonicalization: Some(32), chain_spec: spec, wasm_method: WasmExecutionMethod::Interpreted, // NOTE: we enforce the use of the native runtime to make the errors more debuggable diff --git a/bin/node/testing/src/bench.rs b/bin/node/testing/src/bench.rs index 9e9d6d45e8055..45b782f8ce2b1 100644 --- a/bin/node/testing/src/bench.rs +++ b/bin/node/testing/src/bench.rs @@ -393,7 +393,7 @@ impl BenchDb { state_pruning: Some(PruningMode::ArchiveAll), source: database_type.into_settings(dir.into()), blocks_pruning: sc_client_db::BlocksPruning::KeepAll, - delayed_pruning: true, + delayed_pruning: None, }; let task_executor = TaskExecutor::new(); diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index 865116645f80f..0a5a98dd7c692 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -42,6 +42,10 @@ pub(crate) const NODE_NAME_MAX_LENGTH: usize = 64; /// Default sub directory to store network config. pub(crate) const DEFAULT_NETWORK_CONFIG_PATH: &str = "network"; +/// The blocks that were supposed to get pruned at the finalization N +/// will get pruned at N + 32. +pub(crate) const DELAYED_PRUNING: u32 = 32; + /// The recommended open file descriptor limit to be configured for the process. const RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT: u64 = 10_000; @@ -252,8 +256,11 @@ pub trait CliConfiguration: Sized { /// /// By default this is retrieved from `delayed_canonicalization` if it is available. /// Otherwise its true. - fn delayed_canonicalization(&self) -> Result { - Ok(self.pruning_params().map(|x| x.delayed_canonicalization()).unwrap_or(true)) + fn delayed_canonicalization(&self) -> Result> { + Ok(self + .pruning_params() + .map(|x| x.delayed_canonicalization()) + .unwrap_or(Some(DELAYED_PRUNING))) } /// Get the block pruning mode. diff --git a/client/cli/src/params/pruning_params.rs b/client/cli/src/params/pruning_params.rs index b1c48a33b6b7c..abc4beaf96efd 100644 --- a/client/cli/src/params/pruning_params.rs +++ b/client/cli/src/params/pruning_params.rs @@ -16,7 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::error; +use crate::{config::DELAYED_PRUNING, error}; use clap::Args; use sc_service::{BlocksPruning, PruningMode}; @@ -86,7 +86,11 @@ impl PruningParams { } /// Get the block delayed canonicalization value from the parameters. - pub fn delayed_canonicalization(&self) -> bool { - self.delayed_canonicalization.unwrap_or(true) + pub fn delayed_canonicalization(&self) -> Option { + if self.delayed_canonicalization.unwrap_or(true) { + Some(DELAYED_PRUNING) + } else { + None + } } } diff --git a/client/db/benches/state_access.rs b/client/db/benches/state_access.rs index 3453b54458b24..40df2437688c9 100644 --- a/client/db/benches/state_access.rs +++ b/client/db/benches/state_access.rs @@ -123,7 +123,7 @@ fn create_backend(config: BenchmarkConfig, temp_dir: &TempDir) -> Backend state_pruning: Some(PruningMode::ArchiveAll), source: DatabaseSource::ParityDb { path }, blocks_pruning: BlocksPruning::KeepAll, - delayed_pruning: false, + delayed_pruning: None, }; Backend::new(settings, 100).expect("Creates backend") diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index cbfb3577072e3..ee9bcfe6b6333 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -318,7 +318,7 @@ pub struct DatabaseSettings { /// NOTE: only finalized blocks are subject for removal! pub blocks_pruning: BlocksPruning, /// The pruning of blocks is delayed for a number of finalizations. - pub delayed_pruning: bool, + pub delayed_pruning: Option, } /// Block pruning settings. @@ -1058,11 +1058,7 @@ impl Backend { /// Create a new instance of database backend. /// /// The pruning window is how old a block must be before the state is pruned. - pub fn new( - db_config: DatabaseSettings, - canonicalization_delay: u64, - delayed_pruning: Option, - ) -> ClientResult { + pub fn new(db_config: DatabaseSettings, canonicalization_delay: u64) -> ClientResult { use utils::OpenDbError; let db_source = &db_config.source; @@ -1078,13 +1074,7 @@ impl Backend { Err(as_is) => return Err(as_is.into()), }; - Self::from_database( - db as Arc<_>, - canonicalization_delay, - delayed_pruning, - &db_config, - needs_init, - ) + Self::from_database(db as Arc<_>, canonicalization_delay, &db_config, needs_init) } /// Create new memory-backed client backend for tests. @@ -1116,11 +1106,10 @@ impl Backend { state_pruning: Some(state_pruning), source: DatabaseSource::Custom { db, require_create_flag: true }, blocks_pruning, - delayed_pruning: true, + delayed_pruning, }; - Self::new(db_setting, canonicalization_delay, delayed_pruning) - .expect("failed to create test-db") + Self::new(db_setting, canonicalization_delay).expect("failed to create test-db") } /// Expose the Database that is used by this backend. @@ -1221,7 +1210,6 @@ impl Backend { fn from_database( db: Arc>, canonicalization_delay: u64, - delayed_pruning: Option, config: &DatabaseSettings, should_init: bool, ) -> ClientResult { @@ -1243,7 +1231,7 @@ impl Backend { let state_pruning_used = state_db.pruning_mode(); let is_archive_pruning = state_pruning_used.is_archive(); - let blockchain = BlockchainDb::new(db.clone(), delayed_pruning)?; + let blockchain = BlockchainDb::new(db.clone(), config.delayed_pruning)?; let storage_db = StorageDb { db: db.clone(), state_db, prefix_keys: !db.supports_ref_counting() }; @@ -1255,7 +1243,7 @@ impl Backend { offchain_storage, blockchain, canonicalization_delay, - delayed_pruning, + delayed_pruning: config.delayed_pruning, import_lock: Default::default(), is_archive: is_archive_pruning, io_stats: FrozenForDuration::new(std::time::Duration::from_secs(1)), @@ -2657,10 +2645,9 @@ pub(crate) mod tests { state_pruning: Some(PruningMode::blocks_pruning(1)), source: DatabaseSource::Custom { db: backing, require_create_flag: false }, blocks_pruning: BlocksPruning::KeepFinalized, - delayed_pruning: true, + delayed_pruning: None, }, 0, - None, ) .unwrap(); assert_eq!(backend.blockchain().info().best_number, 9); diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 90b345da45ef4..77d331e2d472e 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -277,10 +277,7 @@ where Block: BlockT, { const CANONICALIZATION_DELAY: u64 = 4096; - const DELAYED_PRUNING: u32 = 32; - - let delayed_pruning = if settings.delayed_pruning { Some(DELAYED_PRUNING) } else { None }; - Ok(Arc::new(Backend::new(settings, CANONICALIZATION_DELAY, delayed_pruning)?)) + Ok(Arc::new(Backend::new(settings, CANONICALIZATION_DELAY)?)) } /// Create an instance of client backed by given backend. diff --git a/client/service/src/config.rs b/client/service/src/config.rs index 40889ce154308..d2a49c87aa27a 100644 --- a/client/service/src/config.rs +++ b/client/service/src/config.rs @@ -78,7 +78,7 @@ pub struct Configuration { /// NOTE: only finalized blocks are subject for removal! pub blocks_pruning: BlocksPruning, /// Enable the delayed pruning of blocks. - pub delayed_canonicalization: bool, + pub delayed_canonicalization: Option, /// Chain configuration. pub chain_spec: Box, /// Wasm execution method. diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index b1b2ba9d27c39..bdc5126919f59 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -1210,7 +1210,7 @@ fn doesnt_import_blocks_that_revert_finality() { state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, source: DatabaseSource::RocksDb { path: tmp.path().into(), cache_size: 1024 }, - delayed_pruning: false, + delayed_pruning: None, }, u64::MAX, ) @@ -1437,7 +1437,7 @@ fn returns_status_for_pruned_blocks() { state_pruning: Some(PruningMode::blocks_pruning(1)), blocks_pruning: BlocksPruning::KeepFinalized, source: DatabaseSource::RocksDb { path: tmp.path().into(), cache_size: 1024 }, - delayed_pruning: false, + delayed_pruning: None, }, u64::MAX, ) diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index de52832d6187a..cedd4b480a2fd 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -235,7 +235,7 @@ fn node_config< trie_cache_maximum_size: Some(16 * 1024 * 1024), state_pruning: Default::default(), blocks_pruning: BlocksPruning::KeepFinalized, - delayed_canonicalization: true, + delayed_canonicalization: Some(32), chain_spec: Box::new((*spec).clone()), wasm_method: sc_service::config::WasmExecutionMethod::Interpreted, wasm_runtime_overrides: Default::default(), From 026181be8264f497ee76ac79c35e22272e8fab02 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Thu, 3 Nov 2022 10:23:27 +0000 Subject: [PATCH 15/24] client/db: Add debug log for pruning and fix canon gap Signed-off-by: Alexandru Vasile --- client/db/src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index ee9bcfe6b6333..f065050ef1832 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -1167,9 +1167,14 @@ impl Backend { }; trace!(target: "db", "Last canonicalized block #{} and last finalized #{}", canonicalized_num, finalized_num); + // Nothing to canonicalize. + if canonicalized_num == finalized_num { + return Ok(()) + } + // Canonicalized every block from the last canonicalized // to the finalized block. - for num in canonicalized_num..=finalized_num { + for num in canonicalized_num+1..=finalized_num { self.startup_canonicalize_block(transaction, num)?; } @@ -1799,7 +1804,9 @@ impl Backend { return Ok(()) } + let f_actual = f_num.clone(); f_num = f_num.saturating_sub(delayed.into()); + debug!(target: "db", "Mark finalized #{} and canonicalize #{}", f_actual, f_num); f_hash = self.blockchain.hash(f_num)?.ok_or_else(|| { sp_blockchain::Error::UnknownBlock(format!("Unknown block number {}", f_num)) })?; From f34dad57b7c0784b1341e35f72faf40f5ac75889 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Thu, 3 Nov 2022 15:32:22 +0000 Subject: [PATCH 16/24] client/db: Apply cargo fmt Signed-off-by: Alexandru Vasile --- client/db/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index f065050ef1832..da3e02ca13d69 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -1174,7 +1174,7 @@ impl Backend { // Canonicalized every block from the last canonicalized // to the finalized block. - for num in canonicalized_num+1..=finalized_num { + for num in canonicalized_num + 1..=finalized_num { self.startup_canonicalize_block(transaction, num)?; } From 2c28ac16a9643ae5b18b79a56e5967e8889fd222 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Thu, 3 Nov 2022 15:38:56 +0000 Subject: [PATCH 17/24] client/cli: Improve documentation Signed-off-by: Alexandru Vasile --- client/cli/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index 0a5a98dd7c692..4959be0c3d284 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -255,7 +255,7 @@ pub trait CliConfiguration: Sized { /// Get the delayed canonicalization mode. /// /// By default this is retrieved from `delayed_canonicalization` if it is available. - /// Otherwise its true. + /// Otherwise the mode is active. fn delayed_canonicalization(&self) -> Result> { Ok(self .pruning_params() From 9f68abd46cae610d70ab22502db91d0e367cad02 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Thu, 3 Nov 2022 16:00:24 +0000 Subject: [PATCH 18/24] client/db: Simplify canonicalization on startup Signed-off-by: Alexandru Vasile --- client/db/src/lib.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index da3e02ca13d69..a468d64f8f855 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -1167,11 +1167,6 @@ impl Backend { }; trace!(target: "db", "Last canonicalized block #{} and last finalized #{}", canonicalized_num, finalized_num); - // Nothing to canonicalize. - if canonicalized_num == finalized_num { - return Ok(()) - } - // Canonicalized every block from the last canonicalized // to the finalized block. for num in canonicalized_num + 1..=finalized_num { From 5f1c535e060f0df6f86b6172033720785d41b4e2 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Thu, 3 Nov 2022 17:04:51 +0000 Subject: [PATCH 19/24] client/db: Adjust testing for origin/master Signed-off-by: Alexandru Vasile --- client/db/src/lib.rs | 106 +++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 249e6d8732c2e..5f7d7b71040ff 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -3923,15 +3923,15 @@ pub(crate) mod tests { // Block tree: // 0 -> 1 let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, BlockId::Hash(hash_1)).unwrap(); - op.mark_finalized(BlockId::Hash(hash_0), None).unwrap(); - op.mark_finalized(BlockId::Hash(hash_1), None).unwrap(); + backend.begin_state_operation(&mut op, &hash_1).unwrap(); + op.mark_finalized(&hash_0, None).unwrap(); + op.mark_finalized(&hash_1, None).unwrap(); backend.commit_operation(op).unwrap(); let bc = backend.blockchain(); // Delayed pruning must keep both blocks around. - assert_eq!(Some(vec![0.into()]), bc.body(BlockId::hash(hash_0)).unwrap()); - assert_eq!(Some(vec![1.into()]), bc.body(BlockId::hash(hash_1)).unwrap()); + assert_eq!(Some(vec![0.into()]), bc.body(&hash_0).unwrap()); + assert_eq!(Some(vec![1.into()]), bc.body(&hash_1).unwrap()); // Block tree: // 0 -> 1 -> 2 -> 3 -> 4 @@ -3940,18 +3940,18 @@ pub(crate) mod tests { let hash_4 = insert_block(&backend, 4, hash_3, None, ext, vec![4.into()], None).unwrap(); let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, BlockId::Hash(hash_4)).unwrap(); - op.mark_finalized(BlockId::Hash(hash_2), None).unwrap(); - op.mark_finalized(BlockId::Hash(hash_3), None).unwrap(); - op.mark_finalized(BlockId::Hash(hash_4), None).unwrap(); + backend.begin_state_operation(&mut op, &hash_4).unwrap(); + op.mark_finalized(&hash_2, None).unwrap(); + op.mark_finalized(&hash_3, None).unwrap(); + op.mark_finalized(&hash_4, None).unwrap(); backend.commit_operation(op).unwrap(); // We keep 3 blocks around: 1 from `BlocksPruning` mode and 2 from delayed pruning. - assert!(bc.body(BlockId::hash(hash_0)).unwrap().is_none()); - assert!(bc.body(BlockId::hash(hash_1)).unwrap().is_none()); - assert_eq!(Some(vec![2.into()]), bc.body(BlockId::hash(hash_2)).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(BlockId::hash(hash_3)).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(BlockId::hash(hash_4)).unwrap()); + assert!(bc.body(&hash_0).unwrap().is_none()); + assert!(bc.body(&hash_1).unwrap().is_none()); + assert_eq!(Some(vec![2.into()]), bc.body(&hash_2).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(&hash_3).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(&hash_4).unwrap()); } #[test] @@ -3987,58 +3987,58 @@ pub(crate) mod tests { .unwrap(); let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, BlockId::hash(blocks[4])).unwrap(); - op.mark_head(BlockId::hash(blocks[4])).unwrap(); + backend.begin_state_operation(&mut op, &blocks[4]).unwrap(); + op.mark_head(&blocks[4]).unwrap(); backend.commit_operation(op).unwrap(); // Mark blocks 0, 1, 2, 3 as finalized. let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, BlockId::hash(blocks[3])).unwrap(); - op.mark_finalized(BlockId::hash(blocks[0]), None).unwrap(); - op.mark_finalized(BlockId::hash(blocks[1]), None).unwrap(); - op.mark_finalized(BlockId::hash(blocks[2]), None).unwrap(); - op.mark_finalized(BlockId::hash(blocks[3]), None).unwrap(); + backend.begin_state_operation(&mut op, &blocks[3]).unwrap(); + op.mark_finalized(&blocks[0], None).unwrap(); + op.mark_finalized(&blocks[1], None).unwrap(); + op.mark_finalized(&blocks[2], None).unwrap(); + op.mark_finalized(&blocks[3], None).unwrap(); backend.commit_operation(op).unwrap(); let bc = backend.blockchain(); // Block 0 is pruned. - assert!(bc.body(BlockId::hash(blocks[0])).unwrap().is_none()); - assert_eq!(Some(vec![1.into()]), bc.body(BlockId::hash(blocks[1])).unwrap()); - assert_eq!(Some(vec![2.into()]), bc.body(BlockId::hash(blocks[2])).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(BlockId::hash(blocks[3])).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(BlockId::hash(blocks[4])).unwrap()); - assert_eq!(Some(vec![5.into()]), bc.body(BlockId::hash(blocks[5])).unwrap()); - assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); - assert_eq!(Some(vec![31.into()]), bc.body(BlockId::hash(fork_hash_root)).unwrap()); + assert!(bc.body(&blocks[0]).unwrap().is_none()); + assert_eq!(Some(vec![1.into()]), bc.body(&blocks[1]).unwrap()); + assert_eq!(Some(vec![2.into()]), bc.body(&blocks[2]).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(&blocks[3]).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(&blocks[4]).unwrap()); + assert_eq!(Some(vec![5.into()]), bc.body(&blocks[5]).unwrap()); + assert_eq!(Some(vec![6.into()]), bc.body(&blocks[6]).unwrap()); + assert_eq!(Some(vec![31.into()]), bc.body(&fork_hash_root).unwrap()); // Mark block 4 as finalized. let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, BlockId::hash(blocks[4])).unwrap(); - op.mark_finalized(BlockId::hash(blocks[4]), None).unwrap(); + backend.begin_state_operation(&mut op, &blocks[4]).unwrap(); + op.mark_finalized(&blocks[4], None).unwrap(); backend.commit_operation(op).unwrap(); // Block 1 is pruned. - assert!(bc.body(BlockId::hash(blocks[1])).unwrap().is_none()); - assert_eq!(Some(vec![2.into()]), bc.body(BlockId::hash(blocks[2])).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(BlockId::hash(blocks[3])).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(BlockId::hash(blocks[4])).unwrap()); - assert_eq!(Some(vec![5.into()]), bc.body(BlockId::hash(blocks[5])).unwrap()); - assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); - assert_eq!(Some(vec![31.into()]), bc.body(BlockId::hash(fork_hash_root)).unwrap()); + assert!(bc.body(&blocks[1]).unwrap().is_none()); + assert_eq!(Some(vec![2.into()]), bc.body(&blocks[2]).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(&blocks[3]).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(&blocks[4]).unwrap()); + assert_eq!(Some(vec![5.into()]), bc.body(&blocks[5]).unwrap()); + assert_eq!(Some(vec![6.into()]), bc.body(&blocks[6]).unwrap()); + assert_eq!(Some(vec![31.into()]), bc.body(&fork_hash_root).unwrap()); // Mark block 5 as finalized. let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, BlockId::hash(blocks[5])).unwrap(); - op.mark_finalized(BlockId::hash(blocks[5]), None).unwrap(); + backend.begin_state_operation(&mut op, &blocks[5]).unwrap(); + op.mark_finalized(&blocks[5], None).unwrap(); backend.commit_operation(op).unwrap(); // Block 2 is pruned along with its fork. - assert!(bc.body(BlockId::hash(blocks[2])).unwrap().is_none()); - assert_eq!(Some(vec![31.into()]), bc.body(BlockId::hash(fork_hash_root)).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(BlockId::hash(blocks[3])).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(BlockId::hash(blocks[4])).unwrap()); - assert_eq!(Some(vec![5.into()]), bc.body(BlockId::hash(blocks[5])).unwrap()); - assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); + assert!(bc.body(&blocks[2]).unwrap().is_none()); + assert_eq!(Some(vec![31.into()]), bc.body(&fork_hash_root).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(&blocks[3]).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(&blocks[4]).unwrap()); + assert_eq!(Some(vec![5.into()]), bc.body(&blocks[5]).unwrap()); + assert_eq!(Some(vec![6.into()]), bc.body(&blocks[6]).unwrap()); // Ensure the forked leaf 3 is properly stated here. let displaced = backend.blockchain().displaced_leaves_after_finalizing(6).unwrap(); @@ -4051,15 +4051,15 @@ pub(crate) mod tests { // at hight (block number - 1) = 3. This is the time when the fork // is picked up for pruning. let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, BlockId::hash(blocks[6])).unwrap(); - op.mark_finalized(BlockId::hash(blocks[6]), None).unwrap(); + backend.begin_state_operation(&mut op, &blocks[6]).unwrap(); + op.mark_finalized(&blocks[6], None).unwrap(); backend.commit_operation(op).unwrap(); - assert!(bc.body(BlockId::hash(blocks[3])).unwrap().is_none()); - assert!(bc.body(BlockId::hash(fork_hash_root)).unwrap().is_none()); - assert_eq!(Some(vec![4.into()]), bc.body(BlockId::hash(blocks[4])).unwrap()); - assert_eq!(Some(vec![5.into()]), bc.body(BlockId::hash(blocks[5])).unwrap()); - assert_eq!(Some(vec![6.into()]), bc.body(BlockId::hash(blocks[6])).unwrap()); + assert!(bc.body(&blocks[3]).unwrap().is_none()); + assert!(bc.body(&fork_hash_root).unwrap().is_none()); + assert_eq!(Some(vec![4.into()]), bc.body(&blocks[4]).unwrap()); + assert_eq!(Some(vec![5.into()]), bc.body(&blocks[5]).unwrap()); + assert_eq!(Some(vec![6.into()]), bc.body(&blocks[6]).unwrap()); // No leaves to report for theoretical node 7. let displaced = backend.blockchain().displaced_leaves_after_finalizing(7).unwrap(); From 118e9430e193ef32f4508f75abd181a99f031bf8 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Thu, 3 Nov 2022 17:31:59 +0000 Subject: [PATCH 20/24] client/db: Remove cloning for block numbers Signed-off-by: Alexandru Vasile --- client/db/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 5f7d7b71040ff..119f101a68b1b 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -1811,7 +1811,7 @@ impl Backend { return Ok(()) } - let f_actual = f_num.clone(); + let f_actual = f_num; f_num = f_num.saturating_sub(delayed.into()); debug!(target: "db", "Mark finalized #{} and canonicalize #{}", f_actual, f_num); f_hash = self.blockchain.hash(f_num)?.ok_or_else(|| { From 8fabc5254eabd497bf7055c95f76e5d7edcfca58 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 7 Nov 2022 11:55:10 +0000 Subject: [PATCH 21/24] client/db: Use `delayed_canonicalization` naming Signed-off-by: Alexandru Vasile --- bin/node/testing/src/bench.rs | 2 +- client/cli/src/commands/chain_info_cmd.rs | 2 +- client/cli/src/config.rs | 4 ++-- client/cli/src/params/pruning_params.rs | 4 ++-- client/db/benches/state_access.rs | 2 +- client/db/src/lib.rs | 16 ++++++++-------- client/service/src/builder.rs | 2 +- client/service/test/src/client/mod.rs | 4 ++-- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/bin/node/testing/src/bench.rs b/bin/node/testing/src/bench.rs index 45b782f8ce2b1..eda49d5991575 100644 --- a/bin/node/testing/src/bench.rs +++ b/bin/node/testing/src/bench.rs @@ -393,7 +393,7 @@ impl BenchDb { state_pruning: Some(PruningMode::ArchiveAll), source: database_type.into_settings(dir.into()), blocks_pruning: sc_client_db::BlocksPruning::KeepAll, - delayed_pruning: None, + delayed_canonicalization: None, }; let task_executor = TaskExecutor::new(); diff --git a/client/cli/src/commands/chain_info_cmd.rs b/client/cli/src/commands/chain_info_cmd.rs index cdac424604199..52514a0f5a626 100644 --- a/client/cli/src/commands/chain_info_cmd.rs +++ b/client/cli/src/commands/chain_info_cmd.rs @@ -77,7 +77,7 @@ impl ChainInfoCmd { state_pruning: config.state_pruning.clone(), source: config.database.clone(), blocks_pruning: config.blocks_pruning, - delayed_pruning: config.delayed_canonicalization, + delayed_canonicalization: config.delayed_canonicalization, }; let backend = sc_service::new_db_backend::(db_config)?; let info: ChainInfo = backend.blockchain().info().into(); diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index 4959be0c3d284..c1ed63bef64ba 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -44,7 +44,7 @@ pub(crate) const DEFAULT_NETWORK_CONFIG_PATH: &str = "network"; /// The blocks that were supposed to get pruned at the finalization N /// will get pruned at N + 32. -pub(crate) const DELAYED_PRUNING: u32 = 32; +pub(crate) const DELAYED_CANONICALIZATION: u32 = 32; /// The recommended open file descriptor limit to be configured for the process. const RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT: u64 = 10_000; @@ -260,7 +260,7 @@ pub trait CliConfiguration: Sized { Ok(self .pruning_params() .map(|x| x.delayed_canonicalization()) - .unwrap_or(Some(DELAYED_PRUNING))) + .unwrap_or(Some(DELAYED_CANONICALIZATION))) } /// Get the block pruning mode. diff --git a/client/cli/src/params/pruning_params.rs b/client/cli/src/params/pruning_params.rs index 71ae76e6591bb..968c2287fa383 100644 --- a/client/cli/src/params/pruning_params.rs +++ b/client/cli/src/params/pruning_params.rs @@ -16,7 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::{config::DELAYED_PRUNING, error}; +use crate::{config::DELAYED_CANONICALIZATION, error}; use clap::Args; use sc_service::{BlocksPruning, PruningMode}; @@ -88,7 +88,7 @@ impl PruningParams { /// Get the block delayed canonicalization value from the parameters. pub fn delayed_canonicalization(&self) -> Option { if self.delayed_canonicalization.unwrap_or(true) { - Some(DELAYED_PRUNING) + Some(DELAYED_CANONICALIZATION) } else { None } diff --git a/client/db/benches/state_access.rs b/client/db/benches/state_access.rs index 0bc768d0a2f30..adcbe2eb9f1d6 100644 --- a/client/db/benches/state_access.rs +++ b/client/db/benches/state_access.rs @@ -122,7 +122,7 @@ fn create_backend(config: BenchmarkConfig, temp_dir: &TempDir) -> Backend state_pruning: Some(PruningMode::ArchiveAll), source: DatabaseSource::ParityDb { path }, blocks_pruning: BlocksPruning::KeepAll, - delayed_pruning: None, + delayed_canonicalization: None, }; Backend::new(settings, 100).expect("Creates backend") diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 119f101a68b1b..f5661eb3731d4 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -318,7 +318,7 @@ pub struct DatabaseSettings { /// NOTE: only finalized blocks are subject for removal! pub blocks_pruning: BlocksPruning, /// The pruning of blocks is delayed for a number of finalizations. - pub delayed_pruning: Option, + pub delayed_canonicalization: Option, } /// Block pruning settings. @@ -1056,7 +1056,7 @@ pub struct Backend { offchain_storage: offchain::LocalStorage, blockchain: BlockchainDb, canonicalization_delay: u64, - delayed_pruning: Option, + delayed_canonicalization: Option, import_lock: Arc>, is_archive: bool, blocks_pruning: BlocksPruning, @@ -1104,7 +1104,7 @@ impl Backend { pub fn new_test_with_tx_storage( blocks_pruning: BlocksPruning, canonicalization_delay: u64, - delayed_pruning: Option, + delayed_canonicalization: Option, ) -> Self { let db = kvdb_memorydb::create(crate::utils::NUM_COLUMNS); let db = sp_database::as_database(db); @@ -1118,7 +1118,7 @@ impl Backend { state_pruning: Some(state_pruning), source: DatabaseSource::Custom { db, require_create_flag: true }, blocks_pruning, - delayed_pruning, + delayed_canonicalization, }; Self::new(db_setting, canonicalization_delay).expect("failed to create test-db") @@ -1243,7 +1243,7 @@ impl Backend { let state_pruning_used = state_db.pruning_mode(); let is_archive_pruning = state_pruning_used.is_archive(); - let blockchain = BlockchainDb::new(db.clone(), config.delayed_pruning)?; + let blockchain = BlockchainDb::new(db.clone(), config.delayed_canonicalization)?; let storage_db = StorageDb { db: db.clone(), state_db, prefix_keys: !db.supports_ref_counting() }; @@ -1255,7 +1255,7 @@ impl Backend { offchain_storage, blockchain, canonicalization_delay, - delayed_pruning: config.delayed_pruning, + delayed_canonicalization: config.delayed_canonicalization, import_lock: Default::default(), is_archive: is_archive_pruning, io_stats: FrozenForDuration::new(std::time::Duration::from_secs(1)), @@ -1805,7 +1805,7 @@ impl Backend { // This implies handling both cases: // - pruning in the state-db via `canonicalize_block` // - pruning in db via displaced leaves and `prune_blocks` - if let Some(delayed) = self.delayed_pruning { + if let Some(delayed) = self.delayed_canonicalization { // No blocks to prune in this window. if f_num < delayed.into() { return Ok(()) @@ -2648,7 +2648,7 @@ pub(crate) mod tests { state_pruning: Some(PruningMode::blocks_pruning(1)), source: DatabaseSource::Custom { db: backing, require_create_flag: false }, blocks_pruning: BlocksPruning::KeepFinalized, - delayed_pruning: None, + delayed_canonicalization: None, }, 0, ) diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index fab147b9193ff..8835e511e94f9 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -214,7 +214,7 @@ where state_pruning: config.state_pruning.clone(), source: config.database.clone(), blocks_pruning: config.blocks_pruning, - delayed_pruning: config.delayed_canonicalization, + delayed_canonicalization: config.delayed_canonicalization, }; let backend = new_db_backend(db_config)?; diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index f130bbe0429b1..2a0611588505f 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -1221,7 +1221,7 @@ fn doesnt_import_blocks_that_revert_finality() { state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, source: DatabaseSource::RocksDb { path: tmp.path().into(), cache_size: 1024 }, - delayed_pruning: None, + delayed_canonicalization: None, }, u64::MAX, ) @@ -1448,7 +1448,7 @@ fn returns_status_for_pruned_blocks() { state_pruning: Some(PruningMode::blocks_pruning(1)), blocks_pruning: BlocksPruning::KeepFinalized, source: DatabaseSource::RocksDb { path: tmp.path().into(), cache_size: 1024 }, - delayed_pruning: None, + delayed_canonicalization: None, }, u64::MAX, ) From 15cb39d91c69969f3522b875cd56a8d1becb4315 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 7 Nov 2022 12:23:02 +0000 Subject: [PATCH 22/24] client/db: Obtain last canonicalized and finalized from meta Signed-off-by: Alexandru Vasile --- client/db/src/lib.rs | 43 ++++++++++------------------- client/state-db/src/noncanonical.rs | 2 +- 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index f5661eb3731d4..a66f146431084 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -63,7 +63,7 @@ use sc_client_api::{ utils::is_descendent_of, IoInfo, MemoryInfo, MemorySize, UsageInfo, }; -use sc_state_db::{IsPruned, StateDb, LAST_CANONICAL}; +use sc_state_db::{IsPruned, StateDb}; use sp_arithmetic::traits::Saturating; use sp_blockchain::{ well_known_cache_keys, Backend as _, CachedHeaderMetadata, Error as ClientError, HeaderBackend, @@ -1149,47 +1149,25 @@ impl Backend { /// in the case of delayed pruning. This inconsistency could /// cause the database to miss-behave if started without this /// option. - fn startup_canonicalization_gap( + fn remove_canonicalization_gap( &self, transaction: &mut Transaction, + canonicalized_num: u64, + finalized_num: u64, ) -> ClientResult<()> { - // Read the finalized block from the database. - let finalized_num = - match self.storage.db.get(columns::META, meta_keys::FINALIZED_BLOCK).and_then( - |block_key| { - self.storage - .db - .get(columns::HEADER, &block_key) - .map(|bytes| Block::Header::decode(&mut &bytes[..]).ok()) - }, - ) { - Some(Some(header)) => (*header.number()).saturated_into::(), - _ => return Ok(()), - }; - - // The last canonicalized block is stored in the state-db meta section. - let canonicalized_num = match self - .storage - .db - .get(columns::STATE_META, LAST_CANONICAL) - .and_then(|bytes| <(Block::Hash, u64)>::decode(&mut &bytes[..]).ok()) - { - Some((_hash, num)) => num, - _ => return Ok(()), - }; trace!(target: "db", "Last canonicalized block #{} and last finalized #{}", canonicalized_num, finalized_num); // Canonicalized every block from the last canonicalized // to the finalized block. for num in canonicalized_num + 1..=finalized_num { - self.startup_canonicalize_block(transaction, num)?; + self.canonicalize_block(transaction, num)?; } Ok(()) } /// Canonicalize the given block number. - fn startup_canonicalize_block( + fn canonicalize_block( &self, transaction: &mut Transaction, number: u64, @@ -1242,6 +1220,7 @@ impl Backend { apply_state_commit(&mut db_init_transaction, state_db_init_commit_set); let state_pruning_used = state_db.pruning_mode(); + let canonicalized_num = state_db.best_canonical(); let is_archive_pruning = state_pruning_used.is_archive(); let blockchain = BlockchainDb::new(db.clone(), config.delayed_canonicalization)?; @@ -1269,6 +1248,8 @@ impl Backend { // Older DB versions have no last state key. Check if the state is available and set it. let info = backend.blockchain.info(); + let finalized_num: NumberFor = info.finalized_number; + let finalized_num = finalized_num.saturated_into::(); if info.finalized_state.is_none() && info.finalized_hash != Default::default() && sc_client_api::Backend::have_state_at( @@ -1285,7 +1266,11 @@ impl Backend { }); } - backend.startup_canonicalization_gap(&mut db_init_transaction)?; + backend.remove_canonicalization_gap( + &mut db_init_transaction, + canonicalized_num.unwrap_or(finalized_num), + finalized_num, + )?; db.commit(db_init_transaction)?; diff --git a/client/state-db/src/noncanonical.rs b/client/state-db/src/noncanonical.rs index abad531cd1d46..559fc7ca023fe 100644 --- a/client/state-db/src/noncanonical.rs +++ b/client/state-db/src/noncanonical.rs @@ -28,7 +28,7 @@ use log::trace; use std::collections::{hash_map::Entry, HashMap, VecDeque}; const NON_CANONICAL_JOURNAL: &[u8] = b"noncanonical_journal"; -pub const LAST_CANONICAL: &[u8] = b"last_canonical"; +pub(crate) const LAST_CANONICAL: &[u8] = b"last_canonical"; const MAX_BLOCKS_PER_LEVEL: u64 = 32; /// See module documentation. From 2014094e43db10949843d023c81683039fc9260b Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 7 Nov 2022 13:11:53 +0000 Subject: [PATCH 23/24] client/state-db: Remove private `LAST_CANONICAL` constant Signed-off-by: Alexandru Vasile --- client/state-db/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/state-db/src/lib.rs b/client/state-db/src/lib.rs index aeb9370c6e985..f21b707a489f0 100644 --- a/client/state-db/src/lib.rs +++ b/client/state-db/src/lib.rs @@ -58,8 +58,6 @@ use std::{ fmt, }; -pub use noncanonical::LAST_CANONICAL; - const PRUNING_MODE: &[u8] = b"mode"; const PRUNING_MODE_ARCHIVE: &[u8] = b"archive"; const PRUNING_MODE_ARCHIVE_CANON: &[u8] = b"archive_canonical"; From 5603f529d458eee243494fd1fea26c01af1fc9ad Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 8 Nov 2022 09:35:49 +0000 Subject: [PATCH 24/24] client/db: Adjust testing to origin/master Signed-off-by: Alexandru Vasile --- client/db/src/lib.rs | 106 +++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 3c6d7f162b82e..c34cc48f32152 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -3913,15 +3913,15 @@ pub(crate) mod tests { // Block tree: // 0 -> 1 let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, &hash_1).unwrap(); - op.mark_finalized(&hash_0, None).unwrap(); - op.mark_finalized(&hash_1, None).unwrap(); + backend.begin_state_operation(&mut op, hash_1).unwrap(); + op.mark_finalized(hash_0, None).unwrap(); + op.mark_finalized(hash_1, None).unwrap(); backend.commit_operation(op).unwrap(); let bc = backend.blockchain(); // Delayed pruning must keep both blocks around. - assert_eq!(Some(vec![0.into()]), bc.body(&hash_0).unwrap()); - assert_eq!(Some(vec![1.into()]), bc.body(&hash_1).unwrap()); + assert_eq!(Some(vec![0.into()]), bc.body(hash_0).unwrap()); + assert_eq!(Some(vec![1.into()]), bc.body(hash_1).unwrap()); // Block tree: // 0 -> 1 -> 2 -> 3 -> 4 @@ -3930,18 +3930,18 @@ pub(crate) mod tests { let hash_4 = insert_block(&backend, 4, hash_3, None, ext, vec![4.into()], None).unwrap(); let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, &hash_4).unwrap(); - op.mark_finalized(&hash_2, None).unwrap(); - op.mark_finalized(&hash_3, None).unwrap(); - op.mark_finalized(&hash_4, None).unwrap(); + backend.begin_state_operation(&mut op, hash_4).unwrap(); + op.mark_finalized(hash_2, None).unwrap(); + op.mark_finalized(hash_3, None).unwrap(); + op.mark_finalized(hash_4, None).unwrap(); backend.commit_operation(op).unwrap(); // We keep 3 blocks around: 1 from `BlocksPruning` mode and 2 from delayed pruning. - assert!(bc.body(&hash_0).unwrap().is_none()); - assert!(bc.body(&hash_1).unwrap().is_none()); - assert_eq!(Some(vec![2.into()]), bc.body(&hash_2).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(&hash_3).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(&hash_4).unwrap()); + assert!(bc.body(hash_0).unwrap().is_none()); + assert!(bc.body(hash_1).unwrap().is_none()); + assert_eq!(Some(vec![2.into()]), bc.body(hash_2).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(hash_3).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(hash_4).unwrap()); } #[test] @@ -3977,58 +3977,58 @@ pub(crate) mod tests { .unwrap(); let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, &blocks[4]).unwrap(); - op.mark_head(&blocks[4]).unwrap(); + backend.begin_state_operation(&mut op, blocks[4]).unwrap(); + op.mark_head(blocks[4]).unwrap(); backend.commit_operation(op).unwrap(); // Mark blocks 0, 1, 2, 3 as finalized. let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, &blocks[3]).unwrap(); - op.mark_finalized(&blocks[0], None).unwrap(); - op.mark_finalized(&blocks[1], None).unwrap(); - op.mark_finalized(&blocks[2], None).unwrap(); - op.mark_finalized(&blocks[3], None).unwrap(); + backend.begin_state_operation(&mut op, blocks[3]).unwrap(); + op.mark_finalized(blocks[0], None).unwrap(); + op.mark_finalized(blocks[1], None).unwrap(); + op.mark_finalized(blocks[2], None).unwrap(); + op.mark_finalized(blocks[3], None).unwrap(); backend.commit_operation(op).unwrap(); let bc = backend.blockchain(); // Block 0 is pruned. - assert!(bc.body(&blocks[0]).unwrap().is_none()); - assert_eq!(Some(vec![1.into()]), bc.body(&blocks[1]).unwrap()); - assert_eq!(Some(vec![2.into()]), bc.body(&blocks[2]).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(&blocks[3]).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(&blocks[4]).unwrap()); - assert_eq!(Some(vec![5.into()]), bc.body(&blocks[5]).unwrap()); - assert_eq!(Some(vec![6.into()]), bc.body(&blocks[6]).unwrap()); - assert_eq!(Some(vec![31.into()]), bc.body(&fork_hash_root).unwrap()); + assert!(bc.body(blocks[0]).unwrap().is_none()); + assert_eq!(Some(vec![1.into()]), bc.body(blocks[1]).unwrap()); + assert_eq!(Some(vec![2.into()]), bc.body(blocks[2]).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!(Some(vec![5.into()]), bc.body(blocks[5]).unwrap()); + assert_eq!(Some(vec![6.into()]), bc.body(blocks[6]).unwrap()); + assert_eq!(Some(vec![31.into()]), bc.body(fork_hash_root).unwrap()); // Mark block 4 as finalized. let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, &blocks[4]).unwrap(); - op.mark_finalized(&blocks[4], None).unwrap(); + backend.begin_state_operation(&mut op, blocks[4]).unwrap(); + op.mark_finalized(blocks[4], None).unwrap(); backend.commit_operation(op).unwrap(); // Block 1 is pruned. - assert!(bc.body(&blocks[1]).unwrap().is_none()); - assert_eq!(Some(vec![2.into()]), bc.body(&blocks[2]).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(&blocks[3]).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(&blocks[4]).unwrap()); - assert_eq!(Some(vec![5.into()]), bc.body(&blocks[5]).unwrap()); - assert_eq!(Some(vec![6.into()]), bc.body(&blocks[6]).unwrap()); - assert_eq!(Some(vec![31.into()]), bc.body(&fork_hash_root).unwrap()); + assert!(bc.body(blocks[1]).unwrap().is_none()); + assert_eq!(Some(vec![2.into()]), bc.body(blocks[2]).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!(Some(vec![5.into()]), bc.body(blocks[5]).unwrap()); + assert_eq!(Some(vec![6.into()]), bc.body(blocks[6]).unwrap()); + assert_eq!(Some(vec![31.into()]), bc.body(fork_hash_root).unwrap()); // Mark block 5 as finalized. let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, &blocks[5]).unwrap(); - op.mark_finalized(&blocks[5], None).unwrap(); + backend.begin_state_operation(&mut op, blocks[5]).unwrap(); + op.mark_finalized(blocks[5], None).unwrap(); backend.commit_operation(op).unwrap(); // Block 2 is pruned along with its fork. - assert!(bc.body(&blocks[2]).unwrap().is_none()); - assert_eq!(Some(vec![31.into()]), bc.body(&fork_hash_root).unwrap()); - assert_eq!(Some(vec![3.into()]), bc.body(&blocks[3]).unwrap()); - assert_eq!(Some(vec![4.into()]), bc.body(&blocks[4]).unwrap()); - assert_eq!(Some(vec![5.into()]), bc.body(&blocks[5]).unwrap()); - assert_eq!(Some(vec![6.into()]), bc.body(&blocks[6]).unwrap()); + assert!(bc.body(blocks[2]).unwrap().is_none()); + assert_eq!(Some(vec![31.into()]), bc.body(fork_hash_root).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!(Some(vec![5.into()]), bc.body(blocks[5]).unwrap()); + assert_eq!(Some(vec![6.into()]), bc.body(blocks[6]).unwrap()); // Ensure the forked leaf 3 is properly stated here. let displaced = backend.blockchain().displaced_leaves_after_finalizing(6).unwrap(); @@ -4041,15 +4041,15 @@ pub(crate) mod tests { // at hight (block number - 1) = 3. This is the time when the fork // is picked up for pruning. let mut op = backend.begin_operation().unwrap(); - backend.begin_state_operation(&mut op, &blocks[6]).unwrap(); - op.mark_finalized(&blocks[6], None).unwrap(); + backend.begin_state_operation(&mut op, blocks[6]).unwrap(); + op.mark_finalized(blocks[6], None).unwrap(); backend.commit_operation(op).unwrap(); - assert!(bc.body(&blocks[3]).unwrap().is_none()); - assert!(bc.body(&fork_hash_root).unwrap().is_none()); - assert_eq!(Some(vec![4.into()]), bc.body(&blocks[4]).unwrap()); - assert_eq!(Some(vec![5.into()]), bc.body(&blocks[5]).unwrap()); - assert_eq!(Some(vec![6.into()]), bc.body(&blocks[6]).unwrap()); + assert!(bc.body(blocks[3]).unwrap().is_none()); + assert!(bc.body(fork_hash_root).unwrap().is_none()); + assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!(Some(vec![5.into()]), bc.body(blocks[5]).unwrap()); + assert_eq!(Some(vec![6.into()]), bc.body(blocks[6]).unwrap()); // No leaves to report for theoretical node 7. let displaced = backend.blockchain().displaced_leaves_after_finalizing(7).unwrap();