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:
@@ -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()),
|
||||
|
||||
16
src/ctx.rs
16
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<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 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;
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user