mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-02-11 15:35:07 -05:00
chore(task): move blocking pool to reth-tasks (#6929)
This commit is contained in:
@@ -18,7 +18,7 @@ reth-network-api.workspace = true
|
||||
reth-provider.workspace = true
|
||||
reth-rpc.workspace = true
|
||||
reth-rpc-api.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
reth-tasks = { workspace = true, features = ["rayon"] }
|
||||
reth-transaction-pool.workspace = true
|
||||
reth-node-api.workspace = true
|
||||
|
||||
|
||||
@@ -22,11 +22,11 @@ use reth_rpc::{
|
||||
cache::EthStateCache, gas_oracle::GasPriceOracle, EthFilterConfig, FeeHistoryCache,
|
||||
FeeHistoryCacheConfig,
|
||||
},
|
||||
AuthLayer, BlockingTaskPool, Claims, EngineEthApi, EthApi, EthFilter,
|
||||
EthSubscriptionIdProvider, JwtAuthValidator, JwtSecret,
|
||||
AuthLayer, Claims, EngineEthApi, EthApi, EthFilter, EthSubscriptionIdProvider,
|
||||
JwtAuthValidator, JwtSecret,
|
||||
};
|
||||
use reth_rpc_api::servers::*;
|
||||
use reth_tasks::TaskSpawner;
|
||||
use reth_tasks::{pool::BlockingTaskPool, TaskSpawner};
|
||||
use reth_transaction_pool::TransactionPool;
|
||||
use std::{
|
||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||
|
||||
@@ -7,8 +7,9 @@ use reth_rpc::{
|
||||
gas_oracle::GasPriceOracleConfig,
|
||||
EthFilterConfig, FeeHistoryCacheConfig, RPC_DEFAULT_GAS_CAP,
|
||||
},
|
||||
BlockingTaskPool, EthApi, EthFilter, EthPubSub,
|
||||
EthApi, EthFilter, EthPubSub,
|
||||
};
|
||||
use reth_tasks::pool::BlockingTaskPool;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// All handlers for the `eth` namespace
|
||||
|
||||
@@ -195,12 +195,15 @@ use reth_rpc::{
|
||||
gas_oracle::GasPriceOracle,
|
||||
EthBundle, FeeHistoryCache,
|
||||
},
|
||||
AdminApi, AuthLayer, BlockingTaskGuard, BlockingTaskPool, Claims, DebugApi, EngineEthApi,
|
||||
EthApi, EthFilter, EthPubSub, EthSubscriptionIdProvider, JwtAuthValidator, JwtSecret, NetApi,
|
||||
OtterscanApi, RPCApi, RethApi, TraceApi, TxPoolApi, Web3Api,
|
||||
AdminApi, AuthLayer, Claims, DebugApi, EngineEthApi, EthApi, EthFilter, EthPubSub,
|
||||
EthSubscriptionIdProvider, JwtAuthValidator, JwtSecret, NetApi, OtterscanApi, RPCApi, RethApi,
|
||||
TraceApi, TxPoolApi, Web3Api,
|
||||
};
|
||||
use reth_rpc_api::servers::*;
|
||||
use reth_tasks::{TaskSpawner, TokioTaskExecutor};
|
||||
use reth_tasks::{
|
||||
pool::{BlockingTaskGuard, BlockingTaskPool},
|
||||
TaskSpawner, TokioTaskExecutor,
|
||||
};
|
||||
use reth_transaction_pool::{noop::NoopTransactionPool, TransactionPool};
|
||||
// re-export for convenience
|
||||
pub use crate::eth::{EthConfig, EthHandlers};
|
||||
|
||||
@@ -23,7 +23,7 @@ reth-network-api.workspace = true
|
||||
reth-network.workspace = true
|
||||
reth-rpc-engine-api.workspace = true
|
||||
reth-revm = { workspace = true, features = ["js-tracer"] }
|
||||
reth-tasks.workspace = true
|
||||
reth-tasks = { workspace = true, features = ["rayon"] }
|
||||
reth-consensus-common.workspace = true
|
||||
reth-rpc-types-compat.workspace = true
|
||||
revm-inspectors.workspace = true
|
||||
@@ -59,7 +59,6 @@ tokio = { workspace = true, features = ["sync"] }
|
||||
tower = "0.4"
|
||||
tokio-stream = { workspace = true, features = ["sync"] }
|
||||
pin-project.workspace = true
|
||||
rayon.workspace = true
|
||||
|
||||
# metrics
|
||||
reth-metrics.workspace = true
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
//! Additional helpers for executing tracing calls
|
||||
|
||||
use std::{
|
||||
future::Future,
|
||||
panic::{catch_unwind, AssertUnwindSafe},
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{ready, Context, Poll},
|
||||
thread,
|
||||
};
|
||||
use tokio::sync::{oneshot, AcquireError, OwnedSemaphorePermit, Semaphore};
|
||||
|
||||
/// RPC Tracing call guard semaphore.
|
||||
///
|
||||
/// This is used to restrict the number of concurrent RPC requests to tracing methods like
|
||||
/// `debug_traceTransaction` as well as `eth_getProof` because they can consume a lot of
|
||||
/// memory and CPU.
|
||||
///
|
||||
/// This types serves as an entry guard for the [BlockingTaskPool] and is used to rate limit
|
||||
/// parallel blocking tasks in the pool.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BlockingTaskGuard(Arc<Semaphore>);
|
||||
|
||||
impl BlockingTaskGuard {
|
||||
/// Create a new `BlockingTaskGuard` with the given maximum number of blocking tasks in
|
||||
/// parallel.
|
||||
pub fn new(max_blocking_tasks: u32) -> Self {
|
||||
Self(Arc::new(Semaphore::new(max_blocking_tasks as usize)))
|
||||
}
|
||||
|
||||
/// See also [Semaphore::acquire_owned]
|
||||
pub async fn acquire_owned(self) -> Result<OwnedSemaphorePermit, AcquireError> {
|
||||
self.0.acquire_owned().await
|
||||
}
|
||||
|
||||
/// See also [Semaphore::acquire_many_owned]
|
||||
pub async fn acquire_many_owned(self, n: u32) -> Result<OwnedSemaphorePermit, AcquireError> {
|
||||
self.0.acquire_many_owned(n).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to execute blocking tasks on a rayon threadpool from within a tokio runtime.
|
||||
///
|
||||
/// This is a dedicated threadpool for blocking tasks which are CPU bound.
|
||||
/// RPC calls that perform blocking IO (disk lookups) are not executed on this pool but on the tokio
|
||||
/// runtime's blocking pool, which performs poorly with CPU bound tasks. Once the tokio blocking
|
||||
/// pool is saturated it is converted into a queue, blocking tasks could then interfere with the
|
||||
/// queue and block other RPC calls.
|
||||
///
|
||||
/// See also [tokio-docs] for more information.
|
||||
///
|
||||
/// [tokio-docs]: https://docs.rs/tokio/latest/tokio/index.html#cpu-bound-tasks-and-blocking-code
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BlockingTaskPool {
|
||||
pool: Arc<rayon::ThreadPool>,
|
||||
}
|
||||
|
||||
impl BlockingTaskPool {
|
||||
/// Create a new `BlockingTaskPool` with the given threadpool.
|
||||
pub fn new(pool: rayon::ThreadPool) -> Self {
|
||||
Self { pool: Arc::new(pool) }
|
||||
}
|
||||
|
||||
/// Convenience function to start building a new threadpool.
|
||||
pub fn builder() -> rayon::ThreadPoolBuilder {
|
||||
rayon::ThreadPoolBuilder::new()
|
||||
}
|
||||
|
||||
/// Convenience function to build a new threadpool with the default configuration.
|
||||
///
|
||||
/// Uses [`rayon::ThreadPoolBuilder::build`](rayon::ThreadPoolBuilder::build) defaults but
|
||||
/// increases the stack size to 8MB.
|
||||
pub fn build() -> Result<Self, rayon::ThreadPoolBuildError> {
|
||||
Self::builder().build().map(Self::new)
|
||||
}
|
||||
|
||||
/// Asynchronous wrapper around Rayon's
|
||||
/// [`ThreadPool::spawn`](rayon::ThreadPool::spawn).
|
||||
///
|
||||
/// Runs a function on the configured threadpool, returning a future that resolves with the
|
||||
/// function's return value.
|
||||
///
|
||||
/// If the function panics, the future will resolve to an error.
|
||||
pub fn spawn<F, R>(&self, func: F) -> BlockingTaskHandle<R>
|
||||
where
|
||||
F: FnOnce() -> R + Send + 'static,
|
||||
R: Send + 'static,
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
self.pool.spawn(move || {
|
||||
let _result = tx.send(catch_unwind(AssertUnwindSafe(func)));
|
||||
});
|
||||
|
||||
BlockingTaskHandle { rx }
|
||||
}
|
||||
|
||||
/// Asynchronous wrapper around Rayon's
|
||||
/// [`ThreadPool::spawn_fifo`](rayon::ThreadPool::spawn_fifo).
|
||||
///
|
||||
/// Runs a function on the configured threadpool, returning a future that resolves with the
|
||||
/// function's return value.
|
||||
///
|
||||
/// If the function panics, the future will resolve to an error.
|
||||
pub fn spawn_fifo<F, R>(&self, func: F) -> BlockingTaskHandle<R>
|
||||
where
|
||||
F: FnOnce() -> R + Send + 'static,
|
||||
R: Send + 'static,
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
self.pool.spawn_fifo(move || {
|
||||
let _result = tx.send(catch_unwind(AssertUnwindSafe(func)));
|
||||
});
|
||||
|
||||
BlockingTaskHandle { rx }
|
||||
}
|
||||
}
|
||||
|
||||
/// Async handle for a blocking task running in a Rayon thread pool.
|
||||
///
|
||||
/// ## Panics
|
||||
///
|
||||
/// If polled from outside a tokio runtime.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
#[pin_project::pin_project]
|
||||
pub struct BlockingTaskHandle<T> {
|
||||
#[pin]
|
||||
pub(crate) rx: oneshot::Receiver<thread::Result<T>>,
|
||||
}
|
||||
|
||||
impl<T> Future for BlockingTaskHandle<T> {
|
||||
type Output = thread::Result<T>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
match ready!(self.project().rx.poll(cx)) {
|
||||
Ok(res) => Poll::Ready(res),
|
||||
Err(_) => Poll::Ready(Err(Box::<TokioBlockingTaskError>::default())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error returned when the Tokio channel is dropped while awaiting a result.
|
||||
///
|
||||
/// This should only happen
|
||||
#[derive(Debug, Default, thiserror::Error)]
|
||||
#[error("tokio channel dropped while awaiting result")]
|
||||
#[non_exhaustive]
|
||||
pub struct TokioBlockingTaskError;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn blocking_pool() {
|
||||
let pool = BlockingTaskPool::build().unwrap();
|
||||
let res = pool.spawn(move || 5);
|
||||
let res = res.await.unwrap();
|
||||
assert_eq!(res, 5);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn blocking_pool_panic() {
|
||||
let pool = BlockingTaskPool::build().unwrap();
|
||||
let res = pool.spawn(move || -> i32 {
|
||||
panic!();
|
||||
});
|
||||
let res = res.await;
|
||||
assert!(res.is_err());
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ use crate::{
|
||||
EthTransactions,
|
||||
},
|
||||
result::{internal_rpc_err, ToRpcResult},
|
||||
BlockingTaskGuard, EthApiSpec,
|
||||
EthApiSpec,
|
||||
};
|
||||
use alloy_rlp::{Decodable, Encodable};
|
||||
use async_trait::async_trait;
|
||||
@@ -29,6 +29,7 @@ use reth_rpc_types::{
|
||||
},
|
||||
BlockError, Bundle, RichBlock, StateContext, TransactionRequest,
|
||||
};
|
||||
use reth_tasks::pool::BlockingTaskGuard;
|
||||
use revm::{
|
||||
db::CacheDB,
|
||||
primitives::{db::DatabaseCommit, BlockEnv, CfgEnvWithHandlerCfg, Env, EnvWithHandlerCfg},
|
||||
|
||||
@@ -21,12 +21,11 @@ use reth_primitives::{
|
||||
Address, BlockId, BlockNumberOrTag, ChainInfo, SealedBlockWithSenders, SealedHeader, B256,
|
||||
U256, U64,
|
||||
};
|
||||
|
||||
use reth_provider::{
|
||||
BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, StateProviderBox, StateProviderFactory,
|
||||
};
|
||||
use reth_rpc_types::{SyncInfo, SyncStatus};
|
||||
use reth_tasks::{TaskSpawner, TokioTaskExecutor};
|
||||
use reth_tasks::{pool::BlockingTaskPool, TaskSpawner, TokioTaskExecutor};
|
||||
use reth_transaction_pool::TransactionPool;
|
||||
use revm_primitives::{CfgEnv, SpecId};
|
||||
use std::{
|
||||
@@ -35,7 +34,6 @@ use std::{
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use tokio::sync::{oneshot, Mutex};
|
||||
|
||||
mod block;
|
||||
@@ -50,7 +48,6 @@ mod sign;
|
||||
mod state;
|
||||
mod transactions;
|
||||
|
||||
use crate::BlockingTaskPool;
|
||||
pub use transactions::{EthTransactions, TransactionSource};
|
||||
|
||||
/// `Eth` API trait.
|
||||
|
||||
@@ -437,7 +437,7 @@ mod tests {
|
||||
cache::EthStateCache, gas_oracle::GasPriceOracle, FeeHistoryCache,
|
||||
FeeHistoryCacheConfig,
|
||||
},
|
||||
BlockingTaskPool, EthApi,
|
||||
EthApi,
|
||||
};
|
||||
use jsonrpsee::types::error::INVALID_PARAMS_CODE;
|
||||
use reth_interfaces::test_utils::{generators, generators::Rng};
|
||||
@@ -453,6 +453,7 @@ mod tests {
|
||||
};
|
||||
use reth_rpc_api::EthApiServer;
|
||||
use reth_rpc_types::FeeHistory;
|
||||
use reth_tasks::pool::BlockingTaskPool;
|
||||
use reth_transaction_pool::test_utils::{testing_pool, TestPool};
|
||||
|
||||
fn build_test_eth_api<
|
||||
|
||||
@@ -118,16 +118,13 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
eth::{
|
||||
cache::EthStateCache, gas_oracle::GasPriceOracle, FeeHistoryCache,
|
||||
FeeHistoryCacheConfig,
|
||||
},
|
||||
BlockingTaskPool,
|
||||
use crate::eth::{
|
||||
cache::EthStateCache, gas_oracle::GasPriceOracle, FeeHistoryCache, FeeHistoryCacheConfig,
|
||||
};
|
||||
use reth_node_ethereum::EthEvmConfig;
|
||||
use reth_primitives::{constants::ETHEREUM_BLOCK_GAS_LIMIT, StorageKey, StorageValue};
|
||||
use reth_provider::test_utils::{ExtendedAccount, MockEthProvider, NoopProvider};
|
||||
use reth_tasks::pool::BlockingTaskPool;
|
||||
use reth_transaction_pool::test_utils::testing_pool;
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ pub(crate) type StateCacheDB = CacheDB<StateProviderDatabase<StateProviderBox>>;
|
||||
/// Commonly used transaction related functions for the [EthApi] type in the `eth_` namespace.
|
||||
///
|
||||
/// Async functions that are spawned onto the
|
||||
/// [BlockingTaskPool](crate::blocking_pool::BlockingTaskPool) begin with `spawn_`
|
||||
/// [BlockingTaskPool](reth_tasks::pool::BlockingTaskPool) begin with `spawn_`
|
||||
///
|
||||
///
|
||||
/// ## Calls
|
||||
@@ -276,7 +276,7 @@ pub trait EthTransactions: Send + Sync {
|
||||
/// the database that points to the beginning of the transaction.
|
||||
///
|
||||
/// Note: Implementers should use a threadpool where blocking is allowed, such as
|
||||
/// [BlockingTaskPool](crate::blocking_pool::BlockingTaskPool).
|
||||
/// [BlockingTaskPool](reth_tasks::pool::BlockingTaskPool).
|
||||
async fn spawn_trace_transaction_in_block<F, R>(
|
||||
&self,
|
||||
hash: B256,
|
||||
@@ -1499,17 +1499,14 @@ pub(crate) fn build_transaction_receipt_with_block_receipts(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
eth::{
|
||||
cache::EthStateCache, gas_oracle::GasPriceOracle, FeeHistoryCache,
|
||||
FeeHistoryCacheConfig,
|
||||
},
|
||||
BlockingTaskPool,
|
||||
use crate::eth::{
|
||||
cache::EthStateCache, gas_oracle::GasPriceOracle, FeeHistoryCache, FeeHistoryCacheConfig,
|
||||
};
|
||||
use reth_network_api::noop::NoopNetwork;
|
||||
use reth_node_ethereum::EthEvmConfig;
|
||||
use reth_primitives::{constants::ETHEREUM_BLOCK_GAS_LIMIT, hex_literal::hex};
|
||||
use reth_provider::test_utils::NoopProvider;
|
||||
use reth_tasks::pool::BlockingTaskPool;
|
||||
use reth_transaction_pool::test_utils::testing_pool;
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
//! `Eth` bundle implementation and helpers.
|
||||
|
||||
use crate::{
|
||||
eth::{
|
||||
error::{EthApiError, EthResult, RpcInvalidTransactionError},
|
||||
revm_utils::FillableTransaction,
|
||||
utils::recover_raw_transaction,
|
||||
EthTransactions,
|
||||
},
|
||||
BlockingTaskGuard,
|
||||
use crate::eth::{
|
||||
error::{EthApiError, EthResult, RpcInvalidTransactionError},
|
||||
revm_utils::FillableTransaction,
|
||||
utils::recover_raw_transaction,
|
||||
EthTransactions,
|
||||
};
|
||||
use jsonrpsee::core::RpcResult;
|
||||
use reth_primitives::{
|
||||
@@ -18,6 +15,7 @@ use reth_primitives::{
|
||||
use reth_revm::database::StateProviderDatabase;
|
||||
use reth_rpc_api::EthCallBundleApiServer;
|
||||
use reth_rpc_types::{EthCallBundle, EthCallBundleResponse, EthCallBundleTransactionResult};
|
||||
use reth_tasks::pool::BlockingTaskGuard;
|
||||
use revm::{
|
||||
db::CacheDB,
|
||||
primitives::{ResultAndState, TxEnv},
|
||||
|
||||
@@ -37,7 +37,6 @@ mod trace;
|
||||
mod txpool;
|
||||
mod web3;
|
||||
pub use admin::AdminApi;
|
||||
pub use blocking_pool::{BlockingTaskGuard, BlockingTaskPool};
|
||||
pub use debug::DebugApi;
|
||||
pub use engine::{EngineApi, EngineEthApi};
|
||||
pub use eth::{EthApi, EthApiSpec, EthFilter, EthPubSub, EthSubscriptionIdProvider};
|
||||
@@ -49,5 +48,4 @@ pub use rpc::RPCApi;
|
||||
pub use trace::TraceApi;
|
||||
pub use txpool::TxPoolApi;
|
||||
pub use web3::Web3Api;
|
||||
pub mod blocking_pool;
|
||||
pub mod result;
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
use crate::{
|
||||
eth::{
|
||||
error::{EthApiError, EthResult},
|
||||
revm_utils::{inspect, inspect_and_return_db, prepare_call_env, EvmOverrides},
|
||||
utils::recover_raw_transaction,
|
||||
EthTransactions,
|
||||
},
|
||||
BlockingTaskGuard,
|
||||
use crate::eth::{
|
||||
error::{EthApiError, EthResult},
|
||||
revm_utils::{inspect, inspect_and_return_db, prepare_call_env, EvmOverrides},
|
||||
utils::recover_raw_transaction,
|
||||
EthTransactions,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use jsonrpsee::core::RpcResult as Result;
|
||||
@@ -24,6 +21,7 @@ use reth_rpc_types::{
|
||||
trace::{filter::TraceFilter, parity::*, tracerequest::TraceCallRequest},
|
||||
BlockError, BlockOverrides, Index, TransactionRequest,
|
||||
};
|
||||
use reth_tasks::pool::BlockingTaskGuard;
|
||||
use revm::{
|
||||
db::{CacheDB, DatabaseCommit},
|
||||
primitives::EnvWithHandlerCfg,
|
||||
|
||||
Reference in New Issue
Block a user