Skip to content

Commit b22ebc4

Browse files
committed
[feature] #3900: fetch_size query parameter
Signed-off-by: Daniil Polyakov <[email protected]>
1 parent 97ed585 commit b22ebc4

File tree

23 files changed

+252
-113
lines changed

23 files changed

+252
-113
lines changed

Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ iroha_genesis = { workspace = true }
5959
iroha_wasm_builder = { workspace = true }
6060

6161

62+
derive_more = { workspace = true }
6263
async-trait = { workspace = true }
6364
color-eyre = { workspace = true }
6465
eyre = { workspace = true }

cli/src/torii/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,9 @@ impl Error {
105105
QueryFailed(query_error)
106106
| InstructionFailed(InstructionExecutionError::Query(query_error)) => match query_error
107107
{
108-
Evaluate(_) | Conversion(_) | UnknownCursor => StatusCode::BAD_REQUEST,
108+
Evaluate(_) | Conversion(_) | UnknownCursor | FetchSizeTooBig => {
109+
StatusCode::BAD_REQUEST
110+
}
109111
Signature(_) => StatusCode::UNAUTHORIZED,
110112
Find(_) => StatusCode::NOT_FOUND,
111113
},

cli/src/torii/routing.rs

+12-12
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
// FIXME: This can't be fixed, because one trait in `warp` is private.
66
#![allow(opaque_hidden_inferred_bound)]
77

8-
use std::num::NonZeroUsize;
9-
108
use eyre::{eyre, WrapErr};
119
use futures::TryStreamExt;
1210
use iroha_config::{
@@ -45,11 +43,13 @@ fn client_query_request(
4543
body::versioned::<SignedQuery>()
4644
.and(sorting())
4745
.and(paginate())
48-
.and_then(|signed_query, sorting, pagination| async move {
46+
.and(fetch_size())
47+
.and_then(|signed_query, sorting, pagination, fetch_size| async move {
4948
Result::<_, std::convert::Infallible>::Ok(http::ClientQueryRequest::query(
5049
signed_query,
5150
sorting,
5251
pagination,
52+
fetch_size,
5353
))
5454
})
5555
.or(cursor().and_then(|cursor| async move {
@@ -73,6 +73,11 @@ fn paginate() -> impl warp::Filter<Extract = (Pagination,), Error = warp::Reject
7373
warp::query()
7474
}
7575

76+
/// Filter for warp which extracts fetch size
77+
fn fetch_size() -> impl warp::Filter<Extract = (FetchSize,), Error = warp::Rejection> + Copy {
78+
warp::query()
79+
}
80+
7681
#[iroha_futures::telemetry_future]
7782
async fn handle_instructions(
7883
queue: Arc<Queue>,
@@ -101,7 +106,6 @@ async fn handle_instructions(
101106
async fn handle_queries(
102107
live_query_store: LiveQueryStoreHandle,
103108
sumeragi: SumeragiHandle,
104-
fetch_size: NonZeroUsize,
105109

106110
query_request: http::ClientQueryRequest,
107111
) -> Result<Scale<BatchedResponse<Value>>> {
@@ -110,11 +114,12 @@ async fn handle_queries(
110114
query: signed_query,
111115
sorting,
112116
pagination,
117+
fetch_size,
113118
}) => sumeragi.apply_wsv(|wsv| {
114119
let valid_query = ValidQueryRequest::validate(signed_query, wsv)?;
115120
let query_output = valid_query.execute(wsv)?;
116121
live_query_store
117-
.handle_query_output(query_output, fetch_size, &sorting, pagination)
122+
.handle_query_output(query_output, &sorting, pagination, fetch_size)
118123
.map_err(ValidationFail::from)
119124
}),
120125
QueryRequest::Cursor(cursor) => live_query_store
@@ -477,15 +482,10 @@ impl Torii {
477482
))
478483
.and(body::versioned()),
479484
)
480-
.or(endpoint4(
485+
.or(endpoint3(
481486
handle_queries,
482487
warp::path(uri::QUERY)
483-
.and(add_state!(
484-
self.query_service,
485-
self.sumeragi,
486-
NonZeroUsize::try_from(self.iroha_cfg.torii.fetch_size)
487-
.expect("u64 should always fit into usize")
488-
))
488+
.and(add_state!(self.query_service, self.sumeragi,))
489489
.and(client_query_request()),
490490
))
491491
.or(endpoint2(

client/src/client.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,15 @@ pub struct ResultSet<T> {
245245
client_cursor: usize,
246246
}
247247

248+
impl<T> ResultSet<T> {
249+
/// Get the length of the batch returned by Iroha.
250+
///
251+
/// This is controlled by `fetch_size` parameter of the query.
252+
pub fn batch_len(&self) -> usize {
253+
self.iter.len()
254+
}
255+
}
256+
248257
impl<T: Clone> Iterator for ResultSet<T>
249258
where
250259
Vec<T>: QueryOutput,
@@ -374,6 +383,7 @@ impl QueryRequest {
374383
query: Vec::default(),
375384
sorting: Sorting::default(),
376385
pagination: Pagination::default(),
386+
fetch_size: FetchSize::default(),
377387
},
378388
),
379389
}
@@ -389,6 +399,7 @@ impl QueryRequest {
389399
iroha_data_model::query::QueryRequest::Query(query_with_params) => builder
390400
.params(query_with_params.sorting().clone().into_query_parameters())
391401
.params(query_with_params.pagination().into_query_parameters())
402+
.params(query_with_params.fetch_size().into_query_parameters())
392403
.body(query_with_params.query().clone()),
393404
iroha_data_model::query::QueryRequest::Cursor(cursor) => {
394405
builder.params(Vec::from(cursor))
@@ -798,6 +809,7 @@ impl Client {
798809
filter: PredicateBox,
799810
pagination: Pagination,
800811
sorting: Sorting,
812+
fetch_size: FetchSize,
801813
) -> Result<(DefaultRequestBuilder, QueryResponseHandler<R::Output>)>
802814
where
803815
<R::Output as TryFrom<Value>>::Error: Into<eyre::Error>,
@@ -809,7 +821,9 @@ impl Client {
809821
torii_url: self.torii_url.clone(),
810822
headers: self.headers.clone(),
811823
request: iroha_data_model::query::QueryRequest::Query(
812-
iroha_data_model::query::QueryWithParameters::new(request, sorting, pagination),
824+
iroha_data_model::query::QueryWithParameters::new(
825+
request, sorting, pagination, fetch_size,
826+
),
813827
),
814828
};
815829

@@ -827,6 +841,7 @@ impl Client {
827841
&self,
828842
request: R,
829843
pagination: Pagination,
844+
fetch_size: FetchSize,
830845
sorting: Sorting,
831846
filter: PredicateBox,
832847
) -> QueryResult<<R::Output as QueryOutput>::Target>
@@ -836,7 +851,7 @@ impl Client {
836851
{
837852
iroha_logger::trace!(?request, %pagination, ?sorting, ?filter);
838853
let (req, mut resp_handler) =
839-
self.prepare_query_request::<R>(request, filter, pagination, sorting)?;
854+
self.prepare_query_request::<R>(request, filter, pagination, sorting, fetch_size)?;
840855

841856
let response = req.build()?.send()?;
842857
let value = resp_handler.handle(&response)?;

client/src/query_builder.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::fmt::Debug;
22

33
use iroha_data_model::{
44
predicate::PredicateBox,
5-
query::{sorting::Sorting, Pagination, Query},
5+
query::{sorting::Sorting, FetchSize, Pagination, Query},
66
Value,
77
};
88

@@ -14,6 +14,7 @@ pub struct QueryRequestBuilder<'a, R> {
1414
pagination: Pagination,
1515
filter: PredicateBox,
1616
sorting: Sorting,
17+
fetch_size: FetchSize,
1718
}
1819

1920
impl<'a, R> QueryRequestBuilder<'a, R>
@@ -29,6 +30,7 @@ where
2930
pagination: Pagination::default(),
3031
sorting: Sorting::default(),
3132
filter: PredicateBox::default(),
33+
fetch_size: FetchSize::default(),
3234
}
3335
}
3436

@@ -47,10 +49,16 @@ where
4749
self
4850
}
4951

52+
pub fn with_fetch_size(mut self, fetch_size: FetchSize) -> Self {
53+
self.fetch_size = fetch_size;
54+
self
55+
}
56+
5057
pub fn execute(self) -> QueryResult<<R::Output as QueryOutput>::Target> {
5158
self.client.request_with_filter_and_pagination_and_sorting(
5259
self.request,
5360
self.pagination,
61+
self.fetch_size,
5462
self.sorting,
5563
self.filter,
5664
)
+34-10
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,16 @@
11
use std::num::{NonZeroU32, NonZeroU64};
22

33
use eyre::Result;
4-
use iroha_client::client::{asset, QueryResult};
4+
use iroha_client::client::{asset, Client, QueryResult};
55
use iroha_data_model::{asset::AssetDefinition, prelude::*, query::Pagination};
66
use test_network::*;
77

88
#[test]
9-
fn client_add_asset_quantity_to_existing_asset_should_increase_asset_amount() -> Result<()> {
9+
fn limits_should_work() -> Result<()> {
1010
let (_rt, _peer, client) = <PeerBuilder>::new().with_port(10_690).start_with_runtime();
1111
wait_for_genesis_committed(&vec![client.clone()], 0);
1212

13-
let register: Vec<InstructionExpr> = ('a'..='z') // This is a subtle mistake, I'm glad we can lint it now.
14-
.map(|c| c.to_string())
15-
.map(|name| (name + "#wonderland").parse().expect("Valid"))
16-
.map(|asset_definition_id| {
17-
RegisterExpr::new(AssetDefinition::quantity(asset_definition_id)).into()
18-
})
19-
.collect();
20-
client.submit_all_blocking(register)?;
13+
register_assets(&client)?;
2114

2215
let vec = &client
2316
.build_query(asset::all_definitions())
@@ -30,3 +23,34 @@ fn client_add_asset_quantity_to_existing_asset_should_increase_asset_amount() ->
3023
assert_eq!(vec.len(), 5);
3124
Ok(())
3225
}
26+
27+
#[test]
28+
fn fetch_size_should_work() -> Result<()> {
29+
let (_rt, _peer, client) = <PeerBuilder>::new().with_port(11_120).start_with_runtime();
30+
wait_for_genesis_committed(&vec![client.clone()], 0);
31+
32+
register_assets(&client)?;
33+
34+
let iter = client
35+
.build_query(asset::all_definitions())
36+
.with_pagination(Pagination {
37+
limit: NonZeroU32::new(20),
38+
start: NonZeroU64::new(0),
39+
})
40+
.with_fetch_size(FetchSize::new(Some(NonZeroU32::new(12).expect("Valid"))))
41+
.execute()?;
42+
assert_eq!(iter.batch_len(), 12);
43+
Ok(())
44+
}
45+
46+
fn register_assets(client: &Client) -> Result<()> {
47+
let register: Vec<InstructionExpr> = ('a'..='z')
48+
.map(|c| c.to_string())
49+
.map(|name| (name + "#wonderland").parse().expect("Valid"))
50+
.map(|asset_definition_id| {
51+
RegisterExpr::new(AssetDefinition::quantity(asset_definition_id)).into()
52+
})
53+
.collect();
54+
let _ = client.submit_all_blocking(register)?;
55+
Ok(())
56+
}

client/tests/integration/permissions.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ fn genesis_transactions_are_validated() {
3232
// Starting peer
3333
let (_rt, _peer, test_client) = <PeerBuilder>::new()
3434
.with_genesis(genesis)
35-
.with_port(11_100)
35+
.with_port(11_110)
3636
.start_with_runtime();
3737

3838
// Checking that peer contains no blocks multiple times
+26
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
1+
use iroha_client::client::{self, ClientQueryError};
2+
use iroha_data_model::{
3+
query::{error::QueryExecutionFail, FetchSize, MAX_FETCH_SIZE},
4+
ValidationFail,
5+
};
6+
use test_network::*;
7+
18
mod account;
29
mod asset;
310
mod role;
11+
12+
#[test]
13+
fn too_big_fetch_size_is_not_allowed() {
14+
let (_rt, _peer, client) = <PeerBuilder>::new().with_port(11_130).start_with_runtime();
15+
wait_for_genesis_committed(&[client.clone()], 0);
16+
17+
let err = client
18+
.build_query(client::asset::all())
19+
.with_fetch_size(FetchSize::new(Some(MAX_FETCH_SIZE.checked_add(1).unwrap())))
20+
.execute()
21+
.expect_err("Should fail");
22+
23+
assert!(matches!(
24+
err,
25+
ClientQueryError::Validation(ValidationFail::QueryFailed(
26+
QueryExecutionFail::FetchSizeTooBig
27+
))
28+
));
29+
}

client/tests/integration/sorting.rs

+10-14
Original file line numberDiff line numberDiff line change
@@ -256,14 +256,12 @@ fn correct_sorting_of_entities() {
256256
.expect("Valid");
257257

258258
let res = test_client
259-
.request_with_filter_and_pagination_and_sorting(
260-
client::domain::all(),
261-
Pagination::default(),
262-
Sorting::by_metadata_key(sort_by_metadata_key.clone()),
263-
PredicateBox::new(value::ValuePredicate::Identifiable(
264-
string::StringPredicate::starts_with("neverland"),
265-
)),
266-
)
259+
.build_query(client::domain::all())
260+
.with_sorting(Sorting::by_metadata_key(sort_by_metadata_key.clone()))
261+
.with_filter(PredicateBox::new(value::ValuePredicate::Identifiable(
262+
string::StringPredicate::starts_with("neverland"),
263+
)))
264+
.execute()
267265
.expect("Valid")
268266
.collect::<QueryResult<Vec<_>>>()
269267
.expect("Valid");
@@ -305,12 +303,10 @@ fn correct_sorting_of_entities() {
305303
string::StringPredicate::starts_with("neverland_"),
306304
));
307305
let res = test_client
308-
.request_with_filter_and_pagination_and_sorting(
309-
client::domain::all(),
310-
Pagination::default(),
311-
Sorting::by_metadata_key(sort_by_metadata_key),
312-
filter,
313-
)
306+
.build_query(client::domain::all())
307+
.with_sorting(Sorting::by_metadata_key(sort_by_metadata_key))
308+
.with_filter(filter)
309+
.execute()
314310
.expect("Valid")
315311
.collect::<QueryResult<Vec<_>>>()
316312
.expect("Valid");

config/iroha_test_config.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@
4343
"P2P_ADDR": "127.0.0.1:1337",
4444
"API_URL": "127.0.0.1:8080",
4545
"MAX_TRANSACTION_SIZE": 32768,
46-
"MAX_CONTENT_LEN": 16384000,
47-
"FETCH_SIZE": 10
46+
"MAX_CONTENT_LEN": 16384000
4847
},
4948
"BLOCK_SYNC": {
5049
"GOSSIP_PERIOD_MS": 10000,

0 commit comments

Comments
 (0)