Produce block always on updated transaction pool state (#5227)

* make sure return ready iterator once state is updated

* update sc_basic_authorship tests

* update node tests

* fix manual seal

* actually fix service test

* add tests

* Update client/basic-authorship/src/basic_authorship.rs

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* helper function

* review suggestions

* warning and continue

* add debug log

* use futures::chennel::oneshot

* use declaration bound

* no option for updated_at

* no allocation

* ready_at / ready

* Update client/transaction-pool/src/lib.rs

Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com>

* Update client/transaction-pool/src/lib.rs

Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com>

* Update client/transaction-pool/src/lib.rs

Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com>

* Update client/transaction-pool/src/lib.rs

Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com>

* Update client/transaction-pool/src/lib.rs

Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com>

* Update client/transaction-pool/src/lib.rs

Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com>

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
Nikolay Volf
2020-03-17 08:24:04 -07:00
committed by GitHub
parent bbf5bc6acf
commit db86094b03
12 changed files with 257 additions and 25 deletions
+77 -6
View File
@@ -31,12 +31,12 @@ pub use sc_transaction_graph as txpool;
pub use crate::api::{FullChainApi, LightChainApi};
use std::{collections::HashMap, sync::Arc, pin::Pin};
use futures::{Future, FutureExt, future::ready};
use futures::{Future, FutureExt, future::ready, channel::oneshot};
use parking_lot::Mutex;
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, NumberFor, AtLeast32Bit, Extrinsic},
traits::{Block as BlockT, NumberFor, AtLeast32Bit, Extrinsic, Zero},
};
use sp_transaction_pool::{
TransactionPool, PoolStatus, ImportNotificationStream, TxHash, TransactionFor,
@@ -44,6 +44,12 @@ use sp_transaction_pool::{
};
use wasm_timer::Instant;
type BoxedReadyIterator<Hash, Data> = Box<dyn Iterator<Item=Arc<sc_transaction_graph::base_pool::Transaction<Hash, Data>>> + Send>;
type ReadyIteratorFor<PoolApi> = BoxedReadyIterator<sc_transaction_graph::ExHash<PoolApi>, sc_transaction_graph::ExtrinsicFor<PoolApi>>;
type PolledIterator<PoolApi> = Pin<Box<dyn Future<Output=ReadyIteratorFor<PoolApi>> + Send>>;
/// Basic implementation of transaction pool that can be customized by providing PoolApi.
pub struct BasicPool<PoolApi, Block>
where
@@ -54,6 +60,48 @@ pub struct BasicPool<PoolApi, Block>
api: Arc<PoolApi>,
revalidation_strategy: Arc<Mutex<RevalidationStrategy<NumberFor<Block>>>>,
revalidation_queue: Arc<revalidation::RevalidationQueue<PoolApi>>,
ready_poll: Arc<Mutex<ReadyPoll<ReadyIteratorFor<PoolApi>, Block>>>,
}
struct ReadyPoll<T, Block: BlockT> {
updated_at: NumberFor<Block>,
pollers: Vec<(NumberFor<Block>, oneshot::Sender<T>)>,
}
impl<T, Block: BlockT> Default for ReadyPoll<T, Block> {
fn default() -> Self {
Self {
updated_at: NumberFor::<Block>::zero(),
pollers: Default::default(),
}
}
}
impl<T, Block: BlockT> ReadyPoll<T, Block> {
fn trigger(&mut self, number: NumberFor<Block>, iterator_factory: impl Fn() -> T) {
self.updated_at = number;
let mut idx = 0;
while idx < self.pollers.len() {
if self.pollers[idx].0 <= number {
let poller_sender = self.pollers.swap_remove(idx);
log::debug!(target: "txpool", "Sending ready signal at block {}", number);
let _ = poller_sender.1.send(iterator_factory());
} else {
idx += 1;
}
}
}
fn add(&mut self, number: NumberFor<Block>) -> oneshot::Receiver<T> {
let (sender, receiver) = oneshot::channel();
self.pollers.push((number, sender));
receiver
}
fn updated_at(&self) -> NumberFor<Block> {
self.updated_at
}
}
#[cfg(not(target_os = "unknown"))]
@@ -128,6 +176,7 @@ impl<PoolApi, Block> BasicPool<PoolApi, Block>
RevalidationType::Full => RevalidationStrategy::Always,
}
)),
ready_poll: Default::default(),
},
background_task,
)
@@ -196,10 +245,6 @@ impl<PoolApi, Block> TransactionPool for BasicPool<PoolApi, Block>
self.pool.validated_pool().status()
}
fn ready(&self) -> Box<dyn Iterator<Item=Arc<Self::InPoolTransaction>>> {
Box::new(self.pool.validated_pool().ready())
}
fn import_notification_stream(&self) -> ImportNotificationStream<TxHash<Self>> {
self.pool.validated_pool().import_notification_stream()
}
@@ -215,6 +260,27 @@ impl<PoolApi, Block> TransactionPool for BasicPool<PoolApi, Block>
fn ready_transaction(&self, hash: &TxHash<Self>) -> Option<Arc<Self::InPoolTransaction>> {
self.pool.validated_pool().ready_by_hash(hash)
}
fn ready_at(&self, at: NumberFor<Self::Block>) -> PolledIterator<PoolApi> {
if self.ready_poll.lock().updated_at() >= at {
let iterator: ReadyIteratorFor<PoolApi> = Box::new(self.pool.validated_pool().ready());
return Box::pin(futures::future::ready(iterator));
}
Box::pin(
self.ready_poll
.lock()
.add(at)
.map(|received| received.unwrap_or_else(|e| {
log::warn!("Error receiving pending set: {:?}", e);
Box::new(vec![].into_iter())
}))
)
}
fn ready(&self) -> ReadyIteratorFor<PoolApi> {
Box::new(self.pool.validated_pool().ready())
}
}
#[cfg_attr(test, derive(Debug))]
@@ -329,6 +395,7 @@ impl<PoolApi, Block> MaintainedTransactionPool for BasicPool<PoolApi, Block>
let revalidation_strategy = self.revalidation_strategy.clone();
let retracted = retracted.clone();
let revalidation_queue = self.revalidation_queue.clone();
let ready_poll = self.ready_poll.clone();
async move {
// We don't query block if we won't prune anything
@@ -348,6 +415,10 @@ impl<PoolApi, Block> MaintainedTransactionPool for BasicPool<PoolApi, Block>
}
}
let extra_pool = pool.clone();
// After #5200 lands, this arguably might be moved to the handler of "all blocks notification".
ready_poll.lock().trigger(block_number, move || Box::new(extra_pool.validated_pool().ready()));
if next_action.resubmit {
let mut resubmit_transactions = Vec::new();
@@ -28,6 +28,7 @@ use substrate_test_runtime_client::{
};
use substrate_test_runtime_transaction_pool::{TestApi, uxt};
use crate::revalidation::BACKGROUND_REVALIDATION_INTERVAL;
use futures::task::Poll;
fn pool() -> Pool<TestApi> {
Pool::new(Default::default(), TestApi::with_alice_nonce(209).into())
@@ -600,5 +601,56 @@ fn fork_aware_finalization() {
assert_eq!(stream.next(), Some(TransactionStatus::Finalized(e1.clone())));
assert_eq!(stream.next(), None);
}
}
#[test]
fn ready_set_should_not_resolve_before_block_update() {
let (pool, _guard) = maintained_pool();
let xt1 = uxt(Alice, 209);
block_on(pool.submit_one(&BlockId::number(1), xt1.clone())).expect("1. Imported");
assert!(pool.ready_at(1).now_or_never().is_none());
}
#[test]
fn ready_set_should_resolve_after_block_update() {
let (pool, _guard) = maintained_pool();
pool.api.push_block(1, vec![]);
let xt1 = uxt(Alice, 209);
block_on(pool.submit_one(&BlockId::number(1), xt1.clone())).expect("1. Imported");
block_on(pool.maintain(block_event(1)));
assert!(pool.ready_at(1).now_or_never().is_some());
}
#[test]
fn ready_set_should_eventually_resolve_when_block_update_arrives() {
let (pool, _guard) = maintained_pool();
pool.api.push_block(1, vec![]);
let xt1 = uxt(Alice, 209);
block_on(pool.submit_one(&BlockId::number(1), xt1.clone())).expect("1. Imported");
let noop_waker = futures::task::noop_waker();
let mut context = futures::task::Context::from_waker(&noop_waker);
let mut ready_set_future = pool.ready_at(1);
if let Poll::Ready(_) = ready_set_future.poll_unpin(&mut context) {
panic!("Ready set should not be ready before block update!");
}
block_on(pool.maintain(block_event(1)));
match ready_set_future.poll_unpin(&mut context) {
Poll::Pending => {
panic!("Ready set should become ready after block update!");
},
Poll::Ready(iterator) => {
let data = iterator.collect::<Vec<_>>();
assert_eq!(data.len(), 1);
}
}
}