Limit the number of transactions in pending set (#8777)

* Unordered iterator.

* Use unordered and limited set if full not required.

* Split timeout work into smaller timers.

* Avoid collecting all pending transactions when mining

* Remove println.

* Use priority ordering in eth-filter.

* Fix ethcore-miner tests and tx propagation.

* Review grumbles addressed.

* Add test for unordered not populating the cache.

* Fix ethcore tests.

* Fix light tests.

* Fix ethcore-sync tests.

* Fix RPC tests.
This commit is contained in:
Tomasz Drwięga
2018-06-12 08:22:54 +02:00
committed by Marek Kotewicz
parent 4817b94d0b
commit 4938d5dde5
29 changed files with 415 additions and 115 deletions

View File

@@ -15,7 +15,8 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use std::collections::{HashMap, BTreeSet};
use std::slice;
use std::collections::{hash_map, HashMap, BTreeSet};
use error;
use listener::{Listener, NoopListener};
@@ -416,7 +417,16 @@ impl<T, S, L> Pool<T, S, L> where
PendingIterator {
ready,
best_transactions,
pool: self
pool: self,
}
}
/// Returns unprioritized list of ready transactions.
pub fn unordered_pending<R: Ready<T>>(&self, ready: R) -> UnorderedIterator<T, R, S> {
UnorderedIterator {
ready,
senders: self.transactions.iter(),
transactions: None,
}
}
@@ -482,6 +492,50 @@ impl<T, S, L> Pool<T, S, L> where
}
}
/// An iterator over all pending (ready) transactions in unoredered fashion.
///
/// NOTE: Current implementation will iterate over all transactions from particular sender
/// ordered by nonce, but that might change in the future.
///
/// NOTE: the transactions are not removed from the queue.
/// You might remove them later by calling `cull`.
pub struct UnorderedIterator<'a, T, R, S> where
T: VerifiedTransaction + 'a,
S: Scoring<T> + 'a,
{
ready: R,
senders: hash_map::Iter<'a, T::Sender, Transactions<T, S>>,
transactions: Option<slice::Iter<'a, Transaction<T>>>,
}
impl<'a, T, R, S> Iterator for UnorderedIterator<'a, T, R, S> where
T: VerifiedTransaction,
R: Ready<T>,
S: Scoring<T>,
{
type Item = Arc<T>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(transactions) = self.transactions.as_mut() {
if let Some(tx) = transactions.next() {
match self.ready.is_ready(&tx) {
Readiness::Ready => {
return Some(tx.transaction.clone());
},
state => trace!("[{:?}] Ignoring {:?} transaction.", tx.hash(), state),
}
}
}
// otherwise fallback and try next sender
let next_sender = self.senders.next()?;
self.transactions = Some(next_sender.1.iter());
}
}
}
/// An iterator over all pending (ready) transactions.
/// NOTE: the transactions are not removed from the queue.
/// You might remove them later by calling `cull`.

View File

@@ -250,6 +250,66 @@ fn should_construct_pending() {
assert_eq!(pending.next(), None);
}
#[test]
fn should_return_unordered_iterator() {
// given
let b = TransactionBuilder::default();
let mut txq = TestPool::default();
let tx0 = txq.import(b.tx().nonce(0).gas_price(5).new()).unwrap();
let tx1 = txq.import(b.tx().nonce(1).gas_price(5).new()).unwrap();
let tx2 = txq.import(b.tx().nonce(2).new()).unwrap();
let tx3 = txq.import(b.tx().nonce(3).gas_price(4).new()).unwrap();
//gap
txq.import(b.tx().nonce(5).new()).unwrap();
let tx5 = txq.import(b.tx().sender(1).nonce(0).new()).unwrap();
let tx6 = txq.import(b.tx().sender(1).nonce(1).new()).unwrap();
let tx7 = txq.import(b.tx().sender(1).nonce(2).new()).unwrap();
let tx8 = txq.import(b.tx().sender(1).nonce(3).gas_price(4).new()).unwrap();
// gap
txq.import(b.tx().sender(1).nonce(5).new()).unwrap();
let tx9 = txq.import(b.tx().sender(2).nonce(0).new()).unwrap();
assert_eq!(txq.light_status().transaction_count, 11);
assert_eq!(txq.status(NonceReady::default()), Status {
stalled: 0,
pending: 9,
future: 2,
});
assert_eq!(txq.status(NonceReady::new(1)), Status {
stalled: 3,
pending: 6,
future: 2,
});
// when
let all: Vec<_> = txq.unordered_pending(NonceReady::default()).collect();
let chain1 = vec![tx0, tx1, tx2, tx3];
let chain2 = vec![tx5, tx6, tx7, tx8];
let chain3 = vec![tx9];
assert_eq!(all.len(), chain1.len() + chain2.len() + chain3.len());
let mut options = vec![
vec![chain1.clone(), chain2.clone(), chain3.clone()],
vec![chain2.clone(), chain1.clone(), chain3.clone()],
vec![chain2.clone(), chain3.clone(), chain1.clone()],
vec![chain3.clone(), chain2.clone(), chain1.clone()],
vec![chain3.clone(), chain1.clone(), chain2.clone()],
vec![chain1.clone(), chain3.clone(), chain2.clone()],
].into_iter().map(|mut v| {
let mut first = v.pop().unwrap();
for mut x in v {
first.append(&mut x);
}
first
});
assert!(options.any(|opt| all == opt));
}
#[test]
fn should_update_scoring_correctly() {
// given