fix: deduplicate background refresh with per-domain guard

Multiple stale queries for the same domain now spawn only one background
refresh. A HashSet<(String, QueryType)> on ServerCtx tracks in-flight
refreshes; subsequent stale hits for the same key skip the spawn.
This commit is contained in:
Razvan Dimescu
2026-04-12 19:49:23 +03:00
parent 8ef95383a2
commit 3c49b0e65d
4 changed files with 15 additions and 4 deletions

View File

@@ -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()),

View File

@@ -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<DnsCache>,
/// Domains currently being refreshed in the background (dedup guard).
pub refreshing: Mutex<HashSet<(String, QueryType)>>,
pub stats: Mutex<ServerStats>,
pub overrides: RwLock<OverrideStore>,
pub blocklist: RwLock<BlocklistStore>,
@@ -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 key = (qname.clone(), qtype);
let already = !ctx.refreshing.lock().unwrap().insert(key.clone());
if !already {
let ctx = Arc::clone(ctx);
let qname = qname.clone();
tokio::spawn(async move { warm_stale(&ctx, &qname, qtype).await });
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;

View File

@@ -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()),

View File

@@ -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),