diff options
author | Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> | 2024-10-15 16:46:42 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-15 16:46:42 -0700 |
commit | 7c3c13cecf48219cdcb90dc0b5019686cdd88626 (patch) | |
tree | ea44361a7b8c061ec84ec29d88aa2c6a1c3ec07a /cli/util | |
parent | 40b1c42138c47e89eefa859cd36a9e3d62541e7b (diff) |
fix(install): retry downloads of registry info / tarballs (#26278)
Fixes #26085.
Adds a basic retry utility with some defaults, starts off with a 100ms
wait, then 250ms, then 500ms
I've applied the retry in the http client, reusing an existing function,
so this also applies to retrying downloads of deno binaries in `upgrade`
and `compile`. I can make a separate function that doesn't retry so this
doesn't affect `upgrade` and `compile`, but it seemed desirable to have
retries there too, so I left it in.
Diffstat (limited to 'cli/util')
-rw-r--r-- | cli/util/mod.rs | 1 | ||||
-rw-r--r-- | cli/util/retry.rs | 41 |
2 files changed, 42 insertions, 0 deletions
diff --git a/cli/util/mod.rs b/cli/util/mod.rs index e59b09d2c..f81a74c44 100644 --- a/cli/util/mod.rs +++ b/cli/util/mod.rs @@ -14,6 +14,7 @@ pub mod logger; pub mod path; pub mod progress_bar; pub mod result; +pub mod retry; pub mod sync; pub mod text_encoding; pub mod unix; diff --git a/cli/util/retry.rs b/cli/util/retry.rs new file mode 100644 index 000000000..a8febe60d --- /dev/null +++ b/cli/util/retry.rs @@ -0,0 +1,41 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use std::future::Future; +use std::time::Duration; + +pub fn retry< + F: FnMut() -> Fut, + T, + E, + Fut: Future<Output = Result<T, E>>, + ShouldRetry: FnMut(&E) -> bool, +>( + mut f: F, + mut should_retry: ShouldRetry, +) -> impl Future<Output = Result<T, E>> { + const WAITS: [Duration; 3] = [ + Duration::from_millis(100), + Duration::from_millis(250), + Duration::from_millis(500), + ]; + + let mut waits = WAITS.into_iter(); + async move { + let mut first_result = None; + loop { + let result = f().await; + match result { + Ok(r) => return Ok(r), + Err(e) if !should_retry(&e) => return Err(e), + _ => {} + } + if first_result.is_none() { + first_result = Some(result); + } + let Some(wait) = waits.next() else { + return first_result.unwrap(); + }; + tokio::time::sleep(wait).await; + } + } +} |