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