## Issue Addressed part of #1883 ## Proposed Changes Adds a new cli argument `--eth1-endpoints` that can be used instead of `--eth1-endpoint` to specify a comma-separated list of endpoints. If the first endpoint returns an error for some request the other endpoints are tried in the given order. ## Additional Info Currently if the first endpoint fails the fallbacks are used silently (except for `try_fallback_test_endpoint` that is used in `do_update` which logs a `WARN` for each endpoint that is not reachable). A question is if we should add more logs so that the user gets warned if his main endpoint is for example just slow and sometimes hits timeouts.
59 lines
1.5 KiB
Rust
59 lines
1.5 KiB
Rust
use itertools::{join, zip};
|
|
use std::fmt::{Debug, Display};
|
|
use std::future::Future;
|
|
|
|
pub struct Fallback<T> {
|
|
servers: Vec<T>,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
pub enum FallbackError<E> {
|
|
AllErrored(Vec<E>),
|
|
}
|
|
|
|
impl<T> Fallback<T> {
|
|
pub fn new(servers: Vec<T>) -> Self {
|
|
Self { servers }
|
|
}
|
|
|
|
/// Return the first successful result or all errors encountered.
|
|
pub async fn first_success<'a, F, O, E, R>(&'a self, func: F) -> Result<O, FallbackError<E>>
|
|
where
|
|
F: Fn(&'a T) -> R,
|
|
R: Future<Output = Result<O, E>>,
|
|
{
|
|
let mut errors = vec![];
|
|
for server in &self.servers {
|
|
match func(server).await {
|
|
Ok(val) => return Ok(val),
|
|
Err(e) => errors.push(e),
|
|
}
|
|
}
|
|
Err(FallbackError::AllErrored(errors))
|
|
}
|
|
|
|
pub fn map_format_error<'a, E, F, S>(&'a self, f: F, error: &FallbackError<E>) -> String
|
|
where
|
|
F: FnMut(&'a T) -> &'a S,
|
|
S: Display + 'a,
|
|
E: Debug,
|
|
{
|
|
match error {
|
|
FallbackError::AllErrored(v) => format!(
|
|
"All fallback errored: {}",
|
|
join(
|
|
zip(self.servers.iter().map(f), v.iter())
|
|
.map(|(server, error)| format!("{} => {:?}", server, error)),
|
|
", "
|
|
)
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: Display> Fallback<T> {
|
|
pub fn format_error<E: Debug>(&self, error: &FallbackError<E>) -> String {
|
|
self.map_format_error(|s| s, error)
|
|
}
|
|
}
|