mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-22 22:34:45 +00:00
Adjust beacon node timeouts for validator client HTTP requests (#2352)
## Issue Addressed Resolves #2313 ## Proposed Changes Provide `BeaconNodeHttpClient` with a dedicated `Timeouts` struct. This will allow granular adjustment of the timeout duration for different calls made from the VC to the BN. These can either be a constant value, or as a ratio of the slot duration. Improve timeout performance by using these adjusted timeout duration's only whenever a fallback endpoint is available. Add a CLI flag called `use-long-timeouts` to revert to the old behavior. ## Additional Info Additionally set the default `BeaconNodeHttpClient` timeouts to the be the slot duration of the network, rather than a constant 12 seconds. This will allow it to adjust to different network specifications. Co-authored-by: Paul Hauner <paul@paulhauner.com>
This commit is contained in:
@@ -24,6 +24,7 @@ use serde::{de::DeserializeOwned, Serialize};
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::iter::Iterator;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
@@ -76,12 +77,34 @@ impl fmt::Display for Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct to define a variety of different timeouts for different validator tasks to ensure
|
||||
/// proper fallback behaviour.
|
||||
#[derive(Clone)]
|
||||
pub struct Timeouts {
|
||||
pub attestation: Duration,
|
||||
pub attester_duties: Duration,
|
||||
pub proposal: Duration,
|
||||
pub proposer_duties: Duration,
|
||||
}
|
||||
|
||||
impl Timeouts {
|
||||
pub fn set_all(timeout: Duration) -> Self {
|
||||
Timeouts {
|
||||
attestation: timeout,
|
||||
attester_duties: timeout,
|
||||
proposal: timeout,
|
||||
proposer_duties: timeout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around `reqwest::Client` which provides convenience methods for interfacing with a
|
||||
/// Lighthouse Beacon Node HTTP server (`http_api`).
|
||||
#[derive(Clone)]
|
||||
pub struct BeaconNodeHttpClient {
|
||||
client: reqwest::Client,
|
||||
server: SensitiveUrl,
|
||||
timeouts: Timeouts,
|
||||
}
|
||||
|
||||
impl fmt::Display for BeaconNodeHttpClient {
|
||||
@@ -97,15 +120,24 @@ impl AsRef<str> for BeaconNodeHttpClient {
|
||||
}
|
||||
|
||||
impl BeaconNodeHttpClient {
|
||||
pub fn new(server: SensitiveUrl) -> Self {
|
||||
pub fn new(server: SensitiveUrl, timeouts: Timeouts) -> Self {
|
||||
Self {
|
||||
client: reqwest::Client::new(),
|
||||
server,
|
||||
timeouts,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_components(server: SensitiveUrl, client: reqwest::Client) -> Self {
|
||||
Self { client, server }
|
||||
pub fn from_components(
|
||||
server: SensitiveUrl,
|
||||
client: reqwest::Client,
|
||||
timeouts: Timeouts,
|
||||
) -> Self {
|
||||
Self {
|
||||
client,
|
||||
server,
|
||||
timeouts,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the path with the standard `/eth1/v1` prefix applied.
|
||||
@@ -130,6 +162,26 @@ impl BeaconNodeHttpClient {
|
||||
.map_err(Error::Reqwest)
|
||||
}
|
||||
|
||||
/// Perform a HTTP GET request with a custom timeout.
|
||||
async fn get_with_timeout<T: DeserializeOwned, U: IntoUrl>(
|
||||
&self,
|
||||
url: U,
|
||||
timeout: Duration,
|
||||
) -> Result<T, Error> {
|
||||
let response = self
|
||||
.client
|
||||
.get(url)
|
||||
.timeout(timeout)
|
||||
.send()
|
||||
.await
|
||||
.map_err(Error::Reqwest)?;
|
||||
ok_or_error(response)
|
||||
.await?
|
||||
.json()
|
||||
.await
|
||||
.map_err(Error::Reqwest)
|
||||
}
|
||||
|
||||
/// Perform a HTTP GET request, returning `None` on a 404 error.
|
||||
async fn get_opt<T: DeserializeOwned, U: IntoUrl>(&self, url: U) -> Result<Option<T>, Error> {
|
||||
let response = self.client.get(url).send().await.map_err(Error::Reqwest)?;
|
||||
@@ -145,6 +197,31 @@ impl BeaconNodeHttpClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform a HTTP GET request with a custom timeout, returning `None` on a 404 error.
|
||||
async fn get_opt_with_timeout<T: DeserializeOwned, U: IntoUrl>(
|
||||
&self,
|
||||
url: U,
|
||||
timeout: Duration,
|
||||
) -> Result<Option<T>, Error> {
|
||||
let response = self
|
||||
.client
|
||||
.get(url)
|
||||
.timeout(timeout)
|
||||
.send()
|
||||
.await
|
||||
.map_err(Error::Reqwest)?;
|
||||
match ok_or_error(response).await {
|
||||
Ok(resp) => resp.json().await.map(Option::Some).map_err(Error::Reqwest),
|
||||
Err(err) => {
|
||||
if err.status() == Some(StatusCode::NOT_FOUND) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform a HTTP GET request using an 'accept' header, returning `None` on a 404 error.
|
||||
pub async fn get_bytes_opt_accept_header<U: IntoUrl>(
|
||||
&self,
|
||||
@@ -189,15 +266,36 @@ impl BeaconNodeHttpClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Perform a HTTP POST request, returning a JSON response.
|
||||
async fn post_with_response<T: DeserializeOwned, U: IntoUrl, V: Serialize>(
|
||||
/// Perform a HTTP POST request with a custom timeout.
|
||||
async fn post_with_timeout<T: Serialize, U: IntoUrl>(
|
||||
&self,
|
||||
url: U,
|
||||
body: &T,
|
||||
timeout: Duration,
|
||||
) -> Result<(), Error> {
|
||||
let response = self
|
||||
.client
|
||||
.post(url)
|
||||
.timeout(timeout)
|
||||
.json(body)
|
||||
.send()
|
||||
.await
|
||||
.map_err(Error::Reqwest)?;
|
||||
ok_or_error(response).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Perform a HTTP POST request with a custom timeout, returning a JSON response.
|
||||
async fn post_with_timeout_and_response<T: DeserializeOwned, U: IntoUrl, V: Serialize>(
|
||||
&self,
|
||||
url: U,
|
||||
body: &V,
|
||||
timeout: Duration,
|
||||
) -> Result<T, Error> {
|
||||
let response = self
|
||||
.client
|
||||
.post(url)
|
||||
.timeout(timeout)
|
||||
.json(body)
|
||||
.send()
|
||||
.await
|
||||
@@ -468,7 +566,8 @@ impl BeaconNodeHttpClient {
|
||||
.push("beacon")
|
||||
.push("blocks");
|
||||
|
||||
self.post(path, block).await?;
|
||||
self.post_with_timeout(path, block, self.timeouts.proposal)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -567,6 +666,7 @@ impl BeaconNodeHttpClient {
|
||||
let response = self
|
||||
.client
|
||||
.post(path)
|
||||
.timeout(self.timeouts.attestation)
|
||||
.json(attestations)
|
||||
.send()
|
||||
.await
|
||||
@@ -929,7 +1029,8 @@ impl BeaconNodeHttpClient {
|
||||
.push("proposer")
|
||||
.push(&epoch.to_string());
|
||||
|
||||
self.get(path).await
|
||||
self.get_with_timeout(path, self.timeouts.proposer_duties)
|
||||
.await
|
||||
}
|
||||
|
||||
/// `GET validator/blocks/{slot}`
|
||||
@@ -975,10 +1076,10 @@ impl BeaconNodeHttpClient {
|
||||
.append_pair("slot", &slot.to_string())
|
||||
.append_pair("committee_index", &committee_index.to_string());
|
||||
|
||||
self.get(path).await
|
||||
self.get_with_timeout(path, self.timeouts.attestation).await
|
||||
}
|
||||
|
||||
/// `GET validator/attestation_attestation?slot,attestation_data_root`
|
||||
/// `GET validator/aggregate_attestation?slot,attestation_data_root`
|
||||
pub async fn get_validator_aggregate_attestation<T: EthSpec>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
@@ -998,7 +1099,8 @@ impl BeaconNodeHttpClient {
|
||||
&format!("{:?}", attestation_data_root),
|
||||
);
|
||||
|
||||
self.get_opt(path).await
|
||||
self.get_opt_with_timeout(path, self.timeouts.attestation)
|
||||
.await
|
||||
}
|
||||
|
||||
/// `POST validator/duties/attester/{epoch}`
|
||||
@@ -1016,7 +1118,8 @@ impl BeaconNodeHttpClient {
|
||||
.push("attester")
|
||||
.push(&epoch.to_string());
|
||||
|
||||
self.post_with_response(path, &indices).await
|
||||
self.post_with_timeout_and_response(path, &indices, self.timeouts.attester_duties)
|
||||
.await
|
||||
}
|
||||
|
||||
/// `POST validator/aggregate_and_proofs`
|
||||
@@ -1034,6 +1137,7 @@ impl BeaconNodeHttpClient {
|
||||
let response = self
|
||||
.client
|
||||
.post(path)
|
||||
.timeout(self.timeouts.attestation)
|
||||
.json(aggregates)
|
||||
.send()
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user