Cache architecture: identity fix, cache merge, parent-change flush

Identity cache: remove TTL-based expiry (60s TTL broke active sessions
after expiry since handle_tun_outbound checks identity_cache before
session table). Replace with LRU-only eviction bounded by configurable
identity_size (default 10K). Lookup now touches timestamp for LRU
freshness.

Cache merge: unify coord_cache and route_cache into single coordinate
cache. Both stored NodeAddr→TreeCoordinate; the layer distinction was
conceptual, not functional. Discovery-sourced entries now get the same
TTL+refresh treatment as session-sourced entries. Simplifies
find_next_hop() to single cache lookup.

Parent-change flush: clear coord_cache after recompute_coords() in both
parent-switch paths of handle_tree_announce(). Stale coordinates after
tree reconvergence cause dead-end routing that's more expensive than
re-discovery.

Tested: 493 unit tests passed, clippy clean, Docker mesh 20/20,
Docker chain 6/6.
This commit is contained in:
Johnathan Corgan
2026-02-16 22:56:34 +00:00
parent 852f561fa0
commit f374370e5c
11 changed files with 108 additions and 479 deletions
+4 -4
View File
@@ -92,7 +92,7 @@ impl Node {
/// Processing steps:
/// 1. Decode and validate
/// 2. Check recent_requests to determine if we originated or are forwarding
/// 3. If originator: cache target_coords in route_cache
/// 3. If originator: cache target_coords in coord_cache
/// 4. If transit: reverse-path forward to from_peer
pub(in crate::node) async fn handle_lookup_response(
&mut self,
@@ -139,7 +139,7 @@ impl Node {
);
let target = response.target;
self.route_cache.insert(
self.coord_cache.insert(
target,
response.target_coords,
now_ms,
@@ -149,7 +149,7 @@ impl Node {
self.pending_lookups.remove(&target);
// If we have pending TUN packets for this target, retry session
// initiation. The route_cache now has coords, so find_next_hop()
// initiation. The coord_cache now has coords, so find_next_hop()
// should succeed.
if self.pending_tun_packets.contains_key(&target) {
self.retry_session_after_discovery(target).await;
@@ -261,7 +261,7 @@ impl Node {
/// Creates a LookupRequest and floods it to all peers. The originator
/// does NOT record the request_id in recent_requests, so when the
/// response arrives, it's recognized as "our request" and the
/// target's coordinates are cached in route_cache.
/// target's coordinates are cached in coord_cache.
pub(in crate::node) async fn initiate_lookup(&mut self, target: &NodeAddr, ttl: u8) {
let origin = *self.node_addr();
let origin_coords = self.tree_state().my_coords().clone();
+1 -4
View File
@@ -497,9 +497,6 @@ impl Node {
if let Some(coords) = self.coord_cache.get(dest, now_ms) {
return coords.clone();
}
if let Some(cached) = self.route_cache.get(dest) {
return cached.coords().clone();
}
// Fallback: use our own coordinates. The SessionSetup dest_coords
// field cannot be empty (wire format requires ≥1 entry). Using our
// own coords is safe — transit routers will still cache them, and
@@ -675,7 +672,7 @@ impl Node {
/// Retry session initiation after discovery provided coordinates.
///
/// Called when a LookupResponse arrives and we have pending TUN packets
/// for the discovered target. The route_cache now has coords, so
/// for the discovered target. The coord_cache now has coords, so
/// `find_next_hop()` should succeed and the SessionSetup can be sent.
pub(in crate::node) async fn retry_session_after_discovery(&mut self, dest_addr: NodeAddr) {
// Look up the destination's public key from the identity cache