Skip to content

Commit dc7a05f

Browse files
committed
dist: don't count download attempts that make progress as retries
1 parent ec7f8b0 commit dc7a05f

5 files changed

Lines changed: 61 additions & 48 deletions

File tree

‎Cargo.lock‎

Lines changed: 0 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ tempfile = "3.8"
9292
thiserror = "2"
9393
threadpool = "1"
9494
tokio = { version = "1.26.0", default-features = false, features = ["macros", "rt-multi-thread", "sync"] }
95-
tokio-retry = "0.3.0"
9695
tokio-stream = "0.1.14"
9796
toml = "1.0"
9897
tracing = "0.1"

‎doc/dev-guide/src/tips-and-tricks.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ should one arise with the backtrack limits.
5252
When downloading a file, rustup will retry the download a number of times. The
5353
default is 3 times, but if this variable is set to a valid usize then it is the
5454
max retry count. A value of `0` means no retries, thus the default of `3` will
55-
mean a download is tried a total of four times before failing out.
55+
mean a download is tried a total of four times before failing out. Attempts
56+
that download at least 1 MiB of the file before failing are resumed by the
57+
next attempt and don't count towards this limit.
5658

5759
## `RUSTUP_BACKTRACE`
5860

‎src/dist/download.rs‎

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,7 @@ impl<'a> DownloadCfg<'a> {
7777
}
7878
}
7979

80-
let partial_file_path = target_file.with_file_name(
81-
target_file
82-
.file_name()
83-
.map(|s| s.to_str().unwrap_or("_"))
84-
.unwrap_or("_")
85-
.to_owned()
86-
+ ".partial",
87-
);
88-
80+
let partial_file_path = self.partial_file_path(hash);
8981
let partial_file_existed = partial_file_path.exists();
9082

9183
let mut hasher = Sha256::new();
@@ -132,6 +124,18 @@ impl<'a> DownloadCfg<'a> {
132124
}
133125
}
134126

127+
/// The number of bytes already downloaded for the file with the given hash
128+
pub(crate) fn partial_len(&self, hash: &str) -> u64 {
129+
match fs::metadata(self.partial_file_path(hash)) {
130+
Ok(metadata) => metadata.len(),
131+
Err(_) => 0,
132+
}
133+
}
134+
135+
fn partial_file_path(&self, hash: &str) -> PathBuf {
136+
self.download_dir.join(format!("{hash}.partial"))
137+
}
138+
135139
pub(crate) fn clean(&self, hashes: &[impl AsRef<Path>]) -> anyhow::Result<()> {
136140
for hash in hashes.iter() {
137141
let used_file = self.download_dir.join(hash);

‎src/dist/manifestation.rs‎

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use futures_util::{
2020
};
2121
use tokio::task::{JoinHandle, spawn_blocking};
2222
use tracing::{debug, info, warn};
23+
use url::Url;
2324

2425
use crate::{
2526
diskio::{Executor, IO_CHUNK_SIZE, get_executor, unpack_ram},
@@ -771,32 +772,13 @@ impl<'a> ComponentBinary<'a> {
771772
}
772773

773774
async fn download(self, max_retries: usize) -> anyhow::Result<(ComponentInstall, &'a str)> {
774-
use tokio_retry::{RetryIf, strategy::FixedInterval};
775-
776775
let url = self.download_cfg.url(&self.binary.url)?;
777-
let installer = RetryIf::start(
778-
FixedInterval::from_millis(0).take(max_retries),
779-
|| {
780-
self.download_cfg
781-
.download(&url, &self.binary.hash, &self.status)
782-
},
783-
|e: &anyhow::Error| {
784-
// retry only known retriable cases
785-
match e.downcast_ref::<RustupError>() {
786-
Some(RustupError::BrokenPartialFile)
787-
| Some(RustupError::IncompletePartialFile)
788-
| Some(RustupError::DownloadingFile { .. }) => {
789-
self.status.retrying();
790-
true
791-
}
792-
_ => false,
793-
}
794-
},
795-
)
796-
.await
797-
.with_context(|| {
798-
RustupError::ComponentDownloadFailed(self.manifest.name(&self.component))
799-
})?;
776+
let installer = self
777+
.download_with_retries(&url, max_retries)
778+
.await
779+
.with_context(|| {
780+
RustupError::ComponentDownloadFailed(self.manifest.name(&self.component))
781+
})?;
800782

801783
let install = ComponentInstall {
802784
status: self.status,
@@ -813,6 +795,44 @@ impl<'a> ComponentBinary<'a> {
813795

814796
Ok((install, &self.binary.hash))
815797
}
798+
799+
async fn download_with_retries(&self, url: &Url, max_retries: usize) -> anyhow::Result<File> {
800+
const MIN_PROGRESS: u64 = 1024 * 1024;
801+
802+
let mut retries = 0;
803+
loop {
804+
let downloaded = self.download_cfg.partial_len(&self.binary.hash);
805+
let err = match self
806+
.download_cfg
807+
.download(url, &self.binary.hash, &self.status)
808+
.await
809+
{
810+
Ok(file) => return Ok(file),
811+
Err(err) => err,
812+
};
813+
814+
// retry only known retriable cases
815+
match err.downcast_ref::<RustupError>() {
816+
Some(RustupError::BrokenPartialFile)
817+
| Some(RustupError::IncompletePartialFile)
818+
| Some(RustupError::DownloadingFile { .. }) => {}
819+
_ => return Err(err),
820+
}
821+
822+
let progress = self
823+
.download_cfg
824+
.partial_len(&self.binary.hash)
825+
.saturating_sub(downloaded);
826+
if progress < MIN_PROGRESS {
827+
if retries == max_retries {
828+
return Err(err);
829+
}
830+
retries += 1;
831+
}
832+
833+
self.status.retrying();
834+
}
835+
}
816836
}
817837

818838
struct ComponentInstall {

0 commit comments

Comments
 (0)