mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 23:21:06 +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:
@@ -23,15 +23,14 @@ mod tests;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::SubscriptionTaskExecutor;
|
||||
use crate::{
|
||||
utils::{pipe_from_stream, spawn_subscription_task},
|
||||
SubscriptionTaskExecutor,
|
||||
};
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use futures::{FutureExt, TryFutureExt};
|
||||
use jsonrpsee::{
|
||||
core::{async_trait, Error as JsonRpseeError, RpcResult},
|
||||
types::SubscriptionResult,
|
||||
SubscriptionSink,
|
||||
};
|
||||
use futures::TryFutureExt;
|
||||
use jsonrpsee::{core::async_trait, types::ErrorObject, PendingSubscriptionSink};
|
||||
use sc_rpc_api::DenyUnsafe;
|
||||
use sc_transaction_pool_api::{
|
||||
error::IntoPoolError, BlockHash, InPoolTransaction, TransactionFor, TransactionPool,
|
||||
@@ -91,7 +90,7 @@ where
|
||||
P::Hash: Unpin,
|
||||
<P::Block as BlockT>::Hash: Unpin,
|
||||
{
|
||||
async fn submit_extrinsic(&self, ext: Bytes) -> RpcResult<TxHash<P>> {
|
||||
async fn submit_extrinsic(&self, ext: Bytes) -> Result<TxHash<P>> {
|
||||
let xt = match Decode::decode(&mut &ext[..]) {
|
||||
Ok(xt) => xt,
|
||||
Err(err) => return Err(Error::Client(Box::new(err)).into()),
|
||||
@@ -105,7 +104,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> RpcResult<()> {
|
||||
fn insert_key(&self, key_type: String, suri: String, public: Bytes) -> Result<()> {
|
||||
self.deny_unsafe.check_if_safe()?;
|
||||
|
||||
let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?;
|
||||
@@ -115,7 +114,7 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rotate_keys(&self) -> RpcResult<Bytes> {
|
||||
fn rotate_keys(&self) -> Result<Bytes> {
|
||||
self.deny_unsafe.check_if_safe()?;
|
||||
|
||||
let best_block_hash = self.client.info().best_hash;
|
||||
@@ -129,7 +128,7 @@ where
|
||||
.map_err(|api_err| Error::Client(Box::new(api_err)).into())
|
||||
}
|
||||
|
||||
fn has_session_keys(&self, session_keys: Bytes) -> RpcResult<bool> {
|
||||
fn has_session_keys(&self, session_keys: Bytes) -> Result<bool> {
|
||||
self.deny_unsafe.check_if_safe()?;
|
||||
|
||||
let best_block_hash = self.client.info().best_hash;
|
||||
@@ -143,21 +142,21 @@ where
|
||||
Ok(self.keystore.has_keys(&keys))
|
||||
}
|
||||
|
||||
fn has_key(&self, public_key: Bytes, key_type: String) -> RpcResult<bool> {
|
||||
fn has_key(&self, public_key: Bytes, key_type: String) -> Result<bool> {
|
||||
self.deny_unsafe.check_if_safe()?;
|
||||
|
||||
let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?;
|
||||
Ok(self.keystore.has_keys(&[(public_key.to_vec(), key_type)]))
|
||||
}
|
||||
|
||||
fn pending_extrinsics(&self) -> RpcResult<Vec<Bytes>> {
|
||||
fn pending_extrinsics(&self) -> Result<Vec<Bytes>> {
|
||||
Ok(self.pool.ready().map(|tx| tx.data().encode().into()).collect())
|
||||
}
|
||||
|
||||
fn remove_extrinsic(
|
||||
&self,
|
||||
bytes_or_hash: Vec<hash::ExtrinsicOrHash<TxHash<P>>>,
|
||||
) -> RpcResult<Vec<TxHash<P>>> {
|
||||
) -> Result<Vec<TxHash<P>>> {
|
||||
self.deny_unsafe.check_if_safe()?;
|
||||
let hashes = bytes_or_hash
|
||||
.into_iter()
|
||||
@@ -178,13 +177,13 @@ where
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn watch_extrinsic(&self, mut sink: SubscriptionSink, xt: Bytes) -> SubscriptionResult {
|
||||
fn watch_extrinsic(&self, pending: PendingSubscriptionSink, xt: Bytes) {
|
||||
let best_block_hash = self.client.info().best_hash;
|
||||
let dxt = match TransactionFor::<P>::decode(&mut &xt[..]).map_err(|e| Error::from(e)) {
|
||||
Ok(dxt) => dxt,
|
||||
Err(e) => {
|
||||
let _ = sink.reject(JsonRpseeError::from(e));
|
||||
return Ok(())
|
||||
spawn_subscription_task(&self.executor, pending.reject(e));
|
||||
return
|
||||
},
|
||||
};
|
||||
|
||||
@@ -198,15 +197,14 @@ where
|
||||
let stream = match submit.await {
|
||||
Ok(stream) => stream,
|
||||
Err(err) => {
|
||||
let _ = sink.reject(JsonRpseeError::from(err));
|
||||
let _ = pending.reject(ErrorObject::from(err)).await;
|
||||
return
|
||||
},
|
||||
};
|
||||
|
||||
sink.pipe_from_stream(stream).await;
|
||||
pipe_from_stream(pending, stream).await;
|
||||
};
|
||||
|
||||
self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed());
|
||||
Ok(())
|
||||
spawn_subscription_task(&self.executor, fut);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,7 @@ use crate::testing::{test_executor, timeout_secs};
|
||||
use assert_matches::assert_matches;
|
||||
use codec::Encode;
|
||||
use jsonrpsee::{
|
||||
core::Error as RpcError,
|
||||
types::{error::CallError, EmptyServerParams as EmptyParams},
|
||||
core::{EmptyServerParams as EmptyParams, Error as RpcError},
|
||||
RpcModule,
|
||||
};
|
||||
use sc_transaction_pool::{BasicPool, FullChainApi};
|
||||
@@ -104,7 +103,7 @@ async fn author_submit_transaction_should_not_cause_error() {
|
||||
|
||||
assert_matches!(
|
||||
api.call::<_, H256>("author_submitExtrinsic", [xt]).await,
|
||||
Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("Already Imported") && err.code() == 1013
|
||||
Err(RpcError::Call(err)) if err.message().contains("Already Imported") && err.code() == 1013
|
||||
);
|
||||
}
|
||||
|
||||
@@ -119,7 +118,7 @@ async fn author_should_watch_extrinsic() {
|
||||
true,
|
||||
);
|
||||
|
||||
let mut sub = api.subscribe("author_submitAndWatchExtrinsic", [xt]).await.unwrap();
|
||||
let mut sub = api.subscribe_unbounded("author_submitAndWatchExtrinsic", [xt]).await.unwrap();
|
||||
let (tx, sub_id) = timeout_secs(10, sub.next::<TransactionStatus<H256, Block>>())
|
||||
.await
|
||||
.unwrap()
|
||||
@@ -157,11 +156,11 @@ async fn author_should_return_watch_validation_error() {
|
||||
let invalid_xt = ExtrinsicBuilder::new_fill_block(Perbill::from_percent(100)).build();
|
||||
|
||||
let api = TestSetup::into_rpc();
|
||||
let failed_sub = api.subscribe(METHOD, [to_hex(&invalid_xt.encode(), true)]).await;
|
||||
let failed_sub = api.subscribe_unbounded(METHOD, [to_hex(&invalid_xt.encode(), true)]).await;
|
||||
|
||||
assert_matches!(
|
||||
failed_sub,
|
||||
Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("Invalid Transaction") && err.code() == 1010
|
||||
Err(RpcError::Call(err)) if err.message().contains("Invalid Transaction") && err.code() == 1010
|
||||
);
|
||||
}
|
||||
|
||||
@@ -277,7 +276,7 @@ async fn author_has_session_keys() {
|
||||
|
||||
assert_matches!(
|
||||
api.call::<_, bool>("author_hasSessionKeys", vec![Bytes::from(vec![1, 2, 3])]).await,
|
||||
Err(RpcError::Call(CallError::Custom(err))) if err.message().contains("Session keys are not encoded correctly")
|
||||
Err(RpcError::Call(err)) if err.message().contains("Session keys are not encoded correctly")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user