Skip to content

Commit

Permalink
rpc v2: backpressure chainHead_v1_storage (#5741)
Browse files Browse the repository at this point in the history
Close #5589

This PR makes it possible for `rpc_v2::Storage::query_iter_paginated` to
be "backpressured" which is achieved by having a channel where the
result is sent back and when this channel is "full" we pause the
iteration.

The chainHead_follow has an internal channel which doesn't represent the
actual connection and that is set to a very small number (16). Recall
that the JSON-RPC server has a dedicate buffer for each connection by
default of 64.

#### Notes

- Because `archive_storage` also depends on
`rpc_v2::Storage::query_iter_paginated` I had to tweak the method to
support limits as well. The reason is that archive_storage won't get
backpressured properly because it's not an subscription. (it would much
easier if it would be a subscription in rpc v2 spec because nothing
against querying huge amount storage keys)
- `query_iter_paginated` doesn't necessarily return the storage "in
order" such as
- `query_iter_paginated(vec![("key1", hash), ("key2", value)], ...)`
could return them in arbitrary order because it's wrapped in
FuturesUnordered but I could change that if we want to process it
inorder (it's slower)
- there is technically no limit on the number of storage queries in each
`chainHead_v1_storage call` rather than the rpc max message limit which
10MB and only allowed to max 16 calls `chainHead_v1_x` concurrently
(this should be fine)

#### Benchmarks using subxt on localhost

- Iterate over 10 accounts on westend-dev -> ~2-3x faster 
- Fetch 1024 storage values (i.e, not descedant values) -> ~50x faster
- Fetch 1024 descendant values -> ~500x faster

The reason for this is because as Josep explained in the issue is that
one is only allowed query five storage items per call and clients has
make lots of calls to drive it forward..

---------

Co-authored-by: command-bot <>
Co-authored-by: James Wilson <james@jsdw.me>
  • Loading branch information
niklasad1 and jsdw authored Oct 3, 2024
1 parent 8614dc0 commit e865ffc
Show file tree
Hide file tree
Showing 13 changed files with 369 additions and 485 deletions.
28 changes: 14 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1322,7 +1322,7 @@ tikv-jemalloc-ctl = { version = "0.5.0" }
tikv-jemallocator = { version = "0.5.0" }
time = { version = "0.3" }
tiny-keccak = { version = "2.0.2" }
tokio = { version = "1.37.0", default-features = false }
tokio = { version = "1.40.0", default-features = false }
tokio-retry = { version = "0.3.0" }
tokio-stream = { version = "0.1.14" }
tokio-test = { version = "0.4.2" }
Expand Down
25 changes: 25 additions & 0 deletions prdoc/pr_5741.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json

title: make RPC endpoint `chainHead_v1_storage` faster

doc:
- audience: Node Operator
description: |
The RPC endpoint `chainHead_v1_storage` now relies solely on backpressure to
determine how quickly to serve back values instead of handing back a fixed number
of entries and then expecting the client to ask for more. This should improve the
throughput for bigger storage queries significantly.

Benchmarks using subxt on localhost:
- Iterate over 10 accounts on westend-dev -> ~2-3x faster
- Fetch 1024 storage values (i.e, not descedant values) -> ~50x faster
- Fetch 1024 descendant values -> ~500x faster

crates:
- name: sc-rpc-spec-v2
bump: major
- name: sc-rpc-server
bump: patch
- name: sc-service
bump: major
5 changes: 3 additions & 2 deletions substrate/client/rpc-servers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,9 @@ where
),
};

let rpc_middleware =
RpcServiceBuilder::new().option_layer(middleware_layer.clone());
let rpc_middleware = RpcServiceBuilder::new()
.rpc_logger(1024)
.option_layer(middleware_layer.clone());
let mut svc = service_builder
.set_rpc_middleware(rpc_middleware)
.build(methods, stop_handle);
Expand Down
3 changes: 2 additions & 1 deletion substrate/client/rpc-spec-v2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ sp-rpc = { workspace = true, default-features = true }
sp-blockchain = { workspace = true, default-features = true }
sp-version = { workspace = true, default-features = true }
sc-client-api = { workspace = true, default-features = true }
sc-utils = { workspace = true, default-features = true }
sc-rpc = { workspace = true, default-features = true }
codec = { workspace = true, default-features = true }
thiserror = { workspace = true }
Expand Down Expand Up @@ -56,6 +55,8 @@ sp-externalities = { workspace = true, default-features = true }
sp-maybe-compressed-blob = { workspace = true, default-features = true }
sc-block-builder = { workspace = true, default-features = true }
sc-service = { features = ["test-helpers"], workspace = true, default-features = true }
sc-rpc = { workspace = true, default-features = true, features = ["test-helpers"] }
assert_matches = { workspace = true }
pretty_assertions = { workspace = true }
sc-transaction-pool = { workspace = true, default-features = true }
sc-utils = { workspace = true, default-features = true }
1 change: 1 addition & 0 deletions substrate/client/rpc-spec-v2/src/archive/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ where
self.storage_max_descendant_responses,
self.storage_max_queried_items,
);

Ok(storage_client.handle_query(hash, items, child_trie))
}
}
Loading

0 comments on commit e865ffc

Please sign in to comment.