Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

statedb: allow longer state pruning history #11980

Merged
merged 21 commits into from
Sep 1, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 44 additions & 22 deletions client/db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ use sc_client_api::{
utils::is_descendent_of,
IoInfo, MemoryInfo, MemorySize, UsageInfo,
};
use sc_state_db::StateDb;
use sc_state_db::{IsPruned, StateDb, StateDbSync};
use sp_arithmetic::traits::Saturating;
use sp_blockchain::{
well_known_cache_keys, Backend as _, CachedHeaderMetadata, Error as ClientError, HeaderBackend,
Expand Down Expand Up @@ -426,9 +426,10 @@ struct PendingBlock<Block: BlockT> {
}

// wrapper that implements trait required for state_db
struct StateMetaDb<'a>(&'a dyn Database<DbHash>);
#[derive(Clone)]
struct StateMetaDb(Arc<dyn Database<DbHash>>);

impl<'a> sc_state_db::MetaDb for StateMetaDb<'a> {
impl sc_state_db::MetaDb for StateMetaDb {
type Error = sp_database::error::DatabaseError;

fn get_meta(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
Expand Down Expand Up @@ -899,7 +900,7 @@ impl<Block: BlockT> sc_client_api::backend::BlockImportOperation<Block>

struct StorageDb<Block: BlockT> {
pub db: Arc<dyn Database<DbHash>>,
pub state_db: StateDb<Block::Hash, Vec<u8>>,
pub state_db: StateDb<Block::Hash, Vec<u8>, StateMetaDb>,
prefix_keys: bool,
}

Expand Down Expand Up @@ -1089,7 +1090,7 @@ impl<Block: BlockT> Backend<Block> {
let mut db_init_transaction = Transaction::new();

let requested_state_pruning = config.state_pruning.clone();
let state_meta_db = StateMetaDb(db.as_ref());
let state_meta_db = StateMetaDb(db.clone());
let map_e = sp_blockchain::Error::from_state_db;

let (state_db_init_commit_set, state_db) = StateDb::open(
Expand Down Expand Up @@ -1303,10 +1304,11 @@ impl<Block: BlockT> Backend<Block> {
}

trace!(target: "db", "Canonicalize block #{} ({:?})", new_canonical, hash);
let commit =
self.storage.state_db.canonicalize_block(&hash).map_err(
sp_blockchain::Error::from_state_db::<sc_state_db::Error<io::Error>>,
)?;
let commit = self.storage.state_db.canonicalize_block(&hash).map_err(
sp_blockchain::Error::from_state_db::<
sc_state_db::Error<sp_database::error::DatabaseError>,
>,
)?;
apply_state_commit(transaction, commit);
}
Ok(())
Expand Down Expand Up @@ -1459,7 +1461,9 @@ impl<Block: BlockT> Backend<Block> {
if number <= last_finalized_num {
// Canonicalize in the db when re-importing existing blocks with state.
let commit = self.storage.state_db.canonicalize_block(&hash).map_err(
sp_blockchain::Error::from_state_db::<sc_state_db::Error<io::Error>>,
sp_blockchain::Error::from_state_db::<
sc_state_db::Error<sp_database::error::DatabaseError>,
>,
)?;
apply_state_commit(&mut transaction, commit);
meta_updates.push(MetaUpdate {
Expand Down Expand Up @@ -1668,10 +1672,11 @@ impl<Block: BlockT> Backend<Block> {
.map(|c| f_num.saturated_into::<u64>() > c)
.unwrap_or(true)
{
let commit =
self.storage.state_db.canonicalize_block(&f_hash).map_err(
sp_blockchain::Error::from_state_db::<sc_state_db::Error<io::Error>>,
)?;
let commit = self.storage.state_db.canonicalize_block(&f_hash).map_err(
sp_blockchain::Error::from_state_db::<
sc_state_db::Error<sp_database::error::DatabaseError>,
>,
)?;
apply_state_commit(transaction, commit);
}

Expand Down Expand Up @@ -2245,13 +2250,17 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {

match self.blockchain.header_metadata(hash) {
Ok(ref hdr) => {
if !self.have_state_at(&hash, hdr.number) {
return Err(sp_blockchain::Error::UnknownBlock(format!(
"State already discarded for {:?}",
block
)))
}
if let Ok(()) = self.storage.state_db.pin(&hash) {
// NOTE: similar to `sp_state_machine::Storage::get` but we can't use
// `sp_state_machine::Storage::get` directly in order to avoid dead lock
NingLin-P marked this conversation as resolved.
Show resolved Hide resolved
let hint = |state_db: &StateDbSync<_, _, _>| {
state_db
.get(hdr.state_root.as_ref(), self.storage.as_ref())
.unwrap_or(None)
.is_some()
};
if let Ok(()) =
self.storage.state_db.pin(&hash, hdr.number.saturated_into::<u64>(), hint)
{
let root = hdr.state_root;
let db_state = DbState::<Block>::new(self.storage.clone(), root);
let state = RefTrackingState::new(db_state, self.storage.clone(), Some(hash));
Expand Down Expand Up @@ -2287,7 +2296,20 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
_ => false,
}
} else {
!self.storage.state_db.is_pruned(hash, number.saturated_into::<u64>())
match self.storage.state_db.is_pruned(hash, number.saturated_into::<u64>()) {
IsPruned::Pruned => false,
IsPruned::NotPruned => true,
IsPruned::MaybePruned => match self.blockchain.header_metadata(*hash) {
Ok(header) => sp_state_machine::Storage::get(
self.storage.as_ref(),
&header.state_root,
(&[], None),
)
.unwrap_or(None)
.is_some(),
_ => false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I think it would be better to return a ClientError (have_state_at seems to be only call by function how return it).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the return type here will also require changing the Backend interface, some callers of it may not expect an error

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I vote for changing it (client db is quite internal to substrate I think). But let's wait for second feed back.

},
}
}
}

Expand Down
Loading