Skip to content

Commit 79a491a

Browse files
committed
Auto merge of #14694 - arlosi:fix-extra-blocking, r=weihanglo
fix(registry): `HttpRegistry` `block_until_ready` returns early when work is still pending ### What does this PR try to resolve? `block_until_ready` is returning early when there are still pending transfers. This leads to extra restarts of the resolver, impacting performance. In the existing implementation: - `handle_completed_downloads` runs and there are no completed downloads - `multi.perform()` is called and completes new downloads, with no pending transfers remaining - Since there are no items `remaining_in_multi`, the function returns early This fixes the issue by reordering the calls to `multi.perform()`, `handle_completed_downloads`, then the completion check. ### How should we test and review this PR? A test is added that uses cargo's tracing to show the number of blocking calls. First commit shows existing behavior, second commit shows fix. Originally based on the PR #14680 by `@x-hgg-x.` The ordering fix in this PR also avoids an additional `block_until_ready` call for retried failed downloads.
2 parents 8040c00 + 8db2192 commit 79a491a

File tree

2 files changed

+69
-6
lines changed

2 files changed

+69
-6
lines changed

src/cargo/sources/registry/http_remote.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -789,26 +789,29 @@ impl<'gctx> RegistryData for HttpRegistry<'gctx> {
789789
}
790790

791791
fn block_until_ready(&mut self) -> CargoResult<()> {
792-
trace!(target: "network",
793-
"block_until_ready: {} transfers pending",
792+
trace!(target: "network::HttpRegistry::block_until_ready",
793+
"{} transfers pending",
794794
self.downloads.pending.len()
795795
);
796796
self.downloads.blocking_calls += 1;
797797

798798
loop {
799-
self.handle_completed_downloads()?;
800-
self.add_sleepers()?;
801-
802799
let remaining_in_multi = tls::set(&self.downloads, || {
803800
self.multi
804801
.perform()
805802
.context("failed to perform http requests")
806803
})?;
807804
trace!(target: "network", "{} transfers remaining", remaining_in_multi);
808-
805+
// Handles transfers performed by `self.multi` above and adds to
806+
// `self.downloads.results`. Failed transfers get added to
807+
// `self.downloads.sleeping` for retry.
808+
self.handle_completed_downloads()?;
809809
if remaining_in_multi + self.downloads.sleeping.len() as u32 == 0 {
810810
return Ok(());
811811
}
812+
// Handles failed transfers in `self.downloads.sleeping` and
813+
// re-adds them to `self.multi`.
814+
self.add_sleepers()?;
812815

813816
if self.downloads.pending.is_empty() {
814817
let delay = self.downloads.sleeping.time_to_next().unwrap();

tests/testsuite/registry.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3314,6 +3314,66 @@ Caused by:
33143314
.run();
33153315
}
33163316

3317+
#[cargo_test]
3318+
fn sparse_blocking_count() {
3319+
let fail_count = Mutex::new(0);
3320+
let _registry = RegistryBuilder::new()
3321+
.http_index()
3322+
.add_responder("/index/3/b/bar", move |req, server| {
3323+
let mut fail_count = fail_count.lock().unwrap();
3324+
if *fail_count < 1 {
3325+
*fail_count += 1;
3326+
server.internal_server_error(req)
3327+
} else {
3328+
server.index(req)
3329+
}
3330+
})
3331+
.build();
3332+
3333+
let p = project()
3334+
.file(
3335+
"Cargo.toml",
3336+
r#"
3337+
[package]
3338+
name = "foo"
3339+
version = "0.0.1"
3340+
edition = "2015"
3341+
authors = []
3342+
3343+
[dependencies]
3344+
bar = ">= 0.0.0"
3345+
"#,
3346+
)
3347+
.file("src/main.rs", "fn main() {}")
3348+
.build();
3349+
3350+
Package::new("bar", "0.0.1").publish();
3351+
3352+
// Ensure we have the expected number of `block_until_ready` calls.
3353+
// The 1st (0 transfers pending), is the deliberate extra call in `ensure_loaded` for a source.
3354+
// The 2nd (1 transfers pending), is the registry `config.json`.
3355+
// the 3rd (1 transfers pending), is the package metadata for `bar`.
3356+
3357+
p.cargo("check")
3358+
.env("CARGO_LOG", "network::HttpRegistry::block_until_ready=trace")
3359+
.with_stderr_data(str![[r#"
3360+
[..] TRACE network::HttpRegistry::block_until_ready: 0 transfers pending
3361+
[UPDATING] `dummy-registry` index
3362+
[..] TRACE network::HttpRegistry::block_until_ready: 1 transfers pending
3363+
[..] TRACE network::HttpRegistry::block_until_ready: 1 transfers pending
3364+
[WARNING] spurious network error (3 tries remaining): failed to get successful HTTP response from `[..]/index/3/b/bar` ([..]), got 500
3365+
body:
3366+
internal server error
3367+
[LOCKING] 1 package to latest compatible version
3368+
[DOWNLOADING] crates ...
3369+
[DOWNLOADED] bar v0.0.1 (registry `dummy-registry`)
3370+
[CHECKING] bar v0.0.1
3371+
[CHECKING] foo v0.0.1 ([ROOT]/foo)
3372+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
3373+
3374+
"#]]).run();
3375+
}
3376+
33173377
#[cargo_test]
33183378
fn sparse_retry_single() {
33193379
let fail_count = Mutex::new(0);

0 commit comments

Comments
 (0)