From a44ca6c92bec87a1f4e95e95729fc8827c437e14 Mon Sep 17 00:00:00 2001 From: Wenhao Date: Mon, 10 Aug 2026 10:38:29 +0000 Subject: [PATCH] fix(p2p): do not cache lookup timeouts as misses --- src/overlaybd/p2p/facade.rs | 42 +++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/overlaybd/p2p/facade.rs b/src/overlaybd/p2p/facade.rs index fc45a91a..f7581866 100644 --- a/src/overlaybd/p2p/facade.rs +++ b/src/overlaybd/p2p/facade.rs @@ -362,9 +362,9 @@ async fn lookup_descriptor( debug!( key = %artifact_key, timeout_ms = state.lookup_timeout.as_millis(), - "p2p lookup timed out; treating as miss" + "p2p lookup timed out; retrying on next read rather than caching a miss" ); - None + return None; } }; let should_cache = descriptor.as_ref().is_none_or(|descriptor| { @@ -1282,6 +1282,44 @@ mod tests { origin_handle.abort(); } + #[tokio::test] + async fn lookup_timeouts_are_not_cached_as_misses() { + let blob: Vec = (0..4096).map(|i| (i % 251) as u8).collect(); + let hits = Arc::new(AtomicUsize::new(0)); + let (origin, origin_handle) = spawn_origin(OriginState { + blob: Arc::new(blob), + hits: hits.clone(), + force_200: false, + }) + .await; + let transport = Arc::new(MockTransport { + lookup_delay: Some(Duration::from_millis(50)), + ..Default::default() + }); + let facade = start_test_facade_with_config( + transport.clone(), + P2pHttpFacadeConfig { + lookup_timeout: Duration::from_millis(10), + fetch_range_timeout: DEFAULT_FETCH_RANGE_TIMEOUT, + descriptor_cache_ttl: Duration::from_secs(60), + descriptor_miss_cache_ttl: Duration::from_secs(60), + descriptor_cache_max_entries: DEFAULT_DESCRIPTOR_CACHE_MAX_ENTRIES, + allowed_publish_roots: Vec::new(), + }, + ) + .await; + let origin_url = format!("{origin}/v2/ns/repo/blobs/sha256:abababababababababababababababababababababababababababababababab?sig=one"); + + let first = get_range(&format!("{}/{}", facade.address(), origin_url), 0, 63).await; + assert_eq!(first.status(), StatusCode::PARTIAL_CONTENT); + let second = get_range(&format!("{}/{}", facade.address(), origin_url), 64, 127).await; + assert_eq!(second.status(), StatusCode::PARTIAL_CONTENT); + assert_eq!(transport.lookup_count.load(Ordering::Relaxed), 2); + assert_eq!(hits.load(Ordering::Relaxed), 2); + facade.shutdown().await.unwrap(); + origin_handle.abort(); + } + #[tokio::test] async fn slow_p2p_range_fetch_falls_back_to_origin() { let blob: Vec = (0..4096).map(|i| (i % 251) as u8).collect();