From 53c6c78721e65912bd3be42d6bb684b5803d42f1 Mon Sep 17 00:00:00 2001 From: Johnathan Corgan Date: Sat, 30 May 2026 01:50:57 +0000 Subject: [PATCH] discovery: count dropped requests when the dedup cache is full The discovery request dedup cache (recent_requests) silently dropped LookupRequests once it reached MAX_RECENT_DISCOVERY_REQUESTS, with no counter to surface the condition. Add a DiscoveryReject::ReqDedupCacheFull reject reason backed by a req_dedup_cache_full counter on DiscoveryStats, mirroring the existing duplicate-request counter, and record it at the drop site so the rejection is visible in show_routing. --- src/control/snapshots/show_routing.json | 1 + src/node/handlers/discovery.rs | 2 ++ src/node/reject.rs | 5 +++++ src/node/stats.rs | 11 +++++++++++ 4 files changed, 19 insertions(+) diff --git a/src/control/snapshots/show_routing.json b/src/control/snapshots/show_routing.json index 794608b..ac6e2a3 100644 --- a/src/control/snapshots/show_routing.json +++ b/src/control/snapshots/show_routing.json @@ -11,6 +11,7 @@ "req_backoff_suppressed": 0, "req_bloom_miss": 0, "req_decode_error": 0, + "req_dedup_cache_full": 0, "req_deduplicated": 0, "req_duplicate": 0, "req_fallback_forwarded": 0, diff --git a/src/node/handlers/discovery.rs b/src/node/handlers/discovery.rs index d53d3e0..872a99b 100644 --- a/src/node/handlers/discovery.rs +++ b/src/node/handlers/discovery.rs @@ -57,6 +57,8 @@ impl Node { } if self.recent_requests.len() >= MAX_RECENT_DISCOVERY_REQUESTS { + self.stats_mut() + .record_reject(RejectReason::Discovery(DiscoveryReject::ReqDedupCacheFull)); debug!( request_id = request.request_id, from = %self.peer_display_name(from), diff --git a/src/node/reject.rs b/src/node/reject.rs index 85f13fb..cac5ad0 100644 --- a/src/node/reject.rs +++ b/src/node/reject.rs @@ -117,6 +117,10 @@ pub enum DiscoveryReject { /// Tracked via /// [`DiscoveryStats::req_duplicate`](crate::node::stats::DiscoveryStats). ReqDuplicate, + /// Request dedup cache (`recent_requests`) is at capacity, so the + /// `LookupRequest` is dropped without being forwarded. Tracked via + /// [`DiscoveryStats::req_dedup_cache_full`](crate::node::stats::DiscoveryStats). + ReqDedupCacheFull, /// Request arrived with TTL=0 — no more forwarding hops allowed. /// Tracked via /// [`DiscoveryStats::req_ttl_exhausted`](crate::node::stats::DiscoveryStats). @@ -309,6 +313,7 @@ mod tests { let variants = [ DiscoveryReject::ReqDecodeError, DiscoveryReject::ReqDuplicate, + DiscoveryReject::ReqDedupCacheFull, DiscoveryReject::ReqTtlExhausted, DiscoveryReject::RespDecodeError, DiscoveryReject::RespIdentityMiss, diff --git a/src/node/stats.rs b/src/node/stats.rs index d93279c..3e96033 100644 --- a/src/node/stats.rs +++ b/src/node/stats.rs @@ -130,6 +130,7 @@ pub struct DiscoveryStats { pub req_received: u64, pub req_decode_error: u64, pub req_duplicate: u64, + pub req_dedup_cache_full: u64, pub req_target_is_us: u64, pub req_forwarded: u64, pub req_ttl_exhausted: u64, @@ -156,6 +157,7 @@ impl DiscoveryStats { match reason { DiscoveryReject::ReqDecodeError => self.req_decode_error += 1, DiscoveryReject::ReqDuplicate => self.req_duplicate += 1, + DiscoveryReject::ReqDedupCacheFull => self.req_dedup_cache_full += 1, DiscoveryReject::ReqTtlExhausted => self.req_ttl_exhausted += 1, DiscoveryReject::RespDecodeError => self.resp_decode_error += 1, DiscoveryReject::RespIdentityMiss => self.resp_identity_miss += 1, @@ -169,6 +171,7 @@ impl DiscoveryStats { req_received: self.req_received, req_decode_error: self.req_decode_error, req_duplicate: self.req_duplicate, + req_dedup_cache_full: self.req_dedup_cache_full, req_target_is_us: self.req_target_is_us, req_forwarded: self.req_forwarded, req_ttl_exhausted: self.req_ttl_exhausted, @@ -585,6 +588,7 @@ pub struct DiscoveryStatsSnapshot { pub req_received: u64, pub req_decode_error: u64, pub req_duplicate: u64, + pub req_dedup_cache_full: u64, pub req_target_is_us: u64, pub req_forwarded: u64, pub req_ttl_exhausted: u64, @@ -851,6 +855,13 @@ mod tests { assert_eq!(s.req_duplicate, 1); } + #[test] + fn discovery_stats_record_reject_req_dedup_cache_full() { + let mut s = DiscoveryStats::default(); + s.record_reject(DiscoveryReject::ReqDedupCacheFull); + assert_eq!(s.req_dedup_cache_full, 1); + } + #[test] fn discovery_stats_record_reject_req_ttl_exhausted() { let mut s = DiscoveryStats::default();