style: cargo fmt
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
12
src/ctx.rs
12
src/ctx.rs
@@ -760,9 +760,9 @@ mod tests {
|
|||||||
for i in 0..5u16 {
|
for i in 0..5u16 {
|
||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
let buf = build_wire_query(100 + i, "coalesce-test.example.com", QueryType::A);
|
let buf = build_wire_query(100 + i, "coalesce-test.example.com", QueryType::A);
|
||||||
handles.push(tokio::spawn(async move {
|
handles.push(tokio::spawn(
|
||||||
handle_query(buf, src, &ctx).await
|
async move { handle_query(buf, src, &ctx).await },
|
||||||
}));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
for h in handles {
|
for h in handles {
|
||||||
@@ -800,7 +800,11 @@ mod tests {
|
|||||||
h2.await.unwrap().unwrap();
|
h2.await.unwrap().unwrap();
|
||||||
|
|
||||||
let actual = query_count.load(std::sync::atomic::Ordering::Relaxed);
|
let actual = query_count.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
assert!(actual >= 2, "A and AAAA should resolve independently, got {}", actual);
|
assert!(
|
||||||
|
actual >= 2,
|
||||||
|
"A and AAAA should resolve independently, got {}",
|
||||||
|
actual
|
||||||
|
);
|
||||||
assert!(ctx.inflight.lock().unwrap().is_empty());
|
assert!(ctx.inflight.lock().unwrap().is_empty());
|
||||||
|
|
||||||
crate::recursive::reset_udp_state();
|
crate::recursive::reset_udp_state();
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ const UDP_FAIL_THRESHOLD: u8 = 3;
|
|||||||
|
|
||||||
static QUERY_ID: AtomicU16 = AtomicU16::new(1);
|
static QUERY_ID: AtomicU16 = AtomicU16::new(1);
|
||||||
static UDP_FAILURES: std::sync::atomic::AtomicU8 = std::sync::atomic::AtomicU8::new(0);
|
static UDP_FAILURES: std::sync::atomic::AtomicU8 = std::sync::atomic::AtomicU8::new(0);
|
||||||
pub(crate) static UDP_DISABLED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
|
pub(crate) static UDP_DISABLED: std::sync::atomic::AtomicBool =
|
||||||
|
std::sync::atomic::AtomicBool::new(false);
|
||||||
|
|
||||||
fn next_id() -> u16 {
|
fn next_id() -> u16 {
|
||||||
QUERY_ID.fetch_add(1, Ordering::Relaxed)
|
QUERY_ID.fetch_add(1, Ordering::Relaxed)
|
||||||
|
|||||||
100
src/srtt.rs
100
src/srtt.rs
@@ -108,6 +108,13 @@ impl SrttCache {
|
|||||||
self.entries.is_empty()
|
self.entries.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn set_updated_at(&mut self, ip: IpAddr, at: Instant) {
|
||||||
|
if let Some(entry) = self.entries.get_mut(&ip) {
|
||||||
|
entry.updated_at = at;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn maybe_evict(&mut self) {
|
fn maybe_evict(&mut self) {
|
||||||
if self.entries.len() < MAX_ENTRIES {
|
if self.entries.len() < MAX_ENTRIES {
|
||||||
return;
|
return;
|
||||||
@@ -203,6 +210,99 @@ mod tests {
|
|||||||
assert_eq!(addrs, original);
|
assert_eq!(addrs, original);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn age(secs: u64) -> Instant {
|
||||||
|
Instant::now() - std::time::Duration::from_secs(secs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cache with ip(1) saturated at FAILURE_PENALTY_MS
|
||||||
|
fn saturated_penalty_cache() -> SrttCache {
|
||||||
|
let mut cache = SrttCache::new(true);
|
||||||
|
for _ in 0..30 {
|
||||||
|
cache.record_rtt(ip(1), FAILURE_PENALTY_MS, false);
|
||||||
|
}
|
||||||
|
cache
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_decay_within_threshold() {
|
||||||
|
let mut cache = SrttCache::new(true);
|
||||||
|
cache.record_rtt(ip(1), 5000, false);
|
||||||
|
cache.set_updated_at(ip(1), age(DECAY_AFTER_SECS));
|
||||||
|
assert_eq!(cache.get(ip(1)), cache.entries[&ip(1)].srtt_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn one_decay_period() {
|
||||||
|
let mut cache = saturated_penalty_cache();
|
||||||
|
let raw = cache.entries[&ip(1)].srtt_ms;
|
||||||
|
cache.set_updated_at(ip(1), age(DECAY_AFTER_SECS + 1));
|
||||||
|
let expected = (raw + INITIAL_SRTT_MS) / 2;
|
||||||
|
assert_eq!(cache.get(ip(1)), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_decay_periods() {
|
||||||
|
let mut cache = saturated_penalty_cache();
|
||||||
|
let raw = cache.entries[&ip(1)].srtt_ms;
|
||||||
|
cache.set_updated_at(ip(1), age(DECAY_AFTER_SECS * 4 + 1));
|
||||||
|
let mut expected = raw;
|
||||||
|
for _ in 0..4 {
|
||||||
|
expected = (expected + INITIAL_SRTT_MS) / 2;
|
||||||
|
}
|
||||||
|
assert_eq!(cache.get(ip(1)), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decay_caps_at_8_periods() {
|
||||||
|
// 9 periods and 100 periods should produce the same result (capped at 8)
|
||||||
|
let mut cache_a = saturated_penalty_cache();
|
||||||
|
let mut cache_b = saturated_penalty_cache();
|
||||||
|
cache_a.set_updated_at(ip(1), age(DECAY_AFTER_SECS * 9 + 1));
|
||||||
|
cache_b.set_updated_at(ip(1), age(DECAY_AFTER_SECS * 100));
|
||||||
|
assert_eq!(cache_a.get(ip(1)), cache_b.get(ip(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decay_converges_toward_initial() {
|
||||||
|
let mut cache = saturated_penalty_cache();
|
||||||
|
cache.set_updated_at(ip(1), age(DECAY_AFTER_SECS * 100));
|
||||||
|
let decayed = cache.get(ip(1));
|
||||||
|
let diff = decayed.abs_diff(INITIAL_SRTT_MS);
|
||||||
|
assert!(
|
||||||
|
diff < 25,
|
||||||
|
"expected near INITIAL_SRTT_MS, got {} (diff={})",
|
||||||
|
decayed,
|
||||||
|
diff
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn record_rtt_applies_decay_before_ewma() {
|
||||||
|
let mut cache = saturated_penalty_cache();
|
||||||
|
cache.set_updated_at(ip(1), age(DECAY_AFTER_SECS * 8));
|
||||||
|
cache.record_rtt(ip(1), 50, false);
|
||||||
|
let srtt = cache.get(ip(1));
|
||||||
|
// Without decay-before-EWMA, result would be ~(5000*7+50)/8 ≈ 4381
|
||||||
|
assert!(srtt < 500, "expected decay before EWMA, got srtt={}", srtt);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decay_reranks_stale_failures() {
|
||||||
|
let mut cache = saturated_penalty_cache();
|
||||||
|
for _ in 0..30 {
|
||||||
|
cache.record_rtt(ip(2), 300, false);
|
||||||
|
}
|
||||||
|
let mut addrs = vec![sock(1), sock(2)];
|
||||||
|
cache.sort_by_rtt(&mut addrs);
|
||||||
|
assert_eq!(addrs, vec![sock(2), sock(1)]);
|
||||||
|
|
||||||
|
// Age server 1 so it decays toward INITIAL (200ms) — below server 2's 300ms
|
||||||
|
cache.set_updated_at(ip(1), age(DECAY_AFTER_SECS * 100));
|
||||||
|
let mut addrs = vec![sock(1), sock(2)];
|
||||||
|
cache.sort_by_rtt(&mut addrs);
|
||||||
|
assert_eq!(addrs, vec![sock(1), sock(2)]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn eviction_removes_oldest() {
|
fn eviction_removes_oldest() {
|
||||||
let mut cache = SrttCache::new(true);
|
let mut cache = SrttCache::new(true);
|
||||||
|
|||||||
Reference in New Issue
Block a user