@@ -472,7 +472,8 @@ impl Cache {
472472 // https://docs.rs/moka/0.12.14/moka/future/struct.Cache.html#concurrent-calls-on-the-same-key
473473 // So we don't need any locking here to prevent multiple downloads for the same
474474 // missing archive index.
475- self . manager
475+ if let Err ( arc_err) = self
476+ . manager
476477 . try_get_with_by_ref ( & local_index_path, async {
477478 // NOTE: benign race with the eviction listener.
478479 //
@@ -512,27 +513,27 @@ impl Cache {
512513 Ok :: < _ , anyhow:: Error > ( Arc :: new ( entry) )
513514 } )
514515 . await
515- . map_err ( | arc_err : Arc < anyhow :: Error > | {
516- // We can't convert this Arc<Error> into the inner error type.
517- // See https://github.com/moka-rs/moka/issues/497
518- // But since some callers are specifically checking
519- // ::is<PathNotFoundError> to differentiate other errors from
520- // the "not found" case, we want to preserve that information
521- // if it was the cause of the error.
522- //
523- // This mean all error types that we later want to use with ::is<> or
524- // ::downcast<> have to be mentioned here.
525- //
526- // While we could also migrate to a custom enum error type, this would
527- // only be really nice when the whole storage lib uses is. Otherwise
528- // we'll end up with some hardcoded conversions again.
529- // So I can leave it as-is for now.
530- if arc_err. is :: < PathNotFoundError > ( ) {
531- anyhow ! ( PathNotFoundError )
532- } else {
533- anyhow ! ( arc_err)
534- }
535- } ) ? ;
516+ {
517+ // We can't convert this Arc<Error> into the inner error type.
518+ // See https://github.com/moka-rs/moka/issues/497
519+ // But since some callers are specifically checking
520+ // ::is<PathNotFoundError> to differentiate other errors from
521+ // the "not found" case, we want to preserve that information
522+ // if it was the cause of the error.
523+ //
524+ // This mean all error types that we later want to use with ::is<> or
525+ // ::downcast<> have to be mentioned here.
526+ //
527+ // While we could also migrate to a custom enum error type, this would
528+ // only be really nice when the whole storage lib uses is. Otherwise
529+ // we'll end up with some hardcoded conversions again.
530+ // So I can leave it as-is for now.
531+ if arc_err. is :: < PathNotFoundError > ( ) {
532+ return Ok ( None ) ;
533+ } else {
534+ return Err ( anyhow ! ( arc_err) ) ;
535+ }
536+ }
536537
537538 // Final attempt: if this still fails, bubble the error.
538539 find_in_file ( local_index_path, path_in_archive) . await
@@ -977,6 +978,44 @@ mod tests {
977978 }
978979 }
979980
981+ struct NotFoundDownloader {
982+ remote_index_path : String ,
983+ fetch_count : std:: sync:: Mutex < usize > ,
984+ }
985+
986+ impl NotFoundDownloader {
987+ fn new ( remote_index_path : String ) -> Self {
988+ Self {
989+ remote_index_path,
990+ fetch_count : std:: sync:: Mutex :: new ( 0 ) ,
991+ }
992+ }
993+
994+ fn fetch_count ( & self ) -> usize {
995+ * self . fetch_count . lock ( ) . unwrap ( )
996+ }
997+ }
998+
999+ impl Downloader for NotFoundDownloader {
1000+ fn fetch_archive_index < ' a > (
1001+ & ' a self ,
1002+ remote_index_path : & ' a str ,
1003+ ) -> Pin < Box < dyn Future < Output = Result < StreamingBlob > > + Send + ' a > > {
1004+ Box :: pin ( async move {
1005+ if remote_index_path != self . remote_index_path {
1006+ bail ! (
1007+ "unexpected remote index path: expected {}, got {remote_index_path}" ,
1008+ self . remote_index_path
1009+ ) ;
1010+ }
1011+
1012+ let mut fetch_count = self . fetch_count . lock ( ) . unwrap ( ) ;
1013+ * fetch_count += 1 ;
1014+ Err ( PathNotFoundError . into ( ) )
1015+ } )
1016+ }
1017+ }
1018+
9801019 async fn create_index_bytes ( file_count : u32 ) -> Result < Vec < u8 > > {
9811020 let tf = create_test_archive ( file_count) . await ?;
9821021 let tempfile = tempfile:: NamedTempFile :: new ( ) ?. into_temp_path ( ) ;
@@ -1169,6 +1208,25 @@ mod tests {
11691208 Ok ( ( ) )
11701209 }
11711210
1211+ #[ tokio:: test]
1212+ async fn find_remote_index_not_found_returns_none_without_retries ( ) -> Result < ( ) > {
1213+ let cache = test_cache ( ) . await ?;
1214+ const LATEST_BUILD_ID : Option < BuildId > = Some ( BuildId ( 7 ) ) ;
1215+ const ARCHIVE_NAME : & str = "missing-index.zip" ;
1216+ const FILE_IN_ARCHIVE : & str = "testfile0" ;
1217+
1218+ let remote_index_path = format ! ( "{ARCHIVE_NAME}.{ARCHIVE_INDEX_FILE_EXTENSION}" ) ;
1219+ let downloader = NotFoundDownloader :: new ( remote_index_path) ;
1220+
1221+ let result = cache
1222+ . find ( ARCHIVE_NAME , LATEST_BUILD_ID , FILE_IN_ARCHIVE , & downloader)
1223+ . await ?;
1224+ assert ! ( result. is_none( ) ) ;
1225+ assert_eq ! ( downloader. fetch_count( ) , 1 ) ;
1226+
1227+ Ok ( ( ) )
1228+ }
1229+
11721230 #[ tokio:: test]
11731231 async fn find_retries_once_then_errors ( ) -> Result < ( ) > {
11741232 let cache = test_cache ( ) . await ?;
0 commit comments