-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Keep existing blocks when restoring a Snapshot #8643
Changes from 41 commits
739212d
4028d5f
5bdbe0e
c3719d2
c2cd1c6
2581509
bda7b4d
0713f7f
cc0e6ba
074cc47
2613611
c9739db
ca95485
f7c50a6
5964934
8724db8
e50052c
67774d6
365ed0c
2cb5772
62293f9
2fac52e
13a3290
c33c1c4
8ba3cde
408ede7
c5b0da7
2adc847
275c303
b3387f6
9cf2bcf
6af933c
3c74448
8c58bd1
b114c65
1101055
9775ee7
e490a89
1242628
cc99b16
835d0d9
1f43ce8
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 |
---|---|---|
|
@@ -229,6 +229,7 @@ pub struct BlockChain { | |
|
||
cache_man: Mutex<CacheManager<CacheId>>, | ||
|
||
pending_best_ancient_block: RwLock<Option<Option<BestAncientBlock>>>, | ||
pending_best_block: RwLock<Option<BestBlock>>, | ||
pending_block_hashes: RwLock<HashMap<BlockNumber, H256>>, | ||
pending_block_details: RwLock<HashMap<H256, BlockDetails>>, | ||
|
@@ -538,6 +539,7 @@ impl BlockChain { | |
block_receipts: RwLock::new(HashMap::new()), | ||
db: db.clone(), | ||
cache_man: Mutex::new(cache_man), | ||
pending_best_ancient_block: RwLock::new(None), | ||
pending_best_block: RwLock::new(None), | ||
pending_block_hashes: RwLock::new(HashMap::new()), | ||
pending_block_details: RwLock::new(HashMap::new()), | ||
|
@@ -808,18 +810,7 @@ impl BlockChain { | |
}, is_best); | ||
|
||
if is_ancient { | ||
let mut best_ancient_block = self.best_ancient_block.write(); | ||
let ancient_number = best_ancient_block.as_ref().map_or(0, |b| b.number); | ||
if self.block_hash(block_number + 1).is_some() { | ||
batch.delete(db::COL_EXTRA, b"ancient"); | ||
*best_ancient_block = None; | ||
} else if block_number > ancient_number { | ||
batch.put(db::COL_EXTRA, b"ancient", &hash); | ||
*best_ancient_block = Some(BestAncientBlock { | ||
hash: hash, | ||
number: block_number, | ||
}); | ||
} | ||
self.set_best_ancient_block(block_number, &hash, batch); | ||
} | ||
|
||
false | ||
|
@@ -860,6 +851,84 @@ impl BlockChain { | |
} | ||
} | ||
|
||
/// Update the best ancient block to the given hash, after checking that | ||
/// it's directly linked to the currently known best ancient block | ||
pub fn update_best_ancient_block(&self, hash: &H256) { | ||
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 function is called only from a single place. Is it possible for hash to not be linked to any known ancient block? If yes, what does it mean? Should it be handled somehow? Currently the result of this function execution is unknown which makes it very difficult to debug 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. So this function will go from the given hash, and will ensure there is a link between the block at the given hash and the last know best ancient block. Thus, it only update the best ancient block if there is a link. |
||
// Get the block view of the next ancient block (it must | ||
// be in DB at this point) | ||
let block_view = match self.block(hash) { | ||
Some(v) => v, | ||
None => return, | ||
}; | ||
|
||
// So that `best_ancient_block` gets unlocked before calling | ||
// `set_best_ancient_block` | ||
{ | ||
// Get the target hash ; if there are no ancient block, | ||
// it means that the chain is already fully linked | ||
// Release the `best_ancient_block` RwLock | ||
let target_hash = { | ||
let best_ancient_block = self.best_ancient_block.read(); | ||
let cur_ancient_block = match *best_ancient_block { | ||
Some(ref b) => b, | ||
None => return, | ||
}; | ||
|
||
// Ensure that the new best ancient block is after the current one | ||
if block_view.number() <= cur_ancient_block.number { | ||
return; | ||
} | ||
|
||
cur_ancient_block.hash.clone() | ||
}; | ||
|
||
let mut block_hash = *hash; | ||
let mut is_linked = false; | ||
|
||
loop { | ||
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. We are holding Would be best to copy the block (or the data we need) and drop the lock as fast as possible. 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. Actually, it prevents the 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. It doesn't really. 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. Hm, right. So should I use a 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. I believe there is a logic error here (or in
it updates only
So there will be a race condition if this function is called between 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. ^^ @ngotchac is this addressed? 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. @tomusdrw Sorry, I saw your comment a bit late :/ I just updated the logic to update the local |
||
if block_hash == target_hash { | ||
is_linked = true; | ||
break; | ||
} | ||
|
||
match self.block_details(&block_hash) { | ||
Some(block_details) => { | ||
block_hash = block_details.parent; | ||
}, | ||
None => break, | ||
} | ||
} | ||
|
||
if !is_linked { | ||
trace!(target: "blockchain", "The given block {:x} is not linked to the known ancient block {:x}", hash, target_hash); | ||
return; | ||
} | ||
} | ||
|
||
let mut batch = self.db.key_value().transaction(); | ||
self.set_best_ancient_block(block_view.number(), hash, &mut batch); | ||
self.db.key_value().write(batch).expect("Low level database error."); | ||
} | ||
|
||
/// Set the best ancient block with the given value: private method | ||
/// `best_ancient_block` must not be locked, otherwise a DeadLock would occur | ||
fn set_best_ancient_block(&self, block_number: BlockNumber, block_hash: &H256, batch: &mut DBTransaction) { | ||
let mut pending_best_ancient_block = self.pending_best_ancient_block.write(); | ||
let ancient_number = self.best_ancient_block.read().as_ref().map_or(0, |b| b.number); | ||
if self.block_hash(block_number + 1).is_some() { | ||
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. I see that this line hasn't been changed, but I believe there is a logic error here. Subsequent calls to 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. I think it's fine because the block hash is written to DB before calling 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. Regarding the pending block_details, I'm not sure I get what you mean. The pending block details are written to DB, as well as the current block detail in |
||
trace!(target: "blockchain", "The two ends of the chain have met."); | ||
batch.delete(db::COL_EXTRA, b"ancient"); | ||
*pending_best_ancient_block = Some(None); | ||
} else if block_number > ancient_number { | ||
trace!(target: "blockchain", "Updating the best ancient block to {}.", block_number); | ||
batch.put(db::COL_EXTRA, b"ancient", &block_hash); | ||
*pending_best_ancient_block = Some(Some(BestAncientBlock { | ||
hash: *block_hash, | ||
number: block_number, | ||
})); | ||
} | ||
} | ||
|
||
/// Insert an epoch transition. Provide an epoch number being transitioned to | ||
/// and epoch transition object. | ||
/// | ||
|
@@ -1112,15 +1181,21 @@ impl BlockChain { | |
|
||
/// Apply pending insertion updates | ||
pub fn commit(&self) { | ||
let mut pending_best_ancient_block = self.pending_best_ancient_block.write(); | ||
let mut pending_best_block = self.pending_best_block.write(); | ||
let mut pending_write_hashes = self.pending_block_hashes.write(); | ||
let mut pending_block_details = self.pending_block_details.write(); | ||
let mut pending_write_txs = self.pending_transaction_addresses.write(); | ||
|
||
let mut best_ancient_block = self.best_ancient_block.write(); | ||
let mut best_block = self.best_block.write(); | ||
let mut write_block_details = self.block_details.write(); | ||
let mut write_hashes = self.block_hashes.write(); | ||
let mut write_txs = self.transaction_addresses.write(); | ||
// update best ancient block | ||
if let Some(block_option) = pending_best_ancient_block.take() { | ||
*best_ancient_block = block_option; | ||
} | ||
// update best block | ||
if let Some(block) = pending_best_block.take() { | ||
*best_block = block; | ||
|
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.
Same question as
kvdb-rocksdb