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

Commit 654e652

Browse files
rphmeiergavofyork
authored andcommitted
Archive Mode and groundwork for state-preserving handles (#166)
* less eager deletion of DB values * archive mode
1 parent 126498a commit 654e652

File tree

6 files changed

+202
-65
lines changed

6 files changed

+202
-65
lines changed

polkadot/consensus/src/service.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,6 @@ fn start_bft<F, C>(
198198
}
199199
};
200200

201-
202201
let input = Messages {
203202
network_stream: network.bft_messages(parent_hash),
204203
local_id: bft_service.local_id(),

substrate/client/db/src/lib.rs

Lines changed: 127 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,6 @@ struct PendingBlock {
9696
is_best: bool,
9797
}
9898

99-
/// Database transaction
100-
pub struct BlockImportOperation {
101-
pending_state: DbState,
102-
pending_block: Option<PendingBlock>,
103-
}
104-
10599
#[derive(Clone)]
106100
struct Meta {
107101
best_hash: HeaderHash,
@@ -261,11 +255,18 @@ impl client::blockchain::Backend for BlockchainDb {
261255
}
262256
}
263257

258+
/// Database transaction
259+
pub struct BlockImportOperation {
260+
old_state: DbState,
261+
updates: MemoryDB,
262+
pending_block: Option<PendingBlock>,
263+
}
264+
264265
impl client::backend::BlockImportOperation for BlockImportOperation {
265266
type State = DbState;
266267

267268
fn state(&self) -> Result<&Self::State, client::error::Error> {
268-
Ok(&self.pending_state)
269+
Ok(&self.old_state)
269270
}
270271

271272
fn set_block_data(&mut self, header: block::Header, body: Option<block::Body>, justification: Option<primitives::bft::Justification>, is_best: bool) -> Result<(), client::error::Error> {
@@ -280,14 +281,14 @@ impl client::backend::BlockImportOperation for BlockImportOperation {
280281
}
281282

282283
fn update_storage(&mut self, update: MemoryDB) -> Result<(), client::error::Error> {
283-
self.pending_state.commit(update);
284+
self.updates = update;
284285
Ok(())
285286
}
286287

287288
fn reset_storage<I: Iterator<Item=(Vec<u8>, Vec<u8>)>>(&mut self, iter: I) -> Result<(), client::error::Error> {
288289
// TODO: wipe out existing trie.
289-
let (_, update) = self.pending_state.storage_root(iter.into_iter().map(|(k, v)| (k, Some(v))));
290-
self.pending_state.commit(update);
290+
let (_, update) = self.old_state.storage_root(iter.into_iter().map(|(k, v)| (k, Some(v))));
291+
self.updates = update;
291292
Ok(())
292293
}
293294
}
@@ -341,10 +342,10 @@ impl<'a> HashDB for Ephemeral<'a> {
341342
}
342343

343344
/// DB-backed patricia trie state, transaction type is an overlay of changes to commit.
345+
#[derive(Clone)]
344346
pub struct DbState {
345347
db: Arc<KeyValueDB>,
346348
root: TrieH256,
347-
updates: MemoryDB,
348349
}
349350

350351
impl state_machine::Backend for DbState {
@@ -364,10 +365,6 @@ impl state_machine::Backend for DbState {
364365
.get(key).map(|x| x.map(|val| val.to_vec())).map_err(map_e)
365366
}
366367

367-
fn commit(&mut self, transaction: MemoryDB) {
368-
self.updates = transaction;
369-
}
370-
371368
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
372369
let mut read_overlay = MemoryDB::default();
373370
let eph = Ephemeral {
@@ -423,10 +420,12 @@ impl state_machine::Backend for DbState {
423420
}
424421
}
425422

426-
/// In-memory backend. Keeps all states and blocks in memory. Useful for testing.
423+
/// Disk backend. Keeps data in a key-value store. In archive mode, trie nodes are kept from all blocks.
424+
/// Otherwise, trie nodes are kept only from the most recent block.
427425
pub struct Backend {
428426
db: Arc<KeyValueDB>,
429427
blockchain: BlockchainDb,
428+
archive: bool,
430429
}
431430

432431
impl Backend {
@@ -438,22 +437,23 @@ impl Backend {
438437
let path = config.path.to_str().ok_or_else(|| client::error::ErrorKind::Backend("Invalid database path".into()))?;
439438
let db = Arc::new(Database::open(&db_config, &path).map_err(db_err)?);
440439

441-
Backend::from_kvdb(db as Arc<_>)
440+
Backend::from_kvdb(db as Arc<_>, true)
442441
}
443442

444443
#[cfg(test)]
445444
fn new_test() -> Backend {
446445
let db = Arc::new(::kvdb_memorydb::create(columns::NUM_COLUMNS));
447446

448-
Backend::from_kvdb(db as Arc<_>).expect("failed to create test-db")
447+
Backend::from_kvdb(db as Arc<_>, false).expect("failed to create test-db")
449448
}
450449

451-
fn from_kvdb(db: Arc<KeyValueDB>) -> Result<Backend, client::error::Error> {
450+
fn from_kvdb(db: Arc<KeyValueDB>, archive: bool) -> Result<Backend, client::error::Error> {
452451
let blockchain = BlockchainDb::new(db.clone())?;
453452

454453
Ok(Backend {
455454
db,
456455
blockchain,
456+
archive
457457
})
458458
}
459459
}
@@ -467,7 +467,8 @@ impl client::backend::Backend for Backend {
467467
let state = self.state_at(block)?;
468468
Ok(BlockImportOperation {
469469
pending_block: None,
470-
pending_state: state,
470+
old_state: state,
471+
updates: MemoryDB::default(),
471472
})
472473
}
473474

@@ -488,10 +489,10 @@ impl client::backend::Backend for Backend {
488489
if pending_block.is_best {
489490
transaction.put(columns::META, meta::BEST_BLOCK, &key);
490491
}
491-
for (key, (val, rc)) in operation.pending_state.updates.drain() {
492+
for (key, (val, rc)) in operation.updates.drain() {
492493
if rc > 0 {
493494
transaction.put(columns::STATE, &key.0[..], &val);
494-
} else {
495+
} else if rc < 0 && !self.archive {
495496
transaction.delete(columns::STATE, &key.0[..]);
496497
}
497498
}
@@ -518,7 +519,6 @@ impl client::backend::Backend for Backend {
518519

519520
return Ok(DbState {
520521
db: self.db.clone(),
521-
updates: Default::default(),
522522
root,
523523
})
524524
}
@@ -528,7 +528,6 @@ impl client::backend::Backend for Backend {
528528
self.blockchain.header(block).and_then(|maybe_hdr| maybe_hdr.map(|hdr| {
529529
DbState {
530530
db: self.db.clone(),
531-
updates: Default::default(),
532531
root: hdr.state_root.0.into(),
533532
}
534533
}).ok_or_else(|| client::error::ErrorKind::UnknownBlock(block).into()))
@@ -595,7 +594,7 @@ mod tests {
595594
(vec![1, 2, 3], vec![9, 9, 9]),
596595
];
597596

598-
header.state_root = op.pending_state.storage_root(storage
597+
header.state_root = op.old_state.storage_root(storage
599598
.iter()
600599
.cloned()
601600
.map(|(x, y)| (x, Some(y)))
@@ -634,7 +633,7 @@ mod tests {
634633
(vec![5, 5, 5], Some(vec![4, 5, 6])),
635634
];
636635

637-
let (root, overlay) = op.pending_state.storage_root(storage.iter().cloned());
636+
let (root, overlay) = op.old_state.storage_root(storage.iter().cloned());
638637
op.update_storage(overlay).unwrap();
639638
header.state_root = root.into();
640639

@@ -654,4 +653,106 @@ mod tests {
654653
assert_eq!(state.storage(&[5, 5, 5]).unwrap(), Some(vec![4, 5, 6]));
655654
}
656655
}
656+
657+
#[test]
658+
fn delete_only_when_negative_rc() {
659+
let key;
660+
let db = Backend::new_test();
661+
662+
{
663+
let mut op = db.begin_operation(BlockId::Hash(Default::default())).unwrap();
664+
let mut header = block::Header {
665+
number: 0,
666+
parent_hash: Default::default(),
667+
state_root: Default::default(),
668+
digest: Default::default(),
669+
extrinsics_root: Default::default(),
670+
};
671+
672+
let storage: Vec<(_, _)> = vec![];
673+
674+
header.state_root = op.old_state.storage_root(storage
675+
.iter()
676+
.cloned()
677+
.map(|(x, y)| (x, Some(y)))
678+
).0.into();
679+
680+
op.reset_storage(storage.iter().cloned()).unwrap();
681+
682+
key = op.updates.insert(b"hello");
683+
op.set_block_data(
684+
header,
685+
Some(vec![]),
686+
None,
687+
true
688+
).unwrap();
689+
690+
db.commit_operation(op).unwrap();
691+
692+
assert_eq!(db.db.get(::columns::STATE, &key.0[..]).unwrap().unwrap(), &b"hello"[..]);
693+
}
694+
695+
{
696+
let mut op = db.begin_operation(BlockId::Number(0)).unwrap();
697+
let mut header = block::Header {
698+
number: 1,
699+
parent_hash: Default::default(),
700+
state_root: Default::default(),
701+
digest: Default::default(),
702+
extrinsics_root: Default::default(),
703+
};
704+
705+
let storage: Vec<(_, _)> = vec![];
706+
707+
header.state_root = op.old_state.storage_root(storage
708+
.iter()
709+
.cloned()
710+
.map(|(x, y)| (x, Some(y)))
711+
).0.into();
712+
713+
op.updates.insert(b"hello");
714+
op.updates.remove(&key);
715+
op.set_block_data(
716+
header,
717+
Some(vec![]),
718+
None,
719+
true
720+
).unwrap();
721+
722+
db.commit_operation(op).unwrap();
723+
724+
assert_eq!(db.db.get(::columns::STATE, &key.0[..]).unwrap().unwrap(), &b"hello"[..]);
725+
}
726+
727+
{
728+
let mut op = db.begin_operation(BlockId::Number(1)).unwrap();
729+
let mut header = block::Header {
730+
number: 1,
731+
parent_hash: Default::default(),
732+
state_root: Default::default(),
733+
digest: Default::default(),
734+
extrinsics_root: Default::default(),
735+
};
736+
737+
let storage: Vec<(_, _)> = vec![];
738+
739+
header.state_root = op.old_state.storage_root(storage
740+
.iter()
741+
.cloned()
742+
.map(|(x, y)| (x, Some(y)))
743+
).0.into();
744+
745+
op.updates.remove(&key);
746+
op.set_block_data(
747+
header,
748+
Some(vec![]),
749+
None,
750+
true
751+
).unwrap();
752+
753+
db.commit_operation(op).unwrap();
754+
755+
assert!(db.db.get(::columns::STATE, &key.0[..]).unwrap().is_none());
756+
}
757+
}
657758
}

substrate/client/src/backend.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ pub trait BlockImportOperation {
3737
}
3838

3939
/// Client backend. Manages the data layer.
40+
///
41+
/// Note on state pruning: while an object from `state_at` is alive, the state
42+
/// should not be pruned. The backend should internally reference-count
43+
/// its state objects.
44+
///
45+
/// The same applies for live `BlockImportOperation`s: while an import operation building on a parent `P`
46+
/// is alive, the state for `P` should not be pruned.
4047
pub trait Backend {
4148
/// Associated block insertion operation type.
4249
type BlockImportOperation: BlockImportOperation;
@@ -52,6 +59,6 @@ pub trait Backend {
5259
fn commit_operation(&self, transaction: Self::BlockImportOperation) -> error::Result<()>;
5360
/// Returns reference to blockchain backend.
5461
fn blockchain(&self) -> &Self::Blockchain;
55-
/// Returns state backend for specified block.
62+
/// Returns state backend with post-state of given block.
5663
fn state_at(&self, block: BlockId) -> error::Result<Self::State>;
5764
}

0 commit comments

Comments
 (0)