Skip to content

Commit 0154bec

Browse files
rockbmbxlc
andauthored
Expose per-upstream client timeouts and retries in ClientConfig (#203)
* Expose per-upstream client timeouts and retries in `ClientConfig` `Client::new` already accepts `request_timeout`, `connection_timeout`, and `retries` arguments, but `from_config` hardcodes all three to `None` because `ClientConfig` only exposes `endpoints` and `shuffle_endpoints`. As a result the only way to override the 30s per-upstream request timeout (and the 30s connection timeout, and the default retry count) is to construct `Client` directly in Rust, which isn't reachable from the YAML-driven config. Adds three optional fields to `ClientConfig`: - `request_timeout_seconds` - `connection_timeout_seconds` - `retries` `from_config` plumbs them into `Client::new`. None of the existing defaults change when the fields are omitted. The motivating case is heavy storage queries against slow public RPCs (Acala under load is the case that surfaced this in `polkadot-fellows/runtimes#1180` / `open-web3-stack/polkadot-ecosystem-tests#621`) where 30s per upstream is not enough and Subway exhausts its endpoint cycle without serving a response. * cargo fmt * feat(bench): Add client config options for connection timeout, request timeout, and retries --------- Co-authored-by: Bryan Chen <xlchen1291@gmail.com>
1 parent e490485 commit 0154bec

5 files changed

Lines changed: 36 additions & 4 deletions

File tree

benches/bench/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,9 @@ fn config() -> Config {
217217
format!("ws://{}", SERVER_TWO_ENDPOINT),
218218
],
219219
shuffle_endpoints: false,
220+
connection_timeout_seconds: None,
221+
request_timeout_seconds: None,
222+
retries: None,
220223
}),
221224
server: Some(ServerConfig {
222225
listen_address: SUBWAY_SERVER_ADDR.to_string(),

src/extensions/client/mod.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@ pub struct ClientConfig {
5757
pub endpoints: Vec<String>,
5858
#[serde(default = "bool_true")]
5959
pub shuffle_endpoints: bool,
60+
/// Per-upstream request timeout in seconds. Defaults to 30s if unset.
61+
/// Increase when upstream chains may take longer than 30s to respond to
62+
/// heavy storage queries.
63+
#[serde(default)]
64+
pub request_timeout_seconds: Option<u64>,
65+
/// Per-upstream connection (handshake) timeout in seconds. Defaults to 30s if unset.
66+
#[serde(default)]
67+
pub connection_timeout_seconds: Option<u64>,
68+
/// Number of retries to apply per upstream call before rotating endpoints.
69+
/// Defaults to the internal client default if unset.
70+
#[serde(default)]
71+
pub retries: Option<u32>,
6072
}
6173

6274
fn validate_endpoint(endpoint: &str, _context: &()) -> garde::Result {
@@ -134,13 +146,21 @@ impl Extension for Client {
134146
type Config = ClientConfig;
135147

136148
async fn from_config(config: &Self::Config, _registry: &ExtensionRegistry) -> Result<Self, anyhow::Error> {
137-
if config.shuffle_endpoints {
149+
let request_timeout = config.request_timeout_seconds.map(Duration::from_secs);
150+
let connection_timeout = config.connection_timeout_seconds.map(Duration::from_secs);
151+
let endpoints = if config.shuffle_endpoints {
138152
let mut endpoints = config.endpoints.clone();
139153
endpoints.shuffle(&mut thread_rng());
140-
Ok(Self::new(endpoints, None, None, None)?)
154+
endpoints
141155
} else {
142-
Ok(Self::new(config.endpoints.clone(), None, None, None)?)
143-
}
156+
config.endpoints.clone()
157+
};
158+
Ok(Self::new(
159+
endpoints,
160+
request_timeout,
161+
connection_timeout,
162+
config.retries,
163+
)?)
144164
}
145165
}
146166

src/server.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,9 @@ mod tests {
254254
client: Some(ClientConfig {
255255
endpoints: vec![endpoint],
256256
shuffle_endpoints: false,
257+
request_timeout_seconds: None,
258+
connection_timeout_seconds: None,
259+
retries: None,
257260
}),
258261
server: Some(ServerConfig {
259262
listen_address: "127.0.0.1".to_string(),

src/tests/merge_subscription.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ async fn merge_subscription_works() {
4949
client: Some(ClientConfig {
5050
endpoints: vec![format!("ws://{addr}")],
5151
shuffle_endpoints: false,
52+
request_timeout_seconds: None,
53+
connection_timeout_seconds: None,
54+
retries: None,
5255
}),
5356
server: Some(ServerConfig {
5457
listen_address: "0.0.0.0".to_string(),

src/tests/upstream.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ async fn upstream_error_propagate() {
3131
client: Some(ClientConfig {
3232
endpoints: vec![format!("ws://{addr}")],
3333
shuffle_endpoints: false,
34+
request_timeout_seconds: None,
35+
connection_timeout_seconds: None,
36+
retries: None,
3437
}),
3538
server: Some(ServerConfig {
3639
listen_address: "0.0.0.0".to_string(),

0 commit comments

Comments
 (0)