diff --git a/src/api.rs b/src/api.rs index e638fba..9aa3f60 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1012,6 +1012,7 @@ mod tests { socket, zone_map: std::collections::HashMap::new(), cache: RwLock::new(crate::cache::DnsCache::new(100, 60, 86400)), + refreshing: Mutex::new(std::collections::HashSet::new()), stats: Mutex::new(crate::stats::ServerStats::new()), overrides: RwLock::new(crate::override_store::OverrideStore::new()), blocklist: RwLock::new(crate::blocklist::BlocklistStore::new()), diff --git a/src/ctx.rs b/src/ctx.rs index c1f28f2..8632a28 100644 --- a/src/ctx.rs +++ b/src/ctx.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::net::SocketAddr; use std::path::PathBuf; use std::sync::{Arc, Mutex, RwLock}; @@ -35,6 +35,8 @@ pub struct ServerCtx { pub zone_map: ZoneMap, /// std::sync::RwLock (not tokio) — locks must never be held across .await points. pub cache: RwLock, + /// Domains currently being refreshed in the background (dedup guard). + pub refreshing: Mutex>, pub stats: Mutex, pub overrides: RwLock, pub blocklist: RwLock, @@ -168,9 +170,15 @@ pub async fn resolve_query( let cached = ctx.cache.read().unwrap().lookup_with_status(&qname, qtype); if let Some((cached, cached_dnssec, stale)) = cached { if stale { - let ctx = Arc::clone(ctx); - let qname = qname.clone(); - tokio::spawn(async move { warm_stale(&ctx, &qname, qtype).await }); + let key = (qname.clone(), qtype); + let already = !ctx.refreshing.lock().unwrap().insert(key.clone()); + if !already { + let ctx = Arc::clone(ctx); + tokio::spawn(async move { + warm_stale(&ctx, &key.0, key.1).await; + ctx.refreshing.lock().unwrap().remove(&key); + }); + } } let mut resp = cached; resp.header.id = query.header.id; diff --git a/src/dot.rs b/src/dot.rs index be22375..0216dbf 100644 --- a/src/dot.rs +++ b/src/dot.rs @@ -357,6 +357,7 @@ mod tests { m }, cache: RwLock::new(crate::cache::DnsCache::new(100, 60, 86400)), + refreshing: Mutex::new(std::collections::HashSet::new()), stats: Mutex::new(crate::stats::ServerStats::new()), overrides: RwLock::new(crate::override_store::OverrideStore::new()), blocklist: RwLock::new(crate::blocklist::BlocklistStore::new()), diff --git a/src/main.rs b/src/main.rs index ebc16cc..9aa3f17 100644 --- a/src/main.rs +++ b/src/main.rs @@ -285,6 +285,7 @@ async fn main() -> numa::Result<()> { config.cache.min_ttl, config.cache.max_ttl, )), + refreshing: Mutex::new(std::collections::HashSet::new()), stats: Mutex::new(ServerStats::new()), overrides: RwLock::new(OverrideStore::new()), blocklist: RwLock::new(blocklist),