Remove the service, replacing it with a struct of individual chain components (#6352)

* WIP

* Making progress

* Almost ready

* Get service tests compiling

* Fix node screenshot

* Line widths

* Fix node cli tests

* Fix node cli warning

* ChainComponents -> ServiceComponents, fix tests

* make spawn_handle public

* Remove spawnnamed impl for taskmanager

* Move the keep alive stuff to the task manager

* Move the telemetry, base path, rpc keep_alive to the service builder

* Make the task manager keep alive an internal detail

* Rewrite the browser start_client future

* Remove run_node etc

* Revert my personal changes to browser-demo/build.sh

* use |config|

* Add a runtime_version function to SubstrateCli

* Reexport role and runtime version from sc cli

* Update Cargo.lock

* runtime_version -> native_runtime_version

* Pass chain spec to native_runtime_version for polkadot

* Fix line widths

* Traitify ServiceComponents Client
This commit is contained in:
Ashley
2020-06-30 12:00:42 +02:00
committed by GitHub
parent 493d5d8591
commit b832e35c5e
19 changed files with 646 additions and 782 deletions
+65 -104
View File
@@ -17,21 +17,20 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::{
Service, NetworkStatus, NetworkState, error::Error, DEFAULT_PROTOCOL_ID, MallocSizeOfWasm,
NetworkStatus, NetworkState, error::Error, DEFAULT_PROTOCOL_ID, MallocSizeOfWasm,
start_rpc_servers, build_network_future, TransactionPoolAdapter, TaskManager, SpawnTaskHandle,
status_sinks, metrics::MetricsService,
client::{light, Client, ClientConfig},
config::{Configuration, KeystoreConfig, PrometheusConfig, OffchainWorkerConfig},
};
use sc_client_api::{
self, light::RemoteBlockchain, execution_extensions::ExtensionsFactory,
ExecutorProvider, CallExecutor, ForkBlocks, BadBlocks, CloneableSpawn, UsageProvider,
backend::RemoteBackend,
self, light::RemoteBlockchain, execution_extensions::ExtensionsFactory, ExecutorProvider,
ForkBlocks, BadBlocks, CloneableSpawn, UsageProvider, backend::RemoteBackend,
};
use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedSender, TracingUnboundedReceiver};
use sc_chain_spec::get_extension;
use sp_consensus::{
block_validation::{BlockAnnounceValidator, DefaultBlockAnnounceValidator},
block_validation::{BlockAnnounceValidator, DefaultBlockAnnounceValidator, Chain},
import_queue::ImportQueue,
};
use futures::{
@@ -46,9 +45,9 @@ use sc_network::NetworkService;
use parking_lot::{Mutex, RwLock};
use sp_runtime::generic::BlockId;
use sp_runtime::traits::{
Block as BlockT, NumberFor, SaturatedConversion, HashFor, Zero,
Block as BlockT, NumberFor, SaturatedConversion, HashFor, Zero, BlockIdTo,
};
use sp_api::ProvideRuntimeApi;
use sp_api::{ProvideRuntimeApi, CallApiAt};
use sc_executor::{NativeExecutor, NativeExecutionDispatch, RuntimeInfo};
use std::{
collections::HashMap,
@@ -62,8 +61,15 @@ use prometheus_endpoint::Registry;
use sc_client_db::{Backend, DatabaseSettings};
use sp_core::traits::CodeExecutor;
use sp_runtime::BuildStorage;
use sc_client_api::execution_extensions::ExecutionExtensions;
use sc_client_api::{
BlockBackend, BlockchainEvents,
backend::StorageProvider,
proof_provider::ProofProvider,
execution_extensions::ExecutionExtensions
};
use sp_core::storage::Storage;
use sp_blockchain::{HeaderMetadata, HeaderBackend};
use crate::{ServiceComponents, TelemetryOnConnectSinks, RpcHandlers, NetworkStatusSinks};
pub type BackgroundTask = Pin<Box<dyn Future<Output=()> + Send>>;
@@ -878,11 +884,11 @@ pub trait ServiceBuilderCommand {
) -> Result<Storage, Error>;
}
impl<TBl, TRtApi, TBackend, TExec, TSc, TImpQu, TExPool, TRpc>
impl<TBl, TRtApi, TBackend, TSc, TImpQu, TExPool, TRpc, TCl>
ServiceBuilder<
TBl,
TRtApi,
Client<TBackend, TExec, TBl, TRtApi>,
TCl,
Arc<OnDemand<TBl>>,
TSc,
TImpQu,
@@ -892,8 +898,12 @@ ServiceBuilder<
TRpc,
TBackend,
> where
Client<TBackend, TExec, TBl, TRtApi>: ProvideRuntimeApi<TBl>,
<Client<TBackend, TExec, TBl, TRtApi> as ProvideRuntimeApi<TBl>>::Api:
TCl: ProvideRuntimeApi<TBl> + HeaderMetadata<TBl, Error=sp_blockchain::Error> + Chain<TBl> +
BlockBackend<TBl> + BlockIdTo<TBl, Error=sp_blockchain::Error> + ProofProvider<TBl> +
HeaderBackend<TBl> + BlockchainEvents<TBl> + ExecutorProvider<TBl> + UsageProvider<TBl> +
StorageProvider<TBl, TBackend> + CallApiAt<TBl, Error=sp_blockchain::Error> +
Send + 'static,
<TCl as ProvideRuntimeApi<TBl>>::Api:
sp_api::Metadata<TBl> +
sc_offchain::OffchainWorkerApi<TBl> +
sp_transaction_pool::runtime_api::TaggedTransactionQueue<TBl> +
@@ -903,7 +913,6 @@ ServiceBuilder<
TBl: BlockT,
TRtApi: 'static + Send + Sync,
TBackend: 'static + sc_client_api::backend::Backend<TBl> + Send,
TExec: 'static + CallExecutor<TBl> + Send + Sync + Clone,
TSc: Clone,
TImpQu: 'static + ImportQueue<TBl>,
TExPool: MaintainedTransactionPool<Block=TBl, Hash = <TBl as BlockT>::Hash> + MallocSizeOfWasm + 'static,
@@ -916,26 +925,12 @@ ServiceBuilder<
Ok(self)
}
fn build_common(self) -> Result<Service<
TBl,
Client<TBackend, TExec, TBl, TRtApi>,
TSc,
NetworkStatus<TBl>,
NetworkService<TBl, <TBl as BlockT>::Hash>,
TExPool,
sc_offchain::OffchainWorkers<
Client<TBackend, TExec, TBl, TRtApi>,
TBackend::OffchainStorage,
TBl
>,
>, Error>
where TExec: CallExecutor<TBl, Backend = TBackend>,
{
fn build_common(self) -> Result<ServiceComponents<TBl, TBackend, TSc, TExPool, TCl>, Error> {
let ServiceBuilder {
marker: _,
mut config,
client,
task_manager,
mut task_manager,
fetcher: on_demand,
backend,
keystore,
@@ -949,17 +944,14 @@ ServiceBuilder<
block_announce_validator_builder,
} = self;
let chain_info = client.usage_info().chain;
sp_session::generate_initial_session_keys(
client.clone(),
&BlockId::Hash(client.chain_info().best_hash),
&BlockId::Hash(chain_info.best_hash),
config.dev_key_seed.clone().map(|s| vec![s]).unwrap_or_default(),
)?;
// A side-channel for essential tasks to communicate shutdown.
let (essential_failed_tx, essential_failed_rx) = tracing_unbounded("mpsc_essential_tasks");
let chain_info = client.chain_info();
info!("📦 Highest known block at #{}", chain_info.best_number);
telemetry!(
SUBSTRATE_INFO;
@@ -968,15 +960,16 @@ ServiceBuilder<
"best" => ?chain_info.best_hash
);
let spawn_handle = task_manager.spawn_handle();
let (system_rpc_tx, system_rpc_rx) = tracing_unbounded("mpsc_system_rpc");
let (network, network_status_sinks, network_future) = build_network(
&config, client.clone(), transaction_pool.clone(), Clone::clone(&spawn_handle), on_demand.clone(),
block_announce_validator_builder, finality_proof_request_builder, finality_proof_provider,
system_rpc_rx, import_queue
&config, client.clone(), transaction_pool.clone(), task_manager.spawn_handle(),
on_demand.clone(), block_announce_validator_builder, finality_proof_request_builder,
finality_proof_provider, system_rpc_rx, import_queue
)?;
let spawn_handle = task_manager.spawn_handle();
// The network worker is responsible for gathering all network messages and processing
// them. This is quite a heavy task, and at the time of the writing of this comment it
// frequently happens that this future takes several seconds or in some situations
@@ -1064,7 +1057,7 @@ ServiceBuilder<
);
let rpc = start_rpc_servers(&config, gen_handler)?;
// This is used internally, so don't restrict access to unsafe RPC
let rpc_handlers = gen_handler(sc_rpc::DenyUnsafe::No);
let rpc_handlers = Arc::new(RpcHandlers(gen_handler(sc_rpc::DenyUnsafe::No)));
let telemetry_connection_sinks: Arc<Mutex<Vec<TracingUnboundedSender<()>>>> = Default::default();
@@ -1110,52 +1103,34 @@ ServiceBuilder<
config.informant_output_format,
));
Ok(Service {
task_manager.keep_alive((telemetry, config.base_path, rpc, rpc_handlers.clone()));
Ok(ServiceComponents {
client,
task_manager,
network,
network_status_sinks,
select_chain,
transaction_pool,
essential_failed_tx,
essential_failed_rx,
rpc_handlers,
_rpc: rpc,
_telemetry: telemetry,
_offchain_workers: offchain_workers,
_telemetry_on_connect_sinks: telemetry_connection_sinks.clone(),
keystore,
marker: PhantomData::<TBl>,
offchain_workers,
telemetry_on_connect_sinks: TelemetryOnConnectSinks(telemetry_connection_sinks),
network_status_sinks: NetworkStatusSinks::new(network_status_sinks),
prometheus_registry: config.prometheus_config.map(|config| config.registry),
_base_path: config.base_path.map(Arc::new),
})
}
/// Builds the light service.
pub fn build_light(self) -> Result<Service<
TBl,
Client<TBackend, TExec, TBl, TRtApi>,
TSc,
NetworkStatus<TBl>,
NetworkService<TBl, <TBl as BlockT>::Hash>,
TExPool,
sc_offchain::OffchainWorkers<
Client<TBackend, TExec, TBl, TRtApi>,
TBackend::OffchainStorage,
TBl
>,
>, Error>
where TExec: CallExecutor<TBl, Backend = TBackend>,
{
pub fn build_light(self) -> Result<ServiceComponents<TBl, TBackend, TSc, TExPool, TCl>, Error> {
self.build_common()
}
}
impl<TBl, TRtApi, TBackend, TExec, TSc, TImpQu, TExPool, TRpc>
impl<TBl, TRtApi, TBackend, TSc, TImpQu, TExPool, TRpc, TCl>
ServiceBuilder<
TBl,
TRtApi,
Client<TBackend, TExec, TBl, TRtApi>,
TCl,
Arc<OnDemand<TBl>>,
TSc,
TImpQu,
@@ -1165,8 +1140,12 @@ ServiceBuilder<
TRpc,
TBackend,
> where
Client<TBackend, TExec, TBl, TRtApi>: ProvideRuntimeApi<TBl>,
<Client<TBackend, TExec, TBl, TRtApi> as ProvideRuntimeApi<TBl>>::Api:
TCl: ProvideRuntimeApi<TBl> + HeaderMetadata<TBl, Error=sp_blockchain::Error> + Chain<TBl> +
BlockBackend<TBl> + BlockIdTo<TBl, Error=sp_blockchain::Error> + ProofProvider<TBl> +
HeaderBackend<TBl> + BlockchainEvents<TBl> + ExecutorProvider<TBl> + UsageProvider<TBl> +
StorageProvider<TBl, TBackend> + CallApiAt<TBl, Error=sp_blockchain::Error> +
Send + 'static,
<TCl as ProvideRuntimeApi<TBl>>::Api:
sp_api::Metadata<TBl> +
sc_offchain::OffchainWorkerApi<TBl> +
sp_transaction_pool::runtime_api::TaggedTransactionQueue<TBl> +
@@ -1176,7 +1155,6 @@ ServiceBuilder<
TBl: BlockT,
TRtApi: 'static + Send + Sync,
TBackend: 'static + sc_client_api::backend::Backend<TBl> + Send,
TExec: 'static + CallExecutor<TBl> + Send + Sync + Clone,
TSc: Clone,
TImpQu: 'static + ImportQueue<TBl>,
TExPool: MaintainedTransactionPool<Block = TBl, Hash = <TBl as BlockT>::Hash> +
@@ -1187,21 +1165,7 @@ ServiceBuilder<
{
/// Builds the full service.
pub fn build_full(self) -> Result<Service<
TBl,
Client<TBackend, TExec, TBl, TRtApi>,
TSc,
NetworkStatus<TBl>,
NetworkService<TBl, <TBl as BlockT>::Hash>,
TExPool,
sc_offchain::OffchainWorkers<
Client<TBackend, TExec, TBl, TRtApi>,
TBackend::OffchainStorage,
TBl
>,
>, Error>
where TExec: CallExecutor<TBl, Backend = TBackend>,
{
pub fn build_full(self) -> Result<ServiceComponents<TBl, TBackend, TSc, TExPool, TCl>, Error> {
// make transaction pool available for off-chain runtime calls.
self.client.execution_extensions()
.register_transaction_pool(Arc::downgrade(&self.transaction_pool) as _);
@@ -1233,18 +1197,16 @@ async fn transaction_notifications<TBl, TExPool>(
}
// Periodically notify the telemetry.
async fn telemetry_periodic_send<TBl, TBackend, TExec, TRtApi, TExPool>(
client: Arc<Client<TBackend, TExec, TBl, TRtApi>>,
async fn telemetry_periodic_send<TBl, TExPool, TCl>(
client: Arc<TCl>,
transaction_pool: Arc<TExPool>,
mut metrics_service: MetricsService,
network_status_sinks: Arc<Mutex<status_sinks::StatusSinks<(NetworkStatus<TBl>, NetworkState)>>>
)
where
TBl: BlockT,
TExec: CallExecutor<TBl>,
Client<TBackend, TExec, TBl, TRtApi>: ProvideRuntimeApi<TBl>,
TCl: ProvideRuntimeApi<TBl> + UsageProvider<TBl>,
TExPool: MaintainedTransactionPool<Block=TBl, Hash = <TBl as BlockT>::Hash>,
TBackend: sc_client_api::backend::Backend<TBl>,
{
let (state_tx, state_rx) = tracing_unbounded::<(NetworkStatus<_>, NetworkState)>("mpsc_netstat1");
network_status_sinks.lock().push(std::time::Duration::from_millis(5000), state_tx);
@@ -1322,11 +1284,11 @@ fn build_telemetry<TBl: BlockT>(
(telemetry, future)
}
fn gen_handler<TBl, TBackend, TExec, TRtApi, TExPool, TRpc>(
fn gen_handler<TBl, TBackend, TExPool, TRpc, TCl>(
deny_unsafe: sc_rpc::DenyUnsafe,
config: &Configuration,
task_manager: &TaskManager,
client: Arc<Client<TBackend, TExec, TBl, TRtApi>>,
client: Arc<TCl>,
transaction_pool: Arc<TExPool>,
keystore: Arc<RwLock<Keystore>>,
on_demand: Option<Arc<OnDemand<TBl>>>,
@@ -1337,13 +1299,14 @@ fn gen_handler<TBl, TBackend, TExec, TRtApi, TExPool, TRpc>(
) -> jsonrpc_pubsub::PubSubHandler<sc_rpc::Metadata>
where
TBl: BlockT,
TExec: CallExecutor<TBl, Backend = TBackend> + Send + Sync + 'static,
TRtApi: Send + Sync + 'static,
Client<TBackend, TExec, TBl, TRtApi>: ProvideRuntimeApi<TBl>,
TCl: ProvideRuntimeApi<TBl> + BlockchainEvents<TBl> + HeaderBackend<TBl> +
HeaderMetadata<TBl, Error=sp_blockchain::Error> + ExecutorProvider<TBl> +
CallApiAt<TBl, Error=sp_blockchain::Error> + ProofProvider<TBl> +
StorageProvider<TBl, TBackend> + BlockBackend<TBl> + Send + Sync + 'static,
TExPool: MaintainedTransactionPool<Block=TBl, Hash = <TBl as BlockT>::Hash> + 'static,
TBackend: sc_client_api::backend::Backend<TBl> + 'static,
TRpc: sc_rpc::RpcExtension<sc_rpc::Metadata>,
<Client<TBackend, TExec, TBl, TRtApi> as ProvideRuntimeApi<TBl>>::Api:
<TCl as ProvideRuntimeApi<TBl>>::Api:
sp_session::SessionKeys<TBl> +
sp_api::Metadata<TBl, Error = sp_blockchain::Error>,
{
@@ -1412,15 +1375,14 @@ fn gen_handler<TBl, TBackend, TExec, TRtApi, TExPool, TRpc>(
))
}
fn build_network<TBl, TBackend, TExec, TRtApi, TExPool, TImpQu>(
fn build_network<TBl, TExPool, TImpQu, TCl>(
config: &Configuration,
client: Arc<Client<TBackend, TExec, TBl, TRtApi>>,
client: Arc<TCl>,
transaction_pool: Arc<TExPool>,
spawn_handle: SpawnTaskHandle,
on_demand: Option<Arc<OnDemand<TBl>>>,
block_announce_validator_builder: Option<Box<
dyn FnOnce(Arc<Client<TBackend, TExec, TBl, TRtApi>>) ->
Box<dyn BlockAnnounceValidator<TBl> + Send> + Send
dyn FnOnce(Arc<TCl>) -> Box<dyn BlockAnnounceValidator<TBl> + Send> + Send
>>,
finality_proof_request_builder: Option<BoxFinalityProofRequestBuilder<TBl>>,
finality_proof_provider: Option<Arc<dyn FinalityProofProvider<TBl>>>,
@@ -1436,11 +1398,10 @@ fn build_network<TBl, TBackend, TExec, TRtApi, TExPool, TImpQu>(
>
where
TBl: BlockT,
TExec: CallExecutor<TBl> + Send + Sync + 'static,
TRtApi: Send + Sync + 'static,
Client<TBackend, TExec, TBl, TRtApi>: ProvideRuntimeApi<TBl>,
TCl: ProvideRuntimeApi<TBl> + HeaderMetadata<TBl, Error=sp_blockchain::Error> + Chain<TBl> +
BlockBackend<TBl> + BlockIdTo<TBl, Error=sp_blockchain::Error> + ProofProvider<TBl> +
HeaderBackend<TBl> + BlockchainEvents<TBl> + 'static,
TExPool: MaintainedTransactionPool<Block=TBl, Hash = <TBl as BlockT>::Hash> + 'static,
TBackend: sc_client_api::backend::Backend<TBl> + 'static,
TImpQu: ImportQueue<TBl> + 'static,
{
let transaction_pool_adapter = Arc::new(TransactionPoolAdapter {
@@ -353,13 +353,6 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
self.executor.runtime_version(id)
}
/// Get block hash by number.
pub fn block_hash(&self,
block_number: <<Block as BlockT>::Header as HeaderT>::Number
) -> sp_blockchain::Result<Option<Block::Hash>> {
self.backend.blockchain().hash(block_number)
}
/// Reads given header and generates CHT-based header proof for CHT of given size.
pub fn header_proof_with_cht_size(
&self,
@@ -1925,6 +1918,10 @@ impl<B, E, Block, RA> BlockBackend<Block> for Client<B, E, Block, RA>
fn justification(&self, id: &BlockId<Block>) -> sp_blockchain::Result<Option<Justification>> {
self.backend.blockchain().justification(*id)
}
fn block_hash(&self, number: NumberFor<Block>) -> sp_blockchain::Result<Option<Block::Hash>> {
self.backend.blockchain().hash(number)
}
}
impl<B, E, Block, RA> backend::AuxStore for Client<B, E, Block, RA>
+67 -259
View File
@@ -36,22 +36,15 @@ mod client;
mod task_manager;
use std::{io, pin::Pin};
use std::marker::PhantomData;
use std::net::SocketAddr;
use std::collections::HashMap;
use std::time::Duration;
use wasm_timer::Instant;
use std::task::{Poll, Context};
use std::task::Poll;
use parking_lot::Mutex;
use client::Client;
use futures::{
Future, FutureExt, Stream, StreamExt,
compat::*,
sink::SinkExt,
task::{Spawn, FutureObj, SpawnError},
};
use sc_network::{NetworkService, NetworkStatus, network_state::NetworkState, PeerId};
use futures::{Future, FutureExt, Stream, StreamExt, compat::*};
use sc_network::{NetworkStatus, network_state::NetworkState, PeerId};
use log::{log, warn, debug, error, Level};
use codec::{Encode, Decode};
use sp_runtime::generic::BlockId;
@@ -84,14 +77,9 @@ pub use sc_network::config::{
TransactionImportFuture,
};
pub use sc_tracing::TracingReceiver;
pub use task_manager::{SpawnEssentialTaskHandle, SpawnTaskHandle};
use task_manager::TaskManager;
use sp_blockchain::{HeaderBackend, HeaderMetadata};
use sp_api::{ApiExt, ConstructRuntimeApi, ApiErrorExt};
use sc_client_api::{
Backend as BackendT, BlockchainEvents, CallExecutor, UsageProvider,
};
use sp_block_builder::BlockBuilder;
pub use task_manager::SpawnTaskHandle;
pub use task_manager::TaskManager;
use sc_client_api::{Backend, BlockchainEvents};
const DEFAULT_PROTOCOL_ID: &str = "sup";
@@ -105,88 +93,10 @@ impl<T: MallocSizeOf> MallocSizeOfWasm for T {}
#[cfg(target_os = "unknown")]
impl<T> MallocSizeOfWasm for T {}
/// Substrate service.
pub struct Service<TBl, TCl, TSc, TNetStatus, TNet, TTxPool, TOc> {
client: Arc<TCl>,
task_manager: TaskManager,
select_chain: Option<TSc>,
network: Arc<TNet>,
// Sinks to propagate network status updates.
// For each element, every time the `Interval` fires we push an element on the sender.
network_status_sinks: Arc<Mutex<status_sinks::StatusSinks<(TNetStatus, NetworkState)>>>,
transaction_pool: Arc<TTxPool>,
// Send a signal when a spawned essential task has concluded. The next time
// the service future is polled it should complete with an error.
essential_failed_tx: TracingUnboundedSender<()>,
// A receiver for spawned essential-tasks concluding.
essential_failed_rx: TracingUnboundedReceiver<()>,
rpc_handlers: sc_rpc_server::RpcHandler<sc_rpc::Metadata>,
_rpc: Box<dyn std::any::Any + Send + Sync>,
_telemetry: Option<sc_telemetry::Telemetry>,
_telemetry_on_connect_sinks: Arc<Mutex<Vec<TracingUnboundedSender<()>>>>,
_offchain_workers: Option<Arc<TOc>>,
keystore: sc_keystore::KeyStorePtr,
marker: PhantomData<TBl>,
prometheus_registry: Option<prometheus_endpoint::Registry>,
// The base path is kept here because it can be a temporary directory which will be deleted
// when dropped
_base_path: Option<Arc<BasePath>>,
}
impl<TBl, TCl, TSc, TNetStatus, TNet, TTxPool, TOc> Unpin for Service<TBl, TCl, TSc, TNetStatus, TNet, TTxPool, TOc> {}
/// Abstraction over a Substrate service.
pub trait AbstractService: Future<Output = Result<(), Error>> + Send + Unpin + Spawn + 'static {
/// Type of block of this chain.
type Block: BlockT;
/// Backend storage for the client.
type Backend: 'static + BackendT<Self::Block>;
/// How to execute calls towards the runtime.
type CallExecutor: 'static + CallExecutor<Self::Block> + Send + Sync + Clone;
/// API that the runtime provides.
type RuntimeApi: Send + Sync;
/// Chain selection algorithm.
type SelectChain: sp_consensus::SelectChain<Self::Block>;
/// Transaction pool.
type TransactionPool: TransactionPool<Block = Self::Block> + MallocSizeOfWasm;
/// The generic Client type, the bounds here are the ones specifically required by
/// internal crates like sc_informant.
type Client:
HeaderMetadata<Self::Block, Error = sp_blockchain::Error> + UsageProvider<Self::Block>
+ BlockchainEvents<Self::Block> + HeaderBackend<Self::Block> + Send + Sync;
/// Get event stream for telemetry connection established events.
fn telemetry_on_connect_stream(&self) -> TracingUnboundedReceiver<()>;
/// return a shared instance of Telemetry (if enabled)
fn telemetry(&self) -> Option<sc_telemetry::Telemetry>;
/// Spawns a task in the background that runs the future passed as parameter.
///
/// Information about this task will be reported to Prometheus.
///
/// The task name is a `&'static str` as opposed to a `String`. The reason for that is that
/// in order to avoid memory consumption issues with the Prometheus metrics, the set of
/// possible task names has to be bounded.
#[deprecated(note = "Use `spawn_task_handle().spawn() instead.")]
fn spawn_task(&self, name: &'static str, task: impl Future<Output = ()> + Send + 'static);
/// Spawns a task in the background that runs the future passed as
/// parameter. The given task is considered essential, i.e. if it errors we
/// trigger a service exit.
#[deprecated(note = "Use `spawn_essential_task_handle().spawn() instead.")]
fn spawn_essential_task(&self, name: &'static str, task: impl Future<Output = ()> + Send + 'static);
/// Returns a handle for spawning essential tasks. Any task spawned through this handle is
/// considered essential, i.e. if it errors we trigger a service exit.
fn spawn_essential_task_handle(&self) -> SpawnEssentialTaskHandle;
/// Returns a handle for spawning tasks.
fn spawn_task_handle(&self) -> SpawnTaskHandle;
/// Returns the keystore that stores keys.
fn keystore(&self) -> sc_keystore::KeyStorePtr;
/// RPC handlers that can perform RPC queries.
pub struct RpcHandlers(sc_rpc_server::RpcHandler<sc_rpc::Metadata>);
impl RpcHandlers {
/// Starts an RPC query.
///
/// The query is passed as a string and must be a JSON text similar to what an HTTP client
@@ -196,178 +106,76 @@ pub trait AbstractService: Future<Output = Result<(), Error>> + Send + Unpin + S
///
/// If the request subscribes you to events, the `Sender` in the `RpcSession` object is used to
/// send back spontaneous events.
fn rpc_query(&self, mem: &RpcSession, request: &str) -> Pin<Box<dyn Future<Output = Option<String>> + Send>>;
pub fn rpc_query(&self, mem: &RpcSession, request: &str)
-> Pin<Box<dyn Future<Output = Option<String>> + Send>> {
self.0.handle_request(request, mem.metadata.clone())
.compat()
.map(|res| res.expect("this should never fail"))
.boxed()
}
}
/// Get shared client instance.
fn client(&self) -> Arc<Self::Client>;
/// Sinks to propagate network status updates.
/// For each element, every time the `Interval` fires we push an element on the sender.
pub struct NetworkStatusSinks<Block: BlockT>(
Arc<Mutex<status_sinks::StatusSinks<(NetworkStatus<Block>, NetworkState)>>>,
);
/// Get clone of select chain.
fn select_chain(&self) -> Option<Self::SelectChain>;
/// Get shared network instance.
fn network(&self)
-> Arc<NetworkService<Self::Block, <Self::Block as BlockT>::Hash>>;
impl<Block: BlockT> NetworkStatusSinks<Block> {
fn new(
sinks: Arc<Mutex<status_sinks::StatusSinks<(NetworkStatus<Block>, NetworkState)>>>
) -> Self {
Self(sinks)
}
/// Returns a receiver that periodically receives a status of the network.
fn network_status(&self, interval: Duration) -> TracingUnboundedReceiver<(NetworkStatus<Self::Block>, NetworkState)>;
/// Get shared transaction pool instance.
fn transaction_pool(&self) -> Arc<Self::TransactionPool>;
/// Get a handle to a future that will resolve on exit.
#[deprecated(note = "Use `spawn_task`/`spawn_essential_task` instead, those functions will attach on_exit signal.")]
fn on_exit(&self) -> ::exit_future::Exit;
/// Get the prometheus metrics registry, if available.
fn prometheus_registry(&self) -> Option<prometheus_endpoint::Registry>;
/// Get a clone of the base_path
fn base_path(&self) -> Option<Arc<BasePath>>;
}
impl<TBl, TBackend, TExec, TRtApi, TSc, TExPool, TOc> AbstractService for
Service<TBl, Client<TBackend, TExec, TBl, TRtApi>, TSc, NetworkStatus<TBl>,
NetworkService<TBl, TBl::Hash>, TExPool, TOc>
where
TBl: BlockT,
TBackend: 'static + BackendT<TBl>,
TExec: 'static + CallExecutor<TBl, Backend = TBackend> + Send + Sync + Clone,
TRtApi: 'static + Send + Sync + ConstructRuntimeApi<TBl, Client<TBackend, TExec, TBl, TRtApi>>,
<TRtApi as ConstructRuntimeApi<TBl, Client<TBackend, TExec, TBl, TRtApi>>>::RuntimeApi:
sp_api::Core<TBl>
+ ApiExt<TBl, StateBackend = TBackend::State>
+ ApiErrorExt<Error = sp_blockchain::Error>
+ BlockBuilder<TBl>,
TSc: sp_consensus::SelectChain<TBl> + 'static + Clone + Send + Unpin,
TExPool: 'static + TransactionPool<Block = TBl> + MallocSizeOfWasm,
TOc: 'static + Send + Sync,
{
type Block = TBl;
type Backend = TBackend;
type CallExecutor = TExec;
type RuntimeApi = TRtApi;
type SelectChain = TSc;
type TransactionPool = TExPool;
type Client = Client<Self::Backend, Self::CallExecutor, Self::Block, Self::RuntimeApi>;
fn telemetry_on_connect_stream(&self) -> TracingUnboundedReceiver<()> {
let (sink, stream) = tracing_unbounded("mpsc_telemetry_on_connect");
self._telemetry_on_connect_sinks.lock().push(sink);
stream
}
fn telemetry(&self) -> Option<sc_telemetry::Telemetry> {
self._telemetry.clone()
}
fn keystore(&self) -> sc_keystore::KeyStorePtr {
self.keystore.clone()
}
fn spawn_task(&self, name: &'static str, task: impl Future<Output = ()> + Send + 'static) {
self.task_manager.spawn(name, task)
}
fn spawn_essential_task(&self, name: &'static str, task: impl Future<Output = ()> + Send + 'static) {
let mut essential_failed = self.essential_failed_tx.clone();
let essential_task = std::panic::AssertUnwindSafe(task)
.catch_unwind()
.map(move |_| {
error!("Essential task `{}` failed. Shutting down service.", name);
let _ = essential_failed.send(());
});
let _ = self.spawn_task_handle().spawn(name, essential_task);
}
fn spawn_task_handle(&self) -> SpawnTaskHandle {
self.task_manager.spawn_handle()
}
fn spawn_essential_task_handle(&self) -> SpawnEssentialTaskHandle {
SpawnEssentialTaskHandle::new(
self.essential_failed_tx.clone(),
self.task_manager.spawn_handle(),
)
}
fn rpc_query(&self, mem: &RpcSession, request: &str) -> Pin<Box<dyn Future<Output = Option<String>> + Send>> {
Box::pin(
self.rpc_handlers.handle_request(request, mem.metadata.clone())
.compat()
.map(|res| res.expect("this should never fail"))
)
}
fn client(&self) -> Arc<Self::Client> {
self.client.clone()
}
fn select_chain(&self) -> Option<Self::SelectChain> {
self.select_chain.clone()
}
fn network(&self)
-> Arc<NetworkService<Self::Block, <Self::Block as BlockT>::Hash>>
{
self.network.clone()
}
fn network_status(&self, interval: Duration) -> TracingUnboundedReceiver<(NetworkStatus<Self::Block>, NetworkState)> {
pub fn network_status(&self, interval: Duration)
-> TracingUnboundedReceiver<(NetworkStatus<Block>, NetworkState)> {
let (sink, stream) = tracing_unbounded("mpsc_network_status");
self.network_status_sinks.lock().push(interval, sink);
self.0.lock().push(interval, sink);
stream
}
}
fn transaction_pool(&self) -> Arc<Self::TransactionPool> {
self.transaction_pool.clone()
}
/// Sinks to propagate telemetry connection established events.
pub struct TelemetryOnConnectSinks(pub Arc<Mutex<Vec<TracingUnboundedSender<()>>>>);
fn on_exit(&self) -> exit_future::Exit {
self.task_manager.on_exit()
}
fn prometheus_registry(&self) -> Option<prometheus_endpoint::Registry> {
self.prometheus_registry.clone()
}
fn base_path(&self) -> Option<Arc<BasePath>> {
self._base_path.clone()
impl TelemetryOnConnectSinks {
/// Get event stream for telemetry connection established events.
pub fn on_connect_stream(&self) -> TracingUnboundedReceiver<()> {
let (sink, stream) =tracing_unbounded("mpsc_telemetry_on_connect");
self.0.lock().push(sink);
stream
}
}
impl<TBl, TCl, TSc, TNetStatus, TNet, TTxPool, TOc> Future for
Service<TBl, TCl, TSc, TNetStatus, TNet, TTxPool, TOc>
{
type Output = Result<(), Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let this = Pin::into_inner(self);
match Pin::new(&mut this.essential_failed_rx).poll_next(cx) {
Poll::Pending => {},
Poll::Ready(_) => {
// Ready(None) should not be possible since we hold a live
// sender.
return Poll::Ready(Err(Error::Other("Essential task failed.".into())));
}
}
// The service future never ends.
Poll::Pending
}
}
impl<TBl, TCl, TSc, TNetStatus, TNet, TTxPool, TOc> Spawn for
Service<TBl, TCl, TSc, TNetStatus, TNet, TTxPool, TOc>
{
fn spawn_obj(
&self,
future: FutureObj<'static, ()>
) -> Result<(), SpawnError> {
self.task_manager.spawn_handle().spawn("unnamed", future);
Ok(())
}
/// The individual components of the chain, built by the service builder. You are encouraged to
/// deconstruct this into its fields.
pub struct ServiceComponents<TBl: BlockT, TBackend: Backend<TBl>, TSc, TExPool, TCl> {
/// A blockchain client.
pub client: Arc<TCl>,
/// A shared transaction pool instance.
pub transaction_pool: Arc<TExPool>,
/// The chain task manager.
pub task_manager: TaskManager,
/// A keystore that stores keys.
pub keystore: sc_keystore::KeyStorePtr,
/// A shared network instance.
pub network: Arc<sc_network::NetworkService<TBl, <TBl as BlockT>::Hash>>,
/// RPC handlers that can perform RPC queries.
pub rpc_handlers: Arc<RpcHandlers>,
/// A shared instance of the chain selection algorithm.
pub select_chain: Option<TSc>,
/// Sinks to propagate network status updates.
pub network_status_sinks: NetworkStatusSinks<TBl>,
/// A prometheus metrics registry, (if enabled).
pub prometheus_registry: Option<prometheus_endpoint::Registry>,
/// Shared Telemetry connection sinks,
pub telemetry_on_connect_sinks: TelemetryOnConnectSinks,
/// A shared offchain workers instance.
pub offchain_workers: Option<Arc<sc_offchain::OffchainWorkers<
TCl, TBackend::OffchainStorage, TBl
>>>,
}
/// Builds a never-ending future that continuously polls the network.
+44 -18
View File
@@ -13,14 +13,15 @@
//! Substrate service tasks management module.
use std::{panic, result::Result};
use std::{panic, result::Result, pin::Pin};
use exit_future::Signal;
use log::debug;
use futures::{
Future, FutureExt,
Future, FutureExt, StreamExt,
future::{select, Either, BoxFuture},
compat::*,
task::{Spawn, FutureObj, SpawnError},
sink::SinkExt,
};
use prometheus_endpoint::{
exponential_buckets, register,
@@ -28,8 +29,8 @@ use prometheus_endpoint::{
CounterVec, HistogramOpts, HistogramVec, Opts, Registry, U64
};
use sc_client_api::CloneableSpawn;
use sp_utils::mpsc::TracingUnboundedSender;
use crate::config::{TaskExecutor, TaskType};
use sp_utils::mpsc::{TracingUnboundedSender, TracingUnboundedReceiver, tracing_unbounded};
use crate::{config::{TaskExecutor, TaskType}, Error};
mod prometheus_future;
@@ -192,7 +193,6 @@ impl SpawnEssentialTaskHandle {
task: impl Future<Output = ()> + Send + 'static,
task_type: TaskType,
) {
use futures::sink::SinkExt;
let mut essential_failed = self.essential_failed_tx.clone();
let essential_task = std::panic::AssertUnwindSafe(task)
.catch_unwind()
@@ -216,6 +216,13 @@ pub struct TaskManager {
executor: TaskExecutor,
/// Prometheus metric where to report the polling times.
metrics: Option<Metrics>,
/// Send a signal when a spawned essential task has concluded. The next time
/// the service future is polled it should complete with an error.
essential_failed_tx: TracingUnboundedSender<()>,
/// A receiver for spawned essential-tasks concluding.
essential_failed_rx: TracingUnboundedReceiver<()>,
/// Things to keep alive until the task manager is dropped.
keep_alive: Box<dyn std::any::Any + Send + Sync>,
}
impl TaskManager {
@@ -226,6 +233,8 @@ impl TaskManager {
prometheus_registry: Option<&Registry>
) -> Result<Self, PrometheusError> {
let (signal, on_exit) = exit_future::signal();
// A side-channel for essential tasks to communicate shutdown.
let (essential_failed_tx, essential_failed_rx) = tracing_unbounded("mpsc_essential_tasks");
let metrics = prometheus_registry.map(Metrics::register).transpose()?;
@@ -234,17 +243,15 @@ impl TaskManager {
signal: Some(signal),
executor,
metrics,
essential_failed_tx,
essential_failed_rx,
keep_alive: Box::new(()),
})
}
/// Spawn background/async task, which will be aware on exit signal.
///
/// See also the documentation of [`SpawnTaskHandler::spawn`].
pub(super) fn spawn(&self, name: &'static str, task: impl Future<Output = ()> + Send + 'static) {
self.spawn_handle().spawn(name, task)
}
pub(super) fn spawn_handle(&self) -> SpawnTaskHandle {
/// Get a handle for spawning tasks.
pub fn spawn_handle(&self) -> SpawnTaskHandle {
SpawnTaskHandle {
on_exit: self.on_exit.clone(),
executor: self.executor.clone(),
@@ -252,18 +259,37 @@ impl TaskManager {
}
}
/// Clone on exit signal.
pub(super) fn on_exit(&self) -> exit_future::Exit {
self.on_exit.clone()
/// Get a handle for spawning essential tasks.
pub fn spawn_essential_handle(&self) -> SpawnEssentialTaskHandle {
SpawnEssentialTaskHandle::new(self.essential_failed_tx.clone(), self.spawn_handle())
}
/// Return a future that will end if an essential task fails.
pub fn future<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'a>> {
Box::pin(async move {
self.essential_failed_rx.next().await;
Err(Error::Other("Essential task failed.".into()))
})
}
/// Signal to terminate all the running tasks.
pub fn terminate(&mut self) {
if let Some(signal) = self.signal.take() {
let _ = signal.fire();
}
}
/// Set what the task manager should keep alivei
pub(super) fn keep_alive<T: 'static + Send + Sync>(&mut self, to_keep_alive: T) {
self.keep_alive = Box::new(to_keep_alive);
}
}
impl Drop for TaskManager {
fn drop(&mut self) {
debug!(target: "service", "Tasks manager shutdown");
if let Some(signal) = self.signal.take() {
let _ = signal.fire();
}
self.terminate();
}
}