Skip to content

Commit a3ce719

Browse files
authored
fix(dns-resolve): add a lower-bound TTL for dns refreshing (#3807)
DNS servers may return extremely low TTLs in some cases. When we're polling DNS to power a load balancer, we need to enforce a minimum duration to prevent tight-looping DNS queries. This change adds a 5s minimum time between DNS lookups when resolving control plane components. fixes linkerd/linkerd2#13508
1 parent 135a735 commit a3ce719

File tree

2 files changed

+32
-9
lines changed

2 files changed

+32
-9
lines changed

linkerd/dns/src/lib.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ impl Resolver {
8585
&self,
8686
name: NameRef<'_>,
8787
default_port: u16,
88-
) -> Result<(Vec<net::SocketAddr>, time::Sleep), ResolveError> {
88+
) -> Result<(Vec<net::SocketAddr>, Instant), ResolveError> {
8989
match self.resolve_srv(name).await {
9090
Ok(res) => Ok(res),
9191
Err(srv_error) => {
@@ -108,18 +108,18 @@ impl Resolver {
108108
async fn resolve_a_or_aaaa(
109109
&self,
110110
name: NameRef<'_>,
111-
) -> Result<(Vec<net::IpAddr>, time::Sleep), ARecordError> {
111+
) -> Result<(Vec<net::IpAddr>, Instant), ARecordError> {
112112
debug!(%name, "Resolving an A/AAAA record");
113113
let lookup = self.dns.lookup_ip(name.as_str()).await?;
114-
let valid_until = Instant::from_std(lookup.valid_until());
115114
let ips = lookup.iter().collect::<Vec<_>>();
116-
Ok((ips, time::sleep_until(valid_until)))
115+
let valid_until = Instant::from_std(lookup.valid_until());
116+
Ok((ips, valid_until))
117117
}
118118

119119
async fn resolve_srv(
120120
&self,
121121
name: NameRef<'_>,
122-
) -> Result<(Vec<net::SocketAddr>, time::Sleep), SrvRecordError> {
122+
) -> Result<(Vec<net::SocketAddr>, Instant), SrvRecordError> {
123123
debug!(%name, "Resolving a SRV record");
124124
let srv = self.dns.srv_lookup(name.as_str()).await?;
125125

@@ -128,9 +128,9 @@ impl Resolver {
128128
.into_iter()
129129
.map(Self::srv_to_socket_addr)
130130
.collect::<Result<_, InvalidSrv>>()?;
131-
debug!(ttl = ?valid_until - time::Instant::now(), ?addrs);
131+
debug!(ttl = ?valid_until - Instant::now(), ?addrs);
132132

133-
Ok((addrs, time::sleep_until(valid_until)))
133+
Ok((addrs, valid_until))
134134
}
135135

136136
// XXX We need to convert the SRV records to an IP addr manually,

linkerd/proxy/dns-resolve/src/lib.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ async fn resolution(dns: dns::Resolver, na: NameAddr) -> Result<UpdateStream, Er
8080
trace!("Closed");
8181
return;
8282
}
83-
expiry.await;
83+
sleep_until_expired(expiry).await;
8484

8585
loop {
8686
match dns.resolve_addrs(na.name().as_ref(), na.port()).await {
@@ -91,7 +91,7 @@ async fn resolution(dns: dns::Resolver, na: NameAddr) -> Result<UpdateStream, Er
9191
trace!("Closed");
9292
return;
9393
}
94-
expiry.await;
94+
sleep_until_expired(expiry).await;
9595
}
9696
Err(error) => {
9797
debug!(%error);
@@ -107,3 +107,26 @@ async fn resolution(dns: dns::Resolver, na: NameAddr) -> Result<UpdateStream, Er
107107

108108
Ok(Box::pin(ReceiverStream::new(rx)))
109109
}
110+
111+
/// Sleep for the provided [`Duration`][tokio::time::Duration].
112+
///
113+
/// NB: This enforces a lower-bound for TTL's to prevent [`resolution()`], above, from spinning
114+
/// in a hot-loop.
115+
#[tracing::instrument(level = "debug")]
116+
async fn sleep_until_expired(valid_until: tokio::time::Instant) {
117+
use tokio::time::{sleep_until, Duration, Instant};
118+
119+
/// The minimum TTL duration that will be respected.
120+
const MINIMUM_TTL: Duration = Duration::from_secs(5);
121+
let minimum = Instant::now() + MINIMUM_TTL;
122+
123+
// Choose a deadline; if the expiry is too short, fall back to the minimum TTL.
124+
let deadline = if valid_until >= minimum {
125+
valid_until
126+
} else {
127+
debug!(ttl.min = ?MINIMUM_TTL, "Given TTL too short, using a minimum TTL");
128+
minimum
129+
};
130+
131+
sleep_until(deadline).await;
132+
}

0 commit comments

Comments
 (0)