From 0d110ed47c72c4c6b51f7219cc88efa0dfa48ec8 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 23 Mar 2017 04:00:22 +0100 Subject: [PATCH] apply pending changes to chain after DB commit --- ethcore/light/src/client/header_chain.rs | 29 ++++++++++++++++++++---- ethcore/light/src/client/mod.rs | 10 ++++---- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/ethcore/light/src/client/header_chain.rs b/ethcore/light/src/client/header_chain.rs index 022bae04e..8256595c3 100644 --- a/ethcore/light/src/client/header_chain.rs +++ b/ethcore/light/src/client/header_chain.rs @@ -126,6 +126,11 @@ fn era_key(number: u64) -> String { format!("candidates_{}", number) } +/// Pending changes from `insert` to be applied after the database write has finished. +pub struct PendingChanges { + best_block: Option, // new best block. +} + /// Header chain. See module docs for more details. pub struct HeaderChain { genesis_header: encoded::Header, // special-case the genesis. @@ -203,10 +208,15 @@ impl HeaderChain { /// Insert a pre-verified header. /// /// This blindly trusts that the data given to it is sensible. - pub fn insert(&self, transaction: &mut DBTransaction, header: Header) -> Result<(), BlockError> { + /// Returns a set of pending changes to be applied with `apply_pending` + /// before the next call to insert and after the transaction has been written. + pub fn insert(&self, transaction: &mut DBTransaction, header: Header) -> Result { let hash = header.hash(); let number = header.number(); let parent_hash = *header.parent_hash(); + let mut pending = PendingChanges { + best_block: None, + }; // hold candidates the whole time to guard import order. let mut candidates = self.candidates.write(); @@ -286,11 +296,11 @@ impl HeaderChain { } trace!(target: "chain", "New best block: ({}, {}), TD {}", number, hash, total_difficulty); - *self.best_block.write() = BlockDescriptor { + pending.best_block = Some(BlockDescriptor { hash: hash, number: number, total_difficulty: total_difficulty, - }; + }); // produce next CHT root if it's time. let earliest_era = *candidates.keys().next().expect("at least one era just created; qed"); @@ -334,7 +344,15 @@ impl HeaderChain { stream.append(&best_num).append(&latest_num); transaction.put(self.col, CURRENT_KEY, &stream.out()) } - Ok(()) + Ok(pending) + } + + /// Apply pending changes from a previous `insert` operation. + /// Must be done before the next `insert` call. + pub fn apply_pending(&self, pending: PendingChanges) { + if let Some(best_block) = pending.best_block { + *self.best_block.write() = best_block; + } } /// Get a block header. In the case of query by number, only canonical blocks @@ -360,6 +378,9 @@ impl HeaderChain { .and_then(load_from_db) } BlockId::Latest | BlockId::Pending => { + // hold candidates hear to prevent deletion of the header + // as we read it. + let _candidates = self.candidates.read(); let hash = { let best = self.best_block.read(); if best.number == 0 { diff --git a/ethcore/light/src/client/mod.rs b/ethcore/light/src/client/mod.rs index 92866da6d..d294053e1 100644 --- a/ethcore/light/src/client/mod.rs +++ b/ethcore/light/src/client/mod.rs @@ -223,18 +223,20 @@ impl Client { let (num, hash) = (verified_header.number(), verified_header.hash()); let mut tx = self.db.transaction(); - match self.chain.insert(&mut tx, verified_header) { - Ok(()) => { + let pending = match self.chain.insert(&mut tx, verified_header) { + Ok(pending) => { good.push(hash); self.report.write().blocks_imported += 1; + pending } Err(e) => { debug!(target: "client", "Error importing header {:?}: {}", (num, hash), e); bad.push(hash); + break; } - } + }; self.db.write_buffered(tx); - + self.chain.apply_pending(pending); if let Err(e) = self.db.flush() { panic!("Database flush failed: {}. Check disk health and space.", e); }