feat: raise cache default to 100K entries, evict stalest instead of dropping

The 10K cap was too conservative — the blocklist alone holds 400K domains.
At ~100 bytes per wire entry, 100K entries is ~10MB.

When the cache is full and evict_expired doesn't free enough slots,
evict_stalest removes the entry with the least remaining TTL instead of
silently discarding the new insert.
This commit is contained in:
Razvan Dimescu
2026-04-12 19:23:28 +03:00
parent 05d5a5145f
commit 043a7e1ba5
3 changed files with 42 additions and 7 deletions

View File

@@ -100,7 +100,7 @@ impl DnsCache {
if self.entry_count >= self.max_entries {
self.evict_expired();
if self.entry_count >= self.max_entries {
return;
self.evict_stalest();
}
}
@@ -260,6 +260,34 @@ impl DnsCache {
});
self.entry_count -= count;
}
/// Evict the single entry closest to (or furthest past) expiry.
fn evict_stalest(&mut self) {
let mut worst: Option<(String, QueryType, Duration)> = None;
for (domain, type_map) in &self.entries {
for (qtype, entry) in type_map {
let age = entry.inserted_at.elapsed();
let remaining = entry.ttl.saturating_sub(age);
match &worst {
None => worst = Some((domain.clone(), *qtype, remaining)),
Some((_, _, w)) if remaining < *w => {
worst = Some((domain.clone(), *qtype, remaining));
}
_ => {}
}
}
}
if let Some((domain, qtype, _)) = worst {
if let Some(type_map) = self.entries.get_mut(&domain) {
if type_map.remove(&qtype).is_some() {
self.entry_count -= 1;
}
if type_map.is_empty() {
self.entries.remove(&domain);
}
}
}
}
}
pub struct CacheInfo {