mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-24 02:01: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:
@@ -29,27 +29,21 @@ use crate::{
|
||||
},
|
||||
SubscriptionTaskExecutor,
|
||||
};
|
||||
use jsonrpsee::{
|
||||
core::async_trait,
|
||||
types::{
|
||||
error::{CallError, ErrorObject},
|
||||
SubscriptionResult,
|
||||
},
|
||||
SubscriptionSink,
|
||||
};
|
||||
use jsonrpsee::{core::async_trait, types::error::ErrorObject, PendingSubscriptionSink};
|
||||
use sc_transaction_pool_api::{
|
||||
error::IntoPoolError, BlockHash, TransactionFor, TransactionPool, TransactionSource,
|
||||
TransactionStatus,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
use sc_rpc::utils::pipe_from_stream;
|
||||
use sp_api::ProvideRuntimeApi;
|
||||
use sp_blockchain::HeaderBackend;
|
||||
use sp_core::Bytes;
|
||||
use sp_runtime::traits::Block as BlockT;
|
||||
|
||||
use codec::Decode;
|
||||
use futures::{FutureExt, StreamExt, TryFutureExt};
|
||||
use futures::{StreamExt, TryFutureExt};
|
||||
|
||||
/// An API for transaction RPC calls.
|
||||
pub struct Transaction<Pool, Client> {
|
||||
@@ -90,52 +84,53 @@ where
|
||||
<Pool::Block as BlockT>::Hash: Unpin,
|
||||
Client: HeaderBackend<Pool::Block> + ProvideRuntimeApi<Pool::Block> + Send + Sync + 'static,
|
||||
{
|
||||
fn submit_and_watch(&self, mut sink: SubscriptionSink, xt: Bytes) -> SubscriptionResult {
|
||||
// This is the only place where the RPC server can return an error for this
|
||||
// subscription. Other defects must be signaled as events to the sink.
|
||||
let decoded_extrinsic = match TransactionFor::<Pool>::decode(&mut &xt[..]) {
|
||||
Ok(decoded_extrinsic) => decoded_extrinsic,
|
||||
Err(e) => {
|
||||
let err = CallError::Custom(ErrorObject::owned(
|
||||
BAD_FORMAT,
|
||||
format!("Extrinsic has invalid format: {}", e),
|
||||
None::<()>,
|
||||
));
|
||||
let _ = sink.reject(err);
|
||||
return Ok(())
|
||||
},
|
||||
};
|
||||
|
||||
let best_block_hash = self.client.info().best_hash;
|
||||
|
||||
let submit = self
|
||||
.pool
|
||||
.submit_and_watch(best_block_hash, TX_SOURCE, decoded_extrinsic)
|
||||
.map_err(|e| {
|
||||
e.into_pool_error()
|
||||
.map(Error::from)
|
||||
.unwrap_or_else(|e| Error::Verification(Box::new(e)))
|
||||
});
|
||||
fn submit_and_watch(&self, pending: PendingSubscriptionSink, xt: Bytes) {
|
||||
let client = self.client.clone();
|
||||
let pool = self.pool.clone();
|
||||
|
||||
let fut = async move {
|
||||
// This is the only place where the RPC server can return an error for this
|
||||
// subscription. Other defects must be signaled as events to the sink.
|
||||
let decoded_extrinsic = match TransactionFor::<Pool>::decode(&mut &xt[..]) {
|
||||
Ok(decoded_extrinsic) => decoded_extrinsic,
|
||||
Err(e) => {
|
||||
let err = ErrorObject::owned(
|
||||
BAD_FORMAT,
|
||||
format!("Extrinsic has invalid format: {}", e),
|
||||
None::<()>,
|
||||
);
|
||||
let _ = pending.reject(err).await;
|
||||
return
|
||||
},
|
||||
};
|
||||
|
||||
let best_block_hash = client.info().best_hash;
|
||||
|
||||
let submit = pool
|
||||
.submit_and_watch(best_block_hash, TX_SOURCE, decoded_extrinsic)
|
||||
.map_err(|e| {
|
||||
e.into_pool_error()
|
||||
.map(Error::from)
|
||||
.unwrap_or_else(|e| Error::Verification(Box::new(e)))
|
||||
});
|
||||
|
||||
match submit.await {
|
||||
Ok(stream) => {
|
||||
let mut state = TransactionState::new();
|
||||
let stream =
|
||||
stream.filter_map(|event| async move { state.handle_event(event) });
|
||||
sink.pipe_from_stream(stream.boxed()).await;
|
||||
stream.filter_map(move |event| async move { state.handle_event(event) });
|
||||
pipe_from_stream(pending, stream.boxed()).await;
|
||||
},
|
||||
Err(err) => {
|
||||
// We have not created an `Watcher` for the tx. Make sure the
|
||||
// error is still propagated as an event.
|
||||
let event: TransactionEvent<<Pool::Block as BlockT>::Hash> = err.into();
|
||||
sink.pipe_from_stream(futures::stream::once(async { event }).boxed()).await;
|
||||
pipe_from_stream(pending, futures::stream::once(async { event }).boxed()).await;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed());
|
||||
Ok(())
|
||||
sc_rpc::utils::spawn_subscription_task(&self.executor, fut);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user