-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Accounts bloom #2357
Accounts bloom #2357
Changes from 20 commits
5207927
5efc137
d2217f7
dc95ee2
82c08bd
1f7036e
12abc31
c33e442
c45e992
260e75f
c2022fa
f3b094a
a55e108
ee36df0
ee34d0e
2db6e7e
039c345
1bf7f46
f4b5de8
e25441d
def6343
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| // Copyright 2015, 2016 Ethcore (UK) Ltd. | ||
| // This file is part of Parity. | ||
|
|
||
| // Parity is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, either version 3 of the License, or | ||
| // (at your option) any later version. | ||
|
|
||
| // Parity is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License for more details. | ||
|
|
||
| // You should have received a copy of the GNU General Public License | ||
| // along with Parity. If not, see <http://www.gnu.org/licenses/>. | ||
|
|
||
| //! Bloom upgrade | ||
|
|
||
| use client::{DB_COL_EXTRA, DB_COL_HEADERS, DB_NO_OF_COLUMNS, DB_COL_STATE}; | ||
| use state_db::{ACCOUNT_BLOOM_SPACE, DEFAULT_ACCOUNT_PRESET, StateDB}; | ||
| use util::trie::TrieDB; | ||
| use views::HeaderView; | ||
| use bloomfilter::Bloom; | ||
| use util::migration::Error; | ||
| use util::journaldb; | ||
| use util::{H256, FixedHash, BytesConvertable}; | ||
| use util::{Database, DatabaseConfig, DBTransaction, CompactionProfile}; | ||
| use std::path::Path; | ||
|
|
||
| /// Account bloom upgrade routine. If bloom already present, does nothing. | ||
| /// If database empty (no best block), does nothing. | ||
| /// Can be called on upgraded database with no issues (will do nothing). | ||
| pub fn upgrade_account_bloom(db_path: &Path) -> Result<(), Error> { | ||
| let path = try!(db_path.to_str().ok_or(Error::MigrationImpossible)); | ||
| trace!(target: "migration", "Account bloom upgrade at {:?}", db_path); | ||
|
|
||
| let source = try!(Database::open(&DatabaseConfig { | ||
| max_open_files: 64, | ||
| cache_size: None, | ||
| compaction: CompactionProfile::default(), | ||
| columns: DB_NO_OF_COLUMNS, | ||
| wal: true, | ||
| }, path)); | ||
|
|
||
| let best_block_hash = match try!(source.get(DB_COL_EXTRA, b"best")) { | ||
| // no migration needed | ||
| None => { | ||
| trace!(target: "migration", "No best block hash, skipping"); | ||
| return Ok(()); | ||
| }, | ||
| Some(hash) => hash, | ||
| }; | ||
| let best_block_header = match try!(source.get(DB_COL_HEADERS, &best_block_hash)) { | ||
| // no best block, nothing to do | ||
| None => { | ||
| trace!(target: "migration", "No best block header, skipping"); | ||
| return Ok(()) | ||
| }, | ||
| Some(x) => x, | ||
| }; | ||
| let state_root = HeaderView::new(&best_block_header).state_root(); | ||
|
|
||
| if StateDB::check_bloom_exists(&source) { | ||
| // bloom already exists, nothing to do | ||
| trace!(target: "migration", "Bloom already present, skipping"); | ||
| return Ok(()) | ||
| } | ||
|
|
||
| println!("Adding accounts bloom (one-time upgrade)"); | ||
| let db = ::std::sync::Arc::new(source); | ||
| let bloom_journal = { | ||
| let mut bloom = Bloom::new(ACCOUNT_BLOOM_SPACE, DEFAULT_ACCOUNT_PRESET); | ||
| // no difference what algorithm is passed, since there will be no writes | ||
| let state_db = journaldb::new( | ||
| db.clone(), | ||
| journaldb::Algorithm::OverlayRecent, | ||
| DB_COL_STATE); | ||
| let account_trie = try!(TrieDB::new(state_db.as_hashdb(), &state_root).map_err(|e| Error::Custom(format!("Cannot open trie: {:?}", e)))); | ||
| for (ref account_key, _) in account_trie.iter() { | ||
| let account_key_hash = H256::from_slice(&account_key); | ||
| bloom.set(account_key_hash.as_slice()); | ||
| } | ||
|
|
||
| bloom.drain_journal() | ||
| }; | ||
|
|
||
| trace!(target: "migration", "Generated {} bloom updates", bloom_journal.entries.len()); | ||
|
|
||
| let batch = DBTransaction::new(&db); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unsure if this can or can not happend concurrently
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Becomes a non-issue if you switch the strategy to the one described in my other comment. |
||
| try!(StateDB::commit_bloom(&batch, bloom_journal).map_err(|_| Error::Custom("Failed to commit bloom".to_owned()))); | ||
| try!(db.write(batch)); | ||
|
|
||
| trace!(target: "migration", "Finished bloom update"); | ||
|
|
||
|
|
||
| Ok(()) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| // Copyright 2015, 2016 Ethcore (UK) Ltd. | ||
| // This file is part of Parity. | ||
|
|
||
| // Parity is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, either version 3 of the License, or | ||
| // (at your option) any later version. | ||
|
|
||
| // Parity is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License for more details. | ||
|
|
||
| // You should have received a copy of the GNU General Public License | ||
| // along with Parity. If not, see <http://www.gnu.org/licenses/>. | ||
|
|
||
| //! This migration compresses the state db. | ||
|
|
||
| use util::migration::SimpleMigration; | ||
|
|
||
| /// Compressing migration. | ||
| #[derive(Default)] | ||
| pub struct ToV10; | ||
|
|
||
| impl SimpleMigration for ToV10 { | ||
| fn version(&self) -> u32 { | ||
| 10 | ||
| } | ||
|
|
||
| fn columns(&self) -> Option<u32> { Some(6) } | ||
|
|
||
| fn simple_migrate(&mut self, key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> { | ||
| Some((key, value)) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -259,9 +259,12 @@ impl State { | |
| /// Mutate storage of account `address` so that it is `value` for `key`. | ||
| pub fn storage_at(&self, address: &Address, key: &H256) -> H256 { | ||
| // Storage key search and update works like this: | ||
| // 1. If there's an entry for the account in the local cache check for the key and return it if found. | ||
| // 2. If there's an entry for the account in the global cache check for the key or load it into that account. | ||
| // 3. If account is missing in the global cache load it into the local cache and cache the key there. | ||
| // 1. Check bloom to see if account never used surely | ||
| // 2. If there's an entry for the account in the local cache check for the key and return it if found. | ||
| // 3. If there's an entry for the account in the global cache check for the key or load it into that account. | ||
| // 4. If account is missing in the global cache load it into the local cache and cache the key there. | ||
|
|
||
| // check bloom | ||
|
|
||
| // check local cache first without updating | ||
| { | ||
|
|
@@ -293,6 +296,7 @@ impl State { | |
| } | ||
| } | ||
| // account is not found in the global cache, get from the DB and insert into local | ||
| if !self.db.check_account_bloom(address) { return H256::zero() } | ||
| let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); | ||
| let maybe_acc = match db.get(address) { | ||
| Ok(acc) => acc.map(Account::from_rlp), | ||
|
|
@@ -387,6 +391,7 @@ impl State { | |
| for (address, ref mut a) in accounts.iter_mut() { | ||
| match a { | ||
| &mut&mut AccountEntry::Cached(ref mut account) if account.is_dirty() => { | ||
| db.note_account_bloom(&address); | ||
| let mut account_db = AccountDBMut::from_hash(db.as_hashdb_mut(), account.address_hash(address)); | ||
| account.commit_storage(trie_factory, &mut account_db); | ||
| account.commit_code(&mut account_db); | ||
|
|
@@ -449,6 +454,7 @@ impl State { | |
| pub fn populate_from(&mut self, accounts: PodState) { | ||
| assert!(self.snapshots.borrow().is_empty()); | ||
| for (add, acc) in accounts.drain().into_iter() { | ||
| self.db.note_account_bloom(&add); | ||
| self.cache.borrow_mut().insert(add, AccountEntry::Cached(Account::from_pod(acc))); | ||
| } | ||
| } | ||
|
|
@@ -525,6 +531,7 @@ impl State { | |
| Some(r) => r, | ||
| None => { | ||
| // not found in the global cache, get from the DB and insert into local | ||
| if !self.db.check_account_bloom(a) { return f(None); } | ||
| let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); | ||
| let mut maybe_acc = match db.get(a) { | ||
| Ok(acc) => acc.map(Account::from_rlp), | ||
|
|
@@ -559,11 +566,17 @@ impl State { | |
| Some(Some(acc)) => self.insert_cache(a, AccountEntry::Cached(acc)), | ||
| Some(None) => self.insert_cache(a, AccountEntry::Missing), | ||
| None => { | ||
| let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); | ||
| let maybe_acc = match db.get(a) { | ||
| Ok(Some(acc)) => AccountEntry::Cached(Account::from_rlp(acc)), | ||
| Ok(None) => AccountEntry::Missing, | ||
| Err(e) => panic!("Potential DB corruption encountered: {}", e), | ||
| let maybe_acc = if self.db.check_account_bloom(a) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this check can be saved just by caching the value of the previous query
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can't see how |
||
| let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); | ||
| let maybe_acc = match db.get(a) { | ||
| Ok(Some(acc)) => AccountEntry::Cached(Account::from_rlp(acc)), | ||
| Ok(None) => AccountEntry::Missing, | ||
| Err(e) => panic!("Potential DB corruption encountered: {}", e), | ||
| }; | ||
| maybe_acc | ||
| } | ||
| else { | ||
| AccountEntry::Missing | ||
| }; | ||
| self.insert_cache(a, maybe_acc); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all constants should be reproduced in the migration implementation for backwards compatibility -- there is no guarantee that they will remain the same as they were at the database version you're migrating.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's not a migration strictly, it's inplace upgrade
it's perfectly guaranteed that the columns should be the same otherwise all other logic will instantly fail