mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 08:51:09 +00:00
Storage chains part 1 (#7868)
* CLI options and DB upgrade * Transaction storage * Block pruning * Block pruning test * Style * Naming * Apply suggestions from code review Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Style Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
@@ -410,8 +410,10 @@ impl BenchDb {
|
||||
let db_config = sc_client_db::DatabaseSettings {
|
||||
state_cache_size: 16*1024*1024,
|
||||
state_cache_child_ratio: Some((0, 100)),
|
||||
pruning: PruningMode::ArchiveAll,
|
||||
state_pruning: PruningMode::ArchiveAll,
|
||||
source: database_type.into_settings(dir.into()),
|
||||
keep_blocks: sc_client_db::KeepBlocks::All,
|
||||
transaction_storage: sc_client_db::TransactionStorageMode::BlockBody,
|
||||
};
|
||||
let task_executor = TaskExecutor::new();
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ use sc_service::config::{
|
||||
NodeKeyConfig, OffchainWorkerConfig, PrometheusConfig, PruningMode, Role, RpcMethods,
|
||||
TaskExecutor, TelemetryEndpoints, TransactionPoolOptions, WasmExecutionMethod,
|
||||
};
|
||||
use sc_service::{ChainSpec, TracingReceiver};
|
||||
use sc_service::{ChainSpec, TracingReceiver, KeepBlocks, TransactionStorageMode };
|
||||
use std::net::SocketAddr;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -203,6 +203,13 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
|
||||
.unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Get the database transaction storage scheme.
|
||||
fn database_transaction_storage(&self) -> Result<TransactionStorageMode> {
|
||||
Ok(self.database_params()
|
||||
.map(|x| x.transaction_storage())
|
||||
.unwrap_or(TransactionStorageMode::BlockBody))
|
||||
}
|
||||
|
||||
/// Get the database backend variant.
|
||||
///
|
||||
/// By default this is retrieved from `DatabaseParams` if it is available. Otherwise its `None`.
|
||||
@@ -244,16 +251,26 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
||||
/// Get the pruning mode.
|
||||
/// Get the state pruning mode.
|
||||
///
|
||||
/// By default this is retrieved from `PruningMode` if it is available. Otherwise its
|
||||
/// `PruningMode::default()`.
|
||||
fn pruning(&self, unsafe_pruning: bool, role: &Role) -> Result<PruningMode> {
|
||||
fn state_pruning(&self, unsafe_pruning: bool, role: &Role) -> Result<PruningMode> {
|
||||
self.pruning_params()
|
||||
.map(|x| x.pruning(unsafe_pruning, role))
|
||||
.map(|x| x.state_pruning(unsafe_pruning, role))
|
||||
.unwrap_or_else(|| Ok(Default::default()))
|
||||
}
|
||||
|
||||
/// Get the block pruning mode.
|
||||
///
|
||||
/// By default this is retrieved from `block_pruning` if it is available. Otherwise its
|
||||
/// `KeepBlocks::All`.
|
||||
fn keep_blocks(&self) -> Result<KeepBlocks> {
|
||||
self.pruning_params()
|
||||
.map(|x| x.keep_blocks())
|
||||
.unwrap_or_else(|| Ok(KeepBlocks::All))
|
||||
}
|
||||
|
||||
/// Get the chain ID (string).
|
||||
///
|
||||
/// By default this is retrieved from `SharedParams`.
|
||||
@@ -493,7 +510,9 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
|
||||
database: self.database_config(&config_dir, database_cache_size, database)?,
|
||||
state_cache_size: self.state_cache_size()?,
|
||||
state_cache_child_ratio: self.state_cache_child_ratio()?,
|
||||
pruning: self.pruning(unsafe_pruning, &role)?,
|
||||
state_pruning: self.state_pruning(unsafe_pruning, &role)?,
|
||||
keep_blocks: self.keep_blocks()?,
|
||||
transaction_storage: self.database_transaction_storage()?,
|
||||
wasm_method: self.wasm_method()?,
|
||||
wasm_runtime_overrides: self.wasm_runtime_overrides(),
|
||||
execution_strategies: self.execution_strategies(is_dev, is_validator)?,
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
use crate::arg_enums::Database;
|
||||
use structopt::StructOpt;
|
||||
use sc_service::TransactionStorageMode;
|
||||
|
||||
/// Parameters for block import.
|
||||
#[derive(Debug, StructOpt)]
|
||||
@@ -34,6 +35,15 @@ pub struct DatabaseParams {
|
||||
/// Limit the memory the database cache can use.
|
||||
#[structopt(long = "db-cache", value_name = "MiB")]
|
||||
pub database_cache_size: Option<usize>,
|
||||
|
||||
/// Enable storage chain mode
|
||||
///
|
||||
/// This changes the storage format for blocks bodys.
|
||||
/// If this is enabled, each transaction is stored separately in the
|
||||
/// transaction database column and is only referenced by hash
|
||||
/// in the block body column.
|
||||
#[structopt(long)]
|
||||
pub storage_chain: bool,
|
||||
}
|
||||
|
||||
impl DatabaseParams {
|
||||
@@ -46,4 +56,13 @@ impl DatabaseParams {
|
||||
pub fn database_cache_size(&self) -> Option<usize> {
|
||||
self.database_cache_size
|
||||
}
|
||||
|
||||
/// Transaction storage scheme.
|
||||
pub fn transaction_storage(&self) -> TransactionStorageMode {
|
||||
if self.storage_chain {
|
||||
TransactionStorageMode::StorageChain
|
||||
} else {
|
||||
TransactionStorageMode::BlockBody
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::error;
|
||||
use sc_service::{PruningMode, Role};
|
||||
use sc_service::{PruningMode, Role, KeepBlocks};
|
||||
use structopt::StructOpt;
|
||||
|
||||
/// Parameters to define the pruning mode
|
||||
@@ -30,11 +30,16 @@ pub struct PruningParams {
|
||||
/// 256 blocks.
|
||||
#[structopt(long = "pruning", value_name = "PRUNING_MODE")]
|
||||
pub pruning: Option<String>,
|
||||
/// Specify the number of finalized blocks to keep in the database.
|
||||
///
|
||||
/// Default is to keep all blocks.
|
||||
#[structopt(long, value_name = "COUNT")]
|
||||
pub keep_blocks: Option<u32>,
|
||||
}
|
||||
|
||||
impl PruningParams {
|
||||
/// Get the pruning value from the parameters
|
||||
pub fn pruning(&self, unsafe_pruning: bool, role: &Role) -> error::Result<PruningMode> {
|
||||
pub fn state_pruning(&self, unsafe_pruning: bool, role: &Role) -> error::Result<PruningMode> {
|
||||
// by default we disable pruning if the node is an authority (i.e.
|
||||
// `ArchiveAll`), otherwise we keep state for the last 256 blocks. if the
|
||||
// node is an authority and pruning is enabled explicitly, then we error
|
||||
@@ -58,4 +63,12 @@ impl PruningParams {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the block pruning value from the parameters
|
||||
pub fn keep_blocks(&self) -> error::Result<KeepBlocks> {
|
||||
Ok(match self.keep_blocks {
|
||||
Some(n) => KeepBlocks::Some(n),
|
||||
None => KeepBlocks::All,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+200
-16
@@ -66,7 +66,7 @@ use codec::{Decode, Encode};
|
||||
use hash_db::Prefix;
|
||||
use sp_trie::{MemoryDB, PrefixedMemoryDB, prefixed_key};
|
||||
use sp_database::Transaction;
|
||||
use sp_core::ChangesTrieConfiguration;
|
||||
use sp_core::{Hasher, ChangesTrieConfiguration};
|
||||
use sp_core::offchain::storage::{OffchainOverlayedChange, OffchainOverlayedChanges};
|
||||
use sp_core::storage::{well_known_keys, ChildInfo};
|
||||
use sp_arithmetic::traits::Saturating;
|
||||
@@ -264,10 +264,33 @@ pub struct DatabaseSettings {
|
||||
pub state_cache_size: usize,
|
||||
/// Ratio of cache size dedicated to child tries.
|
||||
pub state_cache_child_ratio: Option<(usize, usize)>,
|
||||
/// Pruning mode.
|
||||
pub pruning: PruningMode,
|
||||
/// State pruning mode.
|
||||
pub state_pruning: PruningMode,
|
||||
/// Where to find the database.
|
||||
pub source: DatabaseSettingsSrc,
|
||||
/// Block pruning mode.
|
||||
pub keep_blocks: KeepBlocks,
|
||||
/// Block body/Transaction storage scheme.
|
||||
pub transaction_storage: TransactionStorageMode,
|
||||
}
|
||||
|
||||
/// Block pruning settings.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum KeepBlocks {
|
||||
/// Keep full block history.
|
||||
All,
|
||||
/// Keep N recent finalized blocks.
|
||||
Some(u32),
|
||||
}
|
||||
|
||||
/// Block body storage scheme.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum TransactionStorageMode {
|
||||
/// Store block body as an encoded list of full transactions in the BODY column
|
||||
BlockBody,
|
||||
/// Store a list of hashes in the BODY column and each transaction individually
|
||||
/// in the TRANSACTION column.
|
||||
StorageChain,
|
||||
}
|
||||
|
||||
/// Where to find the database..
|
||||
@@ -334,6 +357,8 @@ pub(crate) mod columns {
|
||||
/// Offchain workers local storage
|
||||
pub const OFFCHAIN: u32 = 9;
|
||||
pub const CACHE: u32 = 10;
|
||||
/// Transactions
|
||||
pub const TRANSACTION: u32 = 11;
|
||||
}
|
||||
|
||||
struct PendingBlock<Block: BlockT> {
|
||||
@@ -372,10 +397,14 @@ pub struct BlockchainDb<Block: BlockT> {
|
||||
leaves: RwLock<LeafSet<Block::Hash, NumberFor<Block>>>,
|
||||
header_metadata_cache: Arc<HeaderMetadataCache<Block>>,
|
||||
header_cache: Mutex<LinkedHashMap<Block::Hash, Option<Block::Header>>>,
|
||||
transaction_storage: TransactionStorageMode,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> BlockchainDb<Block> {
|
||||
fn new(db: Arc<dyn Database<DbHash>>) -> ClientResult<Self> {
|
||||
fn new(
|
||||
db: Arc<dyn Database<DbHash>>,
|
||||
transaction_storage: TransactionStorageMode
|
||||
) -> ClientResult<Self> {
|
||||
let meta = read_meta::<Block>(&*db, columns::HEADER)?;
|
||||
let leaves = LeafSet::read_from_db(&*db, columns::META, meta_keys::LEAF_PREFIX)?;
|
||||
Ok(BlockchainDb {
|
||||
@@ -384,6 +413,7 @@ impl<Block: BlockT> BlockchainDb<Block> {
|
||||
meta: Arc::new(RwLock::new(meta)),
|
||||
header_metadata_cache: Arc::new(HeaderMetadataCache::default()),
|
||||
header_cache: Default::default(),
|
||||
transaction_storage,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -418,6 +448,20 @@ impl<Block: BlockT> BlockchainDb<Block> {
|
||||
header.digest().log(DigestItem::as_changes_trie_root)
|
||||
.cloned()))
|
||||
}
|
||||
|
||||
fn extrinsic(&self, hash: &Block::Hash) -> ClientResult<Option<Block::Extrinsic>> {
|
||||
match self.db.get(columns::TRANSACTION, hash.as_ref()) {
|
||||
Some(ex) => {
|
||||
match Decode::decode(&mut &ex[..]) {
|
||||
Ok(ex) => Ok(Some(ex)),
|
||||
Err(err) => Err(sp_blockchain::Error::Backend(
|
||||
format!("Error decoding extrinsic {}: {}", hash, err)
|
||||
)),
|
||||
}
|
||||
},
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> sc_client_api::blockchain::HeaderBackend<Block> for BlockchainDb<Block> {
|
||||
@@ -476,11 +520,30 @@ impl<Block: BlockT> sc_client_api::blockchain::HeaderBackend<Block> for Blockcha
|
||||
impl<Block: BlockT> sc_client_api::blockchain::Backend<Block> for BlockchainDb<Block> {
|
||||
fn body(&self, id: BlockId<Block>) -> ClientResult<Option<Vec<Block::Extrinsic>>> {
|
||||
match read_db(&*self.db, columns::KEY_LOOKUP, columns::BODY, id)? {
|
||||
Some(body) => match Decode::decode(&mut &body[..]) {
|
||||
Ok(body) => Ok(Some(body)),
|
||||
Err(err) => return Err(sp_blockchain::Error::Backend(
|
||||
format!("Error decoding body: {}", err)
|
||||
)),
|
||||
Some(body) => {
|
||||
match self.transaction_storage {
|
||||
TransactionStorageMode::BlockBody => match Decode::decode(&mut &body[..]) {
|
||||
Ok(body) => Ok(Some(body)),
|
||||
Err(err) => return Err(sp_blockchain::Error::Backend(
|
||||
format!("Error decoding body: {}", err)
|
||||
)),
|
||||
},
|
||||
TransactionStorageMode::StorageChain => {
|
||||
match Vec::<Block::Hash>::decode(&mut &body[..]) {
|
||||
Ok(hashes) => {
|
||||
let extrinsics: ClientResult<Vec<Block::Extrinsic>> = hashes.into_iter().map(
|
||||
|h| self.extrinsic(&h) .and_then(|maybe_ex| maybe_ex.ok_or_else(
|
||||
|| sp_blockchain::Error::Backend(
|
||||
format!("Missing transaction: {}", h))))
|
||||
).collect();
|
||||
Ok(Some(extrinsics?))
|
||||
}
|
||||
Err(err) => return Err(sp_blockchain::Error::Backend(
|
||||
format!("Error decoding body list: {}", err)
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
@@ -855,6 +918,8 @@ pub struct Backend<Block: BlockT> {
|
||||
shared_cache: SharedCache<Block>,
|
||||
import_lock: Arc<RwLock<()>>,
|
||||
is_archive: bool,
|
||||
keep_blocks: KeepBlocks,
|
||||
transaction_storage: TransactionStorageMode,
|
||||
io_stats: FrozenForDuration<(kvdb::IoStats, StateUsageInfo)>,
|
||||
state_usage: Arc<StateUsageStats>,
|
||||
}
|
||||
@@ -871,13 +936,29 @@ impl<Block: BlockT> Backend<Block> {
|
||||
/// Create new memory-backed client backend for tests.
|
||||
#[cfg(any(test, feature = "test-helpers"))]
|
||||
pub fn new_test(keep_blocks: u32, canonicalization_delay: u64) -> Self {
|
||||
Self::new_test_with_tx_storage(
|
||||
keep_blocks,
|
||||
canonicalization_delay,
|
||||
TransactionStorageMode::BlockBody,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create new memory-backed client backend for tests.
|
||||
#[cfg(any(test, feature = "test-helpers"))]
|
||||
fn new_test_with_tx_storage(
|
||||
keep_blocks: u32,
|
||||
canonicalization_delay: u64,
|
||||
transaction_storage: TransactionStorageMode,
|
||||
) -> Self {
|
||||
let db = kvdb_memorydb::create(crate::utils::NUM_COLUMNS);
|
||||
let db = sp_database::as_database(db);
|
||||
let db_setting = DatabaseSettings {
|
||||
state_cache_size: 16777216,
|
||||
state_cache_child_ratio: Some((50, 100)),
|
||||
pruning: PruningMode::keep_blocks(keep_blocks),
|
||||
state_pruning: PruningMode::keep_blocks(keep_blocks),
|
||||
source: DatabaseSettingsSrc::Custom(db),
|
||||
keep_blocks: KeepBlocks::Some(keep_blocks),
|
||||
transaction_storage,
|
||||
};
|
||||
|
||||
Self::new(db_setting, canonicalization_delay).expect("failed to create test-db")
|
||||
@@ -888,12 +969,12 @@ impl<Block: BlockT> Backend<Block> {
|
||||
canonicalization_delay: u64,
|
||||
config: &DatabaseSettings,
|
||||
) -> ClientResult<Self> {
|
||||
let is_archive_pruning = config.pruning.is_archive();
|
||||
let blockchain = BlockchainDb::new(db.clone())?;
|
||||
let is_archive_pruning = config.state_pruning.is_archive();
|
||||
let blockchain = BlockchainDb::new(db.clone(), config.transaction_storage.clone())?;
|
||||
let meta = blockchain.meta.clone();
|
||||
let map_e = |e: sc_state_db::Error<io::Error>| sp_blockchain::Error::from_state_db(e);
|
||||
let state_db: StateDb<_, _> = StateDb::new(
|
||||
config.pruning.clone(),
|
||||
config.state_pruning.clone(),
|
||||
!config.source.supports_ref_counting(),
|
||||
&StateMetaDb(&*db),
|
||||
).map_err(map_e)?;
|
||||
@@ -933,6 +1014,8 @@ impl<Block: BlockT> Backend<Block> {
|
||||
is_archive: is_archive_pruning,
|
||||
io_stats: FrozenForDuration::new(std::time::Duration::from_secs(1)),
|
||||
state_usage: Arc::new(StateUsageStats::new()),
|
||||
keep_blocks: config.keep_blocks.clone(),
|
||||
transaction_storage: config.transaction_storage.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1140,7 +1223,21 @@ impl<Block: BlockT> Backend<Block> {
|
||||
|
||||
transaction.set_from_vec(columns::HEADER, &lookup_key, pending_block.header.encode());
|
||||
if let Some(body) = &pending_block.body {
|
||||
transaction.set_from_vec(columns::BODY, &lookup_key, body.encode());
|
||||
match self.transaction_storage {
|
||||
TransactionStorageMode::BlockBody => {
|
||||
transaction.set_from_vec(columns::BODY, &lookup_key, body.encode());
|
||||
},
|
||||
TransactionStorageMode::StorageChain => {
|
||||
let mut hashes = Vec::with_capacity(body.len());
|
||||
for extrinsic in body {
|
||||
let extrinsic = extrinsic.encode();
|
||||
let hash = HashFor::<Block>::hash(&extrinsic);
|
||||
transaction.set(columns::TRANSACTION, &hash.as_ref(), &extrinsic);
|
||||
hashes.push(hash);
|
||||
}
|
||||
transaction.set_from_vec(columns::BODY, &lookup_key, hashes.encode());
|
||||
},
|
||||
}
|
||||
}
|
||||
if let Some(justification) = pending_block.justification {
|
||||
transaction.set_from_vec(columns::JUSTIFICATION, &lookup_key, justification.encode());
|
||||
@@ -1391,6 +1488,7 @@ impl<Block: BlockT> Backend<Block> {
|
||||
}
|
||||
}
|
||||
|
||||
self.prune_blocks(transaction, f_num)?;
|
||||
let new_displaced = self.blockchain.leaves.write().finalize_height(f_num);
|
||||
match displaced {
|
||||
x @ &mut None => *x = Some(new_displaced),
|
||||
@@ -1399,6 +1497,50 @@ impl<Block: BlockT> Backend<Block> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prune_blocks(
|
||||
&self,
|
||||
transaction: &mut Transaction<DbHash>,
|
||||
finalized: NumberFor<Block>,
|
||||
) -> ClientResult<()> {
|
||||
if let KeepBlocks::Some(keep_blocks) = self.keep_blocks {
|
||||
// Always keep the last finalized block
|
||||
let keep = std::cmp::max(keep_blocks, 1);
|
||||
if finalized < keep.into() {
|
||||
return Ok(())
|
||||
}
|
||||
let number = finalized.saturating_sub(keep.into());
|
||||
match read_db(&*self.storage.db, columns::KEY_LOOKUP, columns::BODY, BlockId::<Block>::number(number))? {
|
||||
Some(body) => {
|
||||
debug!(target: "db", "Removing block #{}", number);
|
||||
utils::remove_from_db(
|
||||
transaction,
|
||||
&*self.storage.db,
|
||||
columns::KEY_LOOKUP,
|
||||
columns::BODY,
|
||||
BlockId::<Block>::number(number),
|
||||
)?;
|
||||
match self.transaction_storage {
|
||||
TransactionStorageMode::BlockBody => {},
|
||||
TransactionStorageMode::StorageChain => {
|
||||
match Vec::<Block::Hash>::decode(&mut &body[..]) {
|
||||
Ok(hashes) => {
|
||||
for h in hashes {
|
||||
transaction.remove(columns::TRANSACTION, h.as_ref());
|
||||
}
|
||||
}
|
||||
Err(err) => return Err(sp_blockchain::Error::Backend(
|
||||
format!("Error decoding body list: {}", err)
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => return Ok(()),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_state_commit(transaction: &mut Transaction<DbHash>, commit: sc_state_db::CommitSet<Vec<u8>>) {
|
||||
@@ -1804,6 +1946,17 @@ pub(crate) mod tests {
|
||||
parent_hash: H256,
|
||||
changes: Option<Vec<(Vec<u8>, Vec<u8>)>>,
|
||||
extrinsics_root: H256,
|
||||
) -> H256 {
|
||||
insert_block(backend, number, parent_hash, changes, extrinsics_root, Vec::new())
|
||||
}
|
||||
|
||||
pub fn insert_block(
|
||||
backend: &Backend<Block>,
|
||||
number: u64,
|
||||
parent_hash: H256,
|
||||
changes: Option<Vec<(Vec<u8>, Vec<u8>)>>,
|
||||
extrinsics_root: H256,
|
||||
body: Vec<ExtrinsicWrapper<u64>>,
|
||||
) -> H256 {
|
||||
use sp_runtime::testing::Digest;
|
||||
|
||||
@@ -1830,7 +1983,7 @@ pub(crate) mod tests {
|
||||
};
|
||||
let mut op = backend.begin_operation().unwrap();
|
||||
backend.begin_state_operation(&mut op, block_id).unwrap();
|
||||
op.set_block_data(header, Some(Vec::new()), None, NewBlockState::Best).unwrap();
|
||||
op.set_block_data(header, Some(body), None, NewBlockState::Best).unwrap();
|
||||
op.update_changes_trie((changes_trie_update, ChangesTrieCacheAction::Clear)).unwrap();
|
||||
backend.commit_operation(op).unwrap();
|
||||
|
||||
@@ -1882,8 +2035,10 @@ pub(crate) mod tests {
|
||||
let backend = Backend::<Block>::new(DatabaseSettings {
|
||||
state_cache_size: 16777216,
|
||||
state_cache_child_ratio: Some((50, 100)),
|
||||
pruning: PruningMode::keep_blocks(1),
|
||||
state_pruning: PruningMode::keep_blocks(1),
|
||||
source: DatabaseSettingsSrc::Custom(backing),
|
||||
keep_blocks: KeepBlocks::All,
|
||||
transaction_storage: TransactionStorageMode::BlockBody,
|
||||
}, 0).unwrap();
|
||||
assert_eq!(backend.blockchain().info().best_number, 9);
|
||||
for i in 0..10 {
|
||||
@@ -2427,4 +2582,33 @@ pub(crate) mod tests {
|
||||
assert_eq!(cht_root_1, cht_root_2);
|
||||
assert_eq!(cht_root_2, cht_root_3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prune_blocks_on_finalize() {
|
||||
for storage in &[TransactionStorageMode::BlockBody, TransactionStorageMode::StorageChain] {
|
||||
let backend = Backend::<Block>::new_test_with_tx_storage(2, 0, *storage);
|
||||
let mut blocks = Vec::new();
|
||||
let mut prev_hash = Default::default();
|
||||
for i in 0 .. 5 {
|
||||
let hash = insert_block(&backend, i, prev_hash, None, Default::default(), vec![i.into()]);
|
||||
blocks.push(hash);
|
||||
prev_hash = hash;
|
||||
}
|
||||
|
||||
{
|
||||
let mut op = backend.begin_operation().unwrap();
|
||||
backend.begin_state_operation(&mut op, BlockId::Hash(blocks[4])).unwrap();
|
||||
for i in 1 .. 5 {
|
||||
op.mark_finalized(BlockId::Hash(blocks[i]), None).unwrap();
|
||||
}
|
||||
backend.commit_operation(op).unwrap();
|
||||
}
|
||||
let bc = backend.blockchain();
|
||||
assert_eq!(None, bc.body(BlockId::hash(blocks[0])).unwrap());
|
||||
assert_eq!(None, bc.body(BlockId::hash(blocks[1])).unwrap());
|
||||
assert_eq!(None, 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,21 +24,26 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
use sp_runtime::traits::Block as BlockT;
|
||||
use crate::utils::DatabaseType;
|
||||
use kvdb_rocksdb::{Database, DatabaseConfig};
|
||||
|
||||
/// Version file name.
|
||||
const VERSION_FILE_NAME: &'static str = "db_version";
|
||||
|
||||
/// Current db version.
|
||||
const CURRENT_VERSION: u32 = 1;
|
||||
const CURRENT_VERSION: u32 = 2;
|
||||
|
||||
/// Number of columns in v1.
|
||||
const V1_NUM_COLUMNS: u32 = 11;
|
||||
|
||||
/// Upgrade database to current version.
|
||||
pub fn upgrade_db<Block: BlockT>(db_path: &Path, _db_type: DatabaseType) -> sp_blockchain::Result<()> {
|
||||
pub fn upgrade_db<Block: BlockT>(db_path: &Path, db_type: DatabaseType) -> sp_blockchain::Result<()> {
|
||||
let is_empty = db_path.read_dir().map_or(true, |mut d| d.next().is_none());
|
||||
if !is_empty {
|
||||
let db_version = current_version(db_path)?;
|
||||
match db_version {
|
||||
0 => Err(sp_blockchain::Error::Backend(format!("Unsupported database version: {}", db_version)))?,
|
||||
1 => (),
|
||||
1 => migrate_1_to_2::<Block>(db_path, db_type)?,
|
||||
CURRENT_VERSION => (),
|
||||
_ => Err(sp_blockchain::Error::Backend(format!("Future database version: {}", db_version)))?,
|
||||
}
|
||||
}
|
||||
@@ -46,6 +51,16 @@ pub fn upgrade_db<Block: BlockT>(db_path: &Path, _db_type: DatabaseType) -> sp_b
|
||||
update_version(db_path)
|
||||
}
|
||||
|
||||
/// Migration from version1 to version2:
|
||||
/// 1) the number of columns has changed from 11 to 12;
|
||||
/// 2) transactions column is added;
|
||||
fn migrate_1_to_2<Block: BlockT>(db_path: &Path, _db_type: DatabaseType) -> sp_blockchain::Result<()> {
|
||||
let db_path = db_path.to_str()
|
||||
.ok_or_else(|| sp_blockchain::Error::Backend("Invalid database path".into()))?;
|
||||
let db_cfg = DatabaseConfig::with_columns(V1_NUM_COLUMNS);
|
||||
let db = Database::open(&db_cfg, db_path).map_err(db_err)?;
|
||||
db.add_column().map_err(db_err)
|
||||
}
|
||||
|
||||
/// Reads current database version from the file at given path.
|
||||
/// If the file does not exist returns 0.
|
||||
@@ -87,7 +102,7 @@ fn version_file_path(path: &Path) -> PathBuf {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use sc_state_db::PruningMode;
|
||||
use crate::{DatabaseSettings, DatabaseSettingsSrc};
|
||||
use crate::{DatabaseSettings, DatabaseSettingsSrc, KeepBlocks, TransactionStorageMode};
|
||||
use crate::tests::Block;
|
||||
use super::*;
|
||||
|
||||
@@ -103,8 +118,10 @@ mod tests {
|
||||
crate::utils::open_database::<Block>(&DatabaseSettings {
|
||||
state_cache_size: 0,
|
||||
state_cache_child_ratio: None,
|
||||
pruning: PruningMode::ArchiveAll,
|
||||
state_pruning: PruningMode::ArchiveAll,
|
||||
source: DatabaseSettingsSrc::RocksDb { path: db_path.to_owned(), cache_size: 128 },
|
||||
keep_blocks: KeepBlocks::All,
|
||||
transaction_storage: TransactionStorageMode::BlockBody,
|
||||
}, DatabaseType::Full).map(|_| ())
|
||||
}
|
||||
|
||||
@@ -122,4 +139,15 @@ mod tests {
|
||||
open_database(db_dir.path()).unwrap();
|
||||
assert_eq!(current_version(db_dir.path()).unwrap(), CURRENT_VERSION);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_from_1_to_2_works() {
|
||||
for version_from_file in &[None, Some(1)] {
|
||||
let db_dir = tempfile::TempDir::new().unwrap();
|
||||
let db_path = db_dir.path();
|
||||
create_db(db_path, *version_from_file);
|
||||
open_database(db_path).unwrap();
|
||||
assert_eq!(current_version(db_path).unwrap(), CURRENT_VERSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ use crate::{DatabaseSettings, DatabaseSettingsSrc, Database, DbHash};
|
||||
/// Number of columns in the db. Must be the same for both full && light dbs.
|
||||
/// Otherwise RocksDb will fail to open database && check its type.
|
||||
#[cfg(any(feature = "with-kvdb-rocksdb", feature = "with-parity-db", feature = "test-helpers", test))]
|
||||
pub const NUM_COLUMNS: u32 = 11;
|
||||
pub const NUM_COLUMNS: u32 = 12;
|
||||
/// Meta column. The set of keys in the column is shared by full && light storages.
|
||||
pub const COLUMN_META: u32 = 0;
|
||||
|
||||
@@ -327,6 +327,23 @@ pub fn read_db<Block>(
|
||||
})
|
||||
}
|
||||
|
||||
/// Remove database column entry for the given block.
|
||||
pub fn remove_from_db<Block>(
|
||||
transaction: &mut Transaction<DbHash>,
|
||||
db: &dyn Database<DbHash>,
|
||||
col_index: u32,
|
||||
col: u32,
|
||||
id: BlockId<Block>,
|
||||
) -> sp_blockchain::Result<()>
|
||||
where
|
||||
Block: BlockT,
|
||||
{
|
||||
block_id_to_lookup_key(db, col_index, id).and_then(|key| match key {
|
||||
Some(key) => Ok(transaction.remove(col, key.as_ref())),
|
||||
None => Ok(()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Read a header from the database.
|
||||
pub fn read_header<Block: BlockT>(
|
||||
db: &dyn Database<DbHash>,
|
||||
|
||||
@@ -326,8 +326,10 @@ pub fn new_full_parts<TBl, TRtApi, TExecDisp>(
|
||||
state_cache_size: config.state_cache_size,
|
||||
state_cache_child_ratio:
|
||||
config.state_cache_child_ratio.map(|v| (v, 100)),
|
||||
pruning: config.pruning.clone(),
|
||||
state_pruning: config.state_pruning.clone(),
|
||||
source: config.database.clone(),
|
||||
keep_blocks: config.keep_blocks.clone(),
|
||||
transaction_storage: config.transaction_storage.clone(),
|
||||
};
|
||||
|
||||
let extensions = sc_client_api::execution_extensions::ExecutionExtensions::new(
|
||||
@@ -384,8 +386,10 @@ pub fn new_light_parts<TBl, TRtApi, TExecDisp>(
|
||||
state_cache_size: config.state_cache_size,
|
||||
state_cache_child_ratio:
|
||||
config.state_cache_child_ratio.map(|v| (v, 100)),
|
||||
pruning: config.pruning.clone(),
|
||||
state_pruning: config.state_pruning.clone(),
|
||||
source: config.database.clone(),
|
||||
keep_blocks: config.keep_blocks.clone(),
|
||||
transaction_storage: config.transaction_storage.clone(),
|
||||
};
|
||||
sc_client_db::light::LightStorage::new(db_settings)?
|
||||
};
|
||||
|
||||
@@ -18,7 +18,10 @@
|
||||
|
||||
//! Service configuration.
|
||||
|
||||
pub use sc_client_db::{Database, PruningMode, DatabaseSettingsSrc as DatabaseConfig};
|
||||
pub use sc_client_db::{
|
||||
Database, PruningMode, DatabaseSettingsSrc as DatabaseConfig,
|
||||
KeepBlocks, TransactionStorageMode
|
||||
};
|
||||
pub use sc_network::Multiaddr;
|
||||
pub use sc_network::config::{ExtTransport, MultiaddrWithPeerId, NetworkConfiguration, Role, NodeKeyConfig};
|
||||
pub use sc_executor::WasmExecutionMethod;
|
||||
@@ -58,8 +61,12 @@ pub struct Configuration {
|
||||
pub state_cache_size: usize,
|
||||
/// Size in percent of cache size dedicated to child tries
|
||||
pub state_cache_child_ratio: Option<usize>,
|
||||
/// Pruning settings.
|
||||
pub pruning: PruningMode,
|
||||
/// State pruning settings.
|
||||
pub state_pruning: PruningMode,
|
||||
/// Number of blocks to keep in the db.
|
||||
pub keep_blocks: KeepBlocks,
|
||||
/// Transaction storage scheme.
|
||||
pub transaction_storage: TransactionStorageMode,
|
||||
/// Chain configuration.
|
||||
pub chain_spec: Box<dyn ChainSpec>,
|
||||
/// Wasm execution method.
|
||||
|
||||
@@ -60,6 +60,7 @@ pub use self::builder::{
|
||||
};
|
||||
pub use config::{
|
||||
BasePath, Configuration, DatabaseConfig, PruningMode, Role, RpcMethods, TaskExecutor, TaskType,
|
||||
KeepBlocks, TransactionStorageMode,
|
||||
};
|
||||
pub use sc_chain_spec::{
|
||||
ChainSpec, GenericChainSpec, Properties, RuntimeGenesis, Extension as ChainSpecExtension,
|
||||
|
||||
@@ -31,7 +31,9 @@ use substrate_test_runtime_client::{
|
||||
use sc_client_api::{
|
||||
StorageProvider, BlockBackend, in_mem, BlockchainEvents,
|
||||
};
|
||||
use sc_client_db::{Backend, DatabaseSettings, DatabaseSettingsSrc, PruningMode};
|
||||
use sc_client_db::{
|
||||
Backend, DatabaseSettings, DatabaseSettingsSrc, PruningMode, KeepBlocks, TransactionStorageMode
|
||||
};
|
||||
use sc_block_builder::BlockBuilderProvider;
|
||||
use sc_service::client::{self, Client, LocalCallExecutor, new_in_mem};
|
||||
use sp_runtime::traits::{
|
||||
@@ -1275,7 +1277,9 @@ fn doesnt_import_blocks_that_revert_finality() {
|
||||
DatabaseSettings {
|
||||
state_cache_size: 1 << 20,
|
||||
state_cache_child_ratio: None,
|
||||
pruning: PruningMode::ArchiveAll,
|
||||
state_pruning: PruningMode::ArchiveAll,
|
||||
keep_blocks: KeepBlocks::All,
|
||||
transaction_storage: TransactionStorageMode::BlockBody,
|
||||
source: DatabaseSettingsSrc::RocksDb {
|
||||
path: tmp.path().into(),
|
||||
cache_size: 1024,
|
||||
@@ -1476,7 +1480,9 @@ fn returns_status_for_pruned_blocks() {
|
||||
DatabaseSettings {
|
||||
state_cache_size: 1 << 20,
|
||||
state_cache_child_ratio: None,
|
||||
pruning: PruningMode::keep_blocks(1),
|
||||
state_pruning: PruningMode::keep_blocks(1),
|
||||
keep_blocks: KeepBlocks::All,
|
||||
transaction_storage: TransactionStorageMode::BlockBody,
|
||||
source: DatabaseSettingsSrc::RocksDb {
|
||||
path: tmp.path().into(),
|
||||
cache_size: 1024,
|
||||
|
||||
@@ -35,6 +35,7 @@ use sc_service::{
|
||||
GenericChainSpec,
|
||||
ChainSpecExtension,
|
||||
Configuration,
|
||||
KeepBlocks, TransactionStorageMode,
|
||||
config::{BasePath, DatabaseConfig, KeystoreConfig},
|
||||
RuntimeGenesis,
|
||||
Role,
|
||||
@@ -250,7 +251,9 @@ fn node_config<G: RuntimeGenesis + 'static, E: ChainSpecExtension + Clone + 'sta
|
||||
},
|
||||
state_cache_size: 16777216,
|
||||
state_cache_child_ratio: None,
|
||||
pruning: Default::default(),
|
||||
state_pruning: Default::default(),
|
||||
keep_blocks: KeepBlocks::All,
|
||||
transaction_storage: TransactionStorageMode::BlockBody,
|
||||
chain_spec: Box::new((*spec).clone()),
|
||||
wasm_method: sc_service::config::WasmExecutionMethod::Interpreted,
|
||||
wasm_runtime_overrides: Default::default(),
|
||||
|
||||
@@ -21,7 +21,8 @@ use sc_network::config::TransportConfig;
|
||||
use sc_service::{
|
||||
RpcSession, Role, Configuration, TaskManager, RpcHandlers,
|
||||
config::{DatabaseConfig, KeystoreConfig, NetworkConfiguration},
|
||||
GenericChainSpec, RuntimeGenesis
|
||||
GenericChainSpec, RuntimeGenesis,
|
||||
KeepBlocks, TransactionStorageMode,
|
||||
};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use futures::{
|
||||
@@ -86,7 +87,9 @@ where
|
||||
impl_version: String::from("0.0.0"),
|
||||
offchain_worker: Default::default(),
|
||||
prometheus_config: Default::default(),
|
||||
pruning: Default::default(),
|
||||
state_pruning: Default::default(),
|
||||
keep_blocks: KeepBlocks::All,
|
||||
transaction_storage: TransactionStorageMode::BlockBody,
|
||||
rpc_cors: Default::default(),
|
||||
rpc_http: Default::default(),
|
||||
rpc_ipc: Default::default(),
|
||||
|
||||
Reference in New Issue
Block a user