mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 06:21:11 +00:00
rpc: backpressured RPC server (bump jsonrpsee 0.20) (#1313)
This is a rather big change in jsonrpsee, the major things in this bump are: - Server backpressure (the subscription impls are modified to deal with that) - Allow custom error types / return types (remove jsonrpsee::core::Error and jsonrpee::core::CallError) - Bug fixes (graceful shutdown in particular not used by substrate anyway) - Less dependencies for the clients in particular - Return type requires Clone in method call responses - Moved to tokio channels - Async subscription API (not used in this PR) Major changes in this PR: - The subscriptions are now bounded and if subscription can't keep up with the server it is dropped - CLI: add parameter to configure the jsonrpc server bounded message buffer (default is 64) - Add our own subscription helper to deal with the unbounded streams in substrate The most important things in this PR to review is the added helpers functions in `substrate/client/rpc/src/utils.rs` and the rest is pretty much chore. Regarding the "bounded buffer limit" it may cause the server to handle the JSON-RPC calls slower than before. The message size limit is bounded by "--rpc-response-size" thus "by default 10MB * 64 = 640MB" but the subscription message size is not covered by this limit and could be capped as well. Hopefully the last release prior to 1.0, sorry in advance for a big PR Previous attempt: https://github.com/paritytech/substrate/pull/13992 Resolves https://github.com/paritytech/polkadot-sdk/issues/748, resolves https://github.com/paritytech/polkadot-sdk/issues/627
This commit is contained in:
@@ -19,14 +19,17 @@
|
||||
//! Blockchain API backend for full nodes.
|
||||
|
||||
use super::{client_err, ChainBackend, Error};
|
||||
use crate::SubscriptionTaskExecutor;
|
||||
use crate::{
|
||||
utils::{pipe_from_stream, spawn_subscription_task},
|
||||
SubscriptionTaskExecutor,
|
||||
};
|
||||
use std::{marker::PhantomData, sync::Arc};
|
||||
|
||||
use futures::{
|
||||
future::{self, FutureExt},
|
||||
future::{self},
|
||||
stream::{self, Stream, StreamExt},
|
||||
};
|
||||
use jsonrpsee::SubscriptionSink;
|
||||
use jsonrpsee::{core::async_trait, PendingSubscriptionSink};
|
||||
use sc_client_api::{BlockBackend, BlockchainEvents};
|
||||
use sp_blockchain::HeaderBackend;
|
||||
use sp_runtime::{generic::SignedBlock, traits::Block as BlockT};
|
||||
@@ -48,6 +51,7 @@ impl<Block: BlockT, Client> FullChain<Block, Client> {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<Block, Client> ChainBackend<Client, Block> for FullChain<Block, Client>
|
||||
where
|
||||
Block: BlockT + 'static,
|
||||
@@ -66,11 +70,11 @@ where
|
||||
self.client.block(self.unwrap_or_best(hash)).map_err(client_err)
|
||||
}
|
||||
|
||||
fn subscribe_all_heads(&self, sink: SubscriptionSink) {
|
||||
fn subscribe_all_heads(&self, pending: PendingSubscriptionSink) {
|
||||
subscribe_headers(
|
||||
&self.client,
|
||||
&self.executor,
|
||||
sink,
|
||||
pending,
|
||||
|| self.client().info().best_hash,
|
||||
|| {
|
||||
self.client()
|
||||
@@ -80,11 +84,11 @@ where
|
||||
)
|
||||
}
|
||||
|
||||
fn subscribe_new_heads(&self, sink: SubscriptionSink) {
|
||||
fn subscribe_new_heads(&self, pending: PendingSubscriptionSink) {
|
||||
subscribe_headers(
|
||||
&self.client,
|
||||
&self.executor,
|
||||
sink,
|
||||
pending,
|
||||
|| self.client().info().best_hash,
|
||||
|| {
|
||||
self.client()
|
||||
@@ -95,11 +99,11 @@ where
|
||||
)
|
||||
}
|
||||
|
||||
fn subscribe_finalized_heads(&self, sink: SubscriptionSink) {
|
||||
fn subscribe_finalized_heads(&self, pending: PendingSubscriptionSink) {
|
||||
subscribe_headers(
|
||||
&self.client,
|
||||
&self.executor,
|
||||
sink,
|
||||
pending,
|
||||
|| self.client().info().finalized_hash,
|
||||
|| {
|
||||
self.client()
|
||||
@@ -114,7 +118,7 @@ where
|
||||
fn subscribe_headers<Block, Client, F, G, S>(
|
||||
client: &Arc<Client>,
|
||||
executor: &SubscriptionTaskExecutor,
|
||||
mut sink: SubscriptionSink,
|
||||
pending: PendingSubscriptionSink,
|
||||
best_block_hash: G,
|
||||
stream: F,
|
||||
) where
|
||||
@@ -139,9 +143,5 @@ fn subscribe_headers<Block, Client, F, G, S>(
|
||||
// duplicates at the beginning of the stream though.
|
||||
let stream = stream::iter(maybe_header).chain(stream());
|
||||
|
||||
let fut = async move {
|
||||
sink.pipe_from_stream(stream).await;
|
||||
};
|
||||
|
||||
executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed());
|
||||
spawn_subscription_task(executor, pipe_from_stream(pending, stream));
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ use std::sync::Arc;
|
||||
|
||||
use crate::SubscriptionTaskExecutor;
|
||||
|
||||
use jsonrpsee::{core::RpcResult, types::SubscriptionResult, SubscriptionSink};
|
||||
use jsonrpsee::{core::async_trait, PendingSubscriptionSink};
|
||||
use sc_client_api::BlockchainEvents;
|
||||
use sp_rpc::{list::ListOrValue, number::NumberOrHex};
|
||||
use sp_runtime::{
|
||||
@@ -42,6 +42,7 @@ pub use sc_rpc_api::chain::*;
|
||||
use sp_blockchain::HeaderBackend;
|
||||
|
||||
/// Blockchain backend API
|
||||
#[async_trait]
|
||||
trait ChainBackend<Client, Block: BlockT>: Send + Sync + 'static
|
||||
where
|
||||
Block: BlockT + 'static,
|
||||
@@ -91,13 +92,13 @@ where
|
||||
}
|
||||
|
||||
/// All new head subscription
|
||||
fn subscribe_all_heads(&self, sink: SubscriptionSink);
|
||||
fn subscribe_all_heads(&self, pending: PendingSubscriptionSink);
|
||||
|
||||
/// New best head subscription
|
||||
fn subscribe_new_heads(&self, sink: SubscriptionSink);
|
||||
fn subscribe_new_heads(&self, pending: PendingSubscriptionSink);
|
||||
|
||||
/// Finalized head subscription
|
||||
fn subscribe_finalized_heads(&self, sink: SubscriptionSink);
|
||||
fn subscribe_finalized_heads(&self, pending: PendingSubscriptionSink);
|
||||
}
|
||||
|
||||
/// Create new state API that works on full node.
|
||||
@@ -118,6 +119,7 @@ pub struct Chain<Block: BlockT, Client> {
|
||||
backend: Box<dyn ChainBackend<Client, Block>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<Block, Client> ChainApiServer<NumberFor<Block>, Block::Hash, Block::Header, SignedBlock<Block>>
|
||||
for Chain<Block, Client>
|
||||
where
|
||||
@@ -125,20 +127,20 @@ where
|
||||
Block::Header: Unpin,
|
||||
Client: HeaderBackend<Block> + BlockchainEvents<Block> + 'static,
|
||||
{
|
||||
fn header(&self, hash: Option<Block::Hash>) -> RpcResult<Option<Block::Header>> {
|
||||
self.backend.header(hash).map_err(Into::into)
|
||||
fn header(&self, hash: Option<Block::Hash>) -> Result<Option<Block::Header>, Error> {
|
||||
self.backend.header(hash)
|
||||
}
|
||||
|
||||
fn block(&self, hash: Option<Block::Hash>) -> RpcResult<Option<SignedBlock<Block>>> {
|
||||
self.backend.block(hash).map_err(Into::into)
|
||||
fn block(&self, hash: Option<Block::Hash>) -> Result<Option<SignedBlock<Block>>, Error> {
|
||||
self.backend.block(hash)
|
||||
}
|
||||
|
||||
fn block_hash(
|
||||
&self,
|
||||
number: Option<ListOrValue<NumberOrHex>>,
|
||||
) -> RpcResult<ListOrValue<Option<Block::Hash>>> {
|
||||
) -> Result<ListOrValue<Option<Block::Hash>>, Error> {
|
||||
match number {
|
||||
None => self.backend.block_hash(None).map(ListOrValue::Value).map_err(Into::into),
|
||||
None => self.backend.block_hash(None).map(ListOrValue::Value),
|
||||
Some(ListOrValue::Value(number)) => self
|
||||
.backend
|
||||
.block_hash(Some(number))
|
||||
@@ -152,23 +154,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn finalized_head(&self) -> RpcResult<Block::Hash> {
|
||||
self.backend.finalized_head().map_err(Into::into)
|
||||
fn finalized_head(&self) -> Result<Block::Hash, Error> {
|
||||
self.backend.finalized_head()
|
||||
}
|
||||
|
||||
fn subscribe_all_heads(&self, sink: SubscriptionSink) -> SubscriptionResult {
|
||||
self.backend.subscribe_all_heads(sink);
|
||||
Ok(())
|
||||
fn subscribe_all_heads(&self, pending: PendingSubscriptionSink) {
|
||||
self.backend.subscribe_all_heads(pending);
|
||||
}
|
||||
|
||||
fn subscribe_new_heads(&self, sink: SubscriptionSink) -> SubscriptionResult {
|
||||
self.backend.subscribe_new_heads(sink);
|
||||
Ok(())
|
||||
fn subscribe_new_heads(&self, pending: PendingSubscriptionSink) {
|
||||
self.backend.subscribe_new_heads(pending)
|
||||
}
|
||||
|
||||
fn subscribe_finalized_heads(&self, sink: SubscriptionSink) -> SubscriptionResult {
|
||||
self.backend.subscribe_finalized_heads(sink);
|
||||
Ok(())
|
||||
fn subscribe_finalized_heads(&self, pending: PendingSubscriptionSink) {
|
||||
self.backend.subscribe_finalized_heads(pending)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
use super::*;
|
||||
use crate::testing::{test_executor, timeout_secs};
|
||||
use assert_matches::assert_matches;
|
||||
use jsonrpsee::types::EmptyServerParams as EmptyParams;
|
||||
use jsonrpsee::core::EmptyServerParams as EmptyParams;
|
||||
use sc_block_builder::BlockBuilderBuilder;
|
||||
use sp_consensus::BlockOrigin;
|
||||
use sp_rpc::list::ListOrValue;
|
||||
@@ -252,7 +252,7 @@ async fn test_head_subscription(method: &str) {
|
||||
|
||||
let mut sub = {
|
||||
let api = new_full(client.clone(), test_executor()).into_rpc();
|
||||
let sub = api.subscribe(method, EmptyParams::new()).await.unwrap();
|
||||
let sub = api.subscribe_unbounded(method, EmptyParams::new()).await.unwrap();
|
||||
let block = BlockBuilderBuilder::new(&*client)
|
||||
.on_parent_block(client.chain_info().best_hash)
|
||||
.with_parent_block_number(client.chain_info().best_number)
|
||||
|
||||
Reference in New Issue
Block a user