Skip to content

Commit 7db9bdb

Browse files
committed
don't retry archive index download if it doesn't exist remotely.
1 parent 67ba894 commit 7db9bdb

File tree

2 files changed

+83
-29
lines changed

2 files changed

+83
-29
lines changed

crates/lib/docs_rs_storage/src/archive_index.rs

Lines changed: 80 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -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?;

crates/lib/docs_rs_storage/src/storage/non_blocking.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -149,15 +149,11 @@ impl AsyncStorage {
149149
latest_build_id: Option<BuildId>,
150150
path: &str,
151151
) -> Result<bool> {
152-
match self
152+
Ok(self
153153
.archive_index_cache
154154
.find(archive_path, latest_build_id, path, self)
155-
.await
156-
{
157-
Ok(file_info) => Ok(file_info.is_some()),
158-
Err(err) if err.downcast_ref::<PathNotFoundError>().is_some() => Ok(false),
159-
Err(err) => Err(err),
160-
}
155+
.await?
156+
.is_some())
161157
}
162158

163159
/// get, decompress and materialize an object from store

0 commit comments

Comments
 (0)