fix: retry with next resolver on failure for both strategies
Round-robin now retries with the next resolver when one fails, instead of returning the error immediately. Also reduced connect timeout to 3s and total timeout to 5s for faster failover.
This commit is contained in:
@@ -37,7 +37,8 @@ impl DohClient {
|
||||
/// Create a new DoH client with the given upstream resolvers.
|
||||
pub fn new(resolvers: Vec<ResolverEntry>) -> Self {
|
||||
let http = Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(10))
|
||||
.connect_timeout(std::time::Duration::from_secs(3))
|
||||
.timeout(std::time::Duration::from_secs(5))
|
||||
.build()
|
||||
.expect("failed to build HTTP client");
|
||||
Self { http, resolvers }
|
||||
@@ -45,7 +46,8 @@ impl DohClient {
|
||||
|
||||
/// Forward a DNS wire-format query using the given strategy.
|
||||
///
|
||||
/// For round-robin, `index` is used to select the resolver.
|
||||
/// Both strategies retry with the next resolver on failure.
|
||||
/// For round-robin, `index` picks the starting resolver.
|
||||
/// For failover, resolvers are tried in order starting from `index`.
|
||||
pub async fn forward(
|
||||
&self,
|
||||
@@ -54,24 +56,14 @@ impl DohClient {
|
||||
index: usize,
|
||||
) -> Result<Vec<u8>, UpstreamError> {
|
||||
match strategy {
|
||||
Strategy::RoundRobin => self.forward_to(query_bytes, index).await,
|
||||
Strategy::Failover => self.forward_failover(query_bytes, index).await,
|
||||
Strategy::RoundRobin => self.forward_with_retry(query_bytes, index).await,
|
||||
Strategy::Failover => self.forward_with_retry(query_bytes, index).await,
|
||||
}
|
||||
}
|
||||
|
||||
/// Forward to a specific resolver by index (wraps around).
|
||||
async fn forward_to(
|
||||
&self,
|
||||
query_bytes: &[u8],
|
||||
resolver_index: usize,
|
||||
) -> Result<Vec<u8>, UpstreamError> {
|
||||
let resolver = &self.resolvers[resolver_index % self.resolvers.len()];
|
||||
self.send_to(query_bytes, resolver).await
|
||||
}
|
||||
|
||||
/// Try resolvers in order starting from `start_index`, returning the
|
||||
/// first successful response.
|
||||
async fn forward_failover(
|
||||
/// Try resolvers starting from `start_index`, retrying with the next
|
||||
/// on failure. This provides resilience for both strategies.
|
||||
async fn forward_with_retry(
|
||||
&self,
|
||||
query_bytes: &[u8],
|
||||
start_index: usize,
|
||||
@@ -85,13 +77,13 @@ impl DohClient {
|
||||
match self.send_to(query_bytes, resolver).await {
|
||||
Ok(bytes) => return Ok(bytes),
|
||||
Err(e) => {
|
||||
warn!(resolver = %resolver.name, error = %e, "failover: resolver failed, trying next");
|
||||
warn!(resolver = %resolver.name, error = %e, "resolver failed, trying next");
|
||||
last_err = Some(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(last_err.expect("failover: no resolvers configured"))
|
||||
Err(last_err.expect("no resolvers configured"))
|
||||
}
|
||||
|
||||
/// Send a query to a single resolver.
|
||||
|
||||
Reference in New Issue
Block a user