From b382c19abdb9985be1724c3b8cde83906da07d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= Date: Wed, 27 Jun 2018 12:21:40 +0100 Subject: [PATCH] ethcore: use LruCache for nonce cache Only clear the nonce cache when a block is retracted --- Cargo.lock | 1 + ethcore/private-tx/Cargo.toml | 1 + ethcore/private-tx/src/lib.rs | 10 +++++++--- ethcore/src/miner/miner.rs | 17 +++++++++++------ ethcore/src/miner/pool_client.rs | 24 +++++------------------- 5 files changed, 25 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1b156dbaff..7e66012d0e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -772,6 +772,7 @@ dependencies = [ "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "keccak-hash 0.1.2", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie 0.1.0", "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/private-tx/Cargo.toml b/ethcore/private-tx/Cargo.toml index 441c9882e29..e499cb98cad 100644 --- a/ethcore/private-tx/Cargo.toml +++ b/ethcore/private-tx/Cargo.toml @@ -24,6 +24,7 @@ fetch = { path = "../../util/fetch" } futures = "0.1" keccak-hash = { path = "../../util/hash" } log = "0.3" +lru-cache = "0.1" parking_lot = "0.5" patricia-trie = { path = "../../util/patricia_trie" } rand = "0.3" diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index 31abdb1eca5..5668d3e10e3 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -38,6 +38,7 @@ extern crate ethjson; extern crate fetch; extern crate futures; extern crate keccak_hash as hash; +extern crate lru_cache; extern crate parking_lot; extern crate patricia_trie as trie; extern crate rlp; @@ -69,6 +70,7 @@ use std::collections::{HashMap, HashSet}; use std::time::Duration; use ethereum_types::{H128, H256, U256, Address}; use hash::keccak; +use lru_cache::LruCache; use rlp::*; use parking_lot::{Mutex, RwLock}; use bytes::Bytes; @@ -94,6 +96,8 @@ use_contract!(private, "PrivateContract", "res/private.json"); /// Initialization vector length. const INIT_VEC_LEN: usize = 16; +const MAX_NONCE_CACHE_SIZE: usize = 4096; + /// Configurtion for private transaction provider #[derive(Default, PartialEq, Debug, Clone)] pub struct ProviderConfig { @@ -243,7 +247,7 @@ impl Provider where { Ok(original_transaction) } - fn pool_client<'a>(&'a self, nonce_cache: &'a RwLock>) -> miner::pool_client::PoolClient<'a, Client> { + fn pool_client<'a>(&'a self, nonce_cache: &'a RwLock>) -> miner::pool_client::PoolClient<'a, Client> { let engine = self.client.engine(); let refuse_service_transactions = true; miner::pool_client::PoolClient::new( @@ -262,7 +266,7 @@ impl Provider where { /// can be replaced with a single `drain()` method instead. /// Thanks to this we also don't really need to lock the entire verification for the time of execution. fn process_queue(&self) -> Result<(), Error> { - let nonce_cache = Default::default(); + let nonce_cache = RwLock::new(LruCache::new(MAX_NONCE_CACHE_SIZE)); let mut verification_queue = self.transactions_for_verification.lock(); let ready_transactions = verification_queue.ready_transactions(self.pool_client(&nonce_cache)); for transaction in ready_transactions { @@ -583,7 +587,7 @@ impl Importer for Arc { trace!("Validating transaction: {:?}", original_tx); // Verify with the first account available trace!("The following account will be used for verification: {:?}", validation_account); - let nonce_cache = Default::default(); + let nonce_cache = RwLock::new(LruCache::new(MAX_NONCE_CACHE_SIZE)); self.transactions_for_verification.lock().add_transaction( original_tx, contract, diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index af03b2d1e0a..3e45ae44bb3 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::time::{Instant, Duration}; -use std::collections::{BTreeMap, HashSet, HashMap}; +use std::collections::{BTreeMap, HashSet}; use std::sync::Arc; use ansi_term::Colour; @@ -26,6 +26,7 @@ use ethcore_miner::gas_pricer::GasPricer; use ethcore_miner::pool::{self, TransactionQueue, VerifiedTransaction, QueueStatus, PrioritizationStrategy}; use ethcore_miner::work_notify::NotifyWork; use ethereum_types::{H256, U256, Address}; +use lru_cache::LruCache; use parking_lot::{Mutex, RwLock}; use rayon::prelude::*; use transaction::{ @@ -95,6 +96,8 @@ const DEFAULT_MINIMAL_GAS_PRICE: u64 = 20_000_000_000; /// in case we have only a fraction of available block gas limit left. const MAX_SKIPPED_TRANSACTIONS: usize = 8; +const MAX_NONCE_CACHE_SIZE: usize = 4096; + /// Configures the behaviour of the miner. #[derive(Debug, PartialEq)] pub struct MinerOptions { @@ -200,7 +203,7 @@ pub struct Miner { sealing: Mutex, params: RwLock, listeners: RwLock>>, - nonce_cache: RwLock>, + nonce_cache: RwLock>, gas_pricer: Mutex, options: MinerOptions, // TODO [ToDr] Arc is only required because of price updater @@ -239,7 +242,7 @@ impl Miner { params: RwLock::new(AuthoringParams::default()), listeners: RwLock::new(vec![]), gas_pricer: Mutex::new(gas_pricer), - nonce_cache: RwLock::new(HashMap::with_capacity(1024)), + nonce_cache: RwLock::new(LruCache::new(MAX_NONCE_CACHE_SIZE)), options, transaction_queue: Arc::new(TransactionQueue::new(limits, verifier_options, tx_queue_strategy)), accounts, @@ -1064,13 +1067,15 @@ impl miner::MinerService for Miner { // 2. We ignore blocks that are `invalid` because it doesn't have any meaning in terms of the transactions that // are in those blocks - // Clear nonce cache - self.nonce_cache.write().clear(); - // First update gas limit in transaction queue and minimal gas price. let gas_limit = *chain.best_block_header().gas_limit(); self.update_transaction_queue_limits(gas_limit); + if retracted.len() != 0 { + // Clear nonce cache + self.nonce_cache.write().clear(); + } + // Then import all transactions... let client = self.pool_client(chain); { diff --git a/ethcore/src/miner/pool_client.rs b/ethcore/src/miner/pool_client.rs index bcf93d37532..de43eefa7be 100644 --- a/ethcore/src/miner/pool_client.rs +++ b/ethcore/src/miner/pool_client.rs @@ -17,11 +17,11 @@ //! Blockchain access for transaction pool. use std::fmt; -use std::collections::HashMap; -use ethereum_types::{H256, U256, Address}; -use ethcore_miner::pool; use ethcore_miner::pool::client::NonceClient; +use ethcore_miner::pool; +use ethereum_types::{H256, U256, Address}; +use lru_cache::LruCache; use transaction::{ self, UnverifiedTransaction, @@ -36,10 +36,7 @@ use header::Header; use miner; use miner::service_transaction_checker::ServiceTransactionChecker; -type NoncesCache = RwLock>; - -const MAX_NONCE_CACHE_SIZE: usize = 4096; -const EXPECTED_NONCE_CACHE_SIZE: usize = 2048; +type NoncesCache = RwLock>; /// Blockchain accesss for transaction pool. pub struct PoolClient<'a, C: 'a> { @@ -194,7 +191,7 @@ impl<'a, C: 'a> NonceClient for CachedNonceClient<'a, C> where C: Nonce + Sync, { fn account_nonce(&self, address: &Address) -> U256 { - if let Some(nonce) = self.cache.read().get(address) { + if let Some(nonce) = self.cache.write().get_mut(address) { return *nonce; } @@ -204,17 +201,6 @@ impl<'a, C: 'a> NonceClient for CachedNonceClient<'a, C> where let nonce = self.client.latest_nonce(address); cache.insert(*address, nonce); - if cache.len() < MAX_NONCE_CACHE_SIZE { - return nonce - } - - // Remove excessive amount of entries from the cache - while cache.len() > EXPECTED_NONCE_CACHE_SIZE { - // Just remove random entry - if let Some(key) = cache.keys().next().cloned() { - cache.remove(&key); - } - } nonce } }