From 4b61d2198230a2bfda6c509d8a0a997bf7bffb62 Mon Sep 17 00:00:00 2001 From: elrrrrrrr Date: Sun, 21 Jun 2026 23:09:48 +0800 Subject: [PATCH] perf(pm): drop reqwest `stream` feature, stream download via `Response::chunk` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #3142 added live install progress using `response.bytes_stream()`, which requires reqwest's `stream` feature. #3183 (binary slimming) had removed that feature on a base that predated the call site, so once both landed on `next` the combination failed to compile (`E0599: no method bytes_stream`). #3187 fixed the breakage by re-adding the `stream` feature. But download progress never needed `stream`. `Response::chunk()` reads the body frame-by-frame with no feature gate, so it feeds the same per-chunk `DOWNLOADED_BYTES` counter the spinner renders. The body is still fully buffered before return (the extractor is not streaming), so this only buys the progress signal, not lower peak memory — identical behavior, one fewer feature. Revert #3187's `stream` feature (drops the wasm-only `wasm-streams` dep from the lock) and rewrite the download loop to use `chunk()`, dropping the `futures::StreamExt` import. Verified: cargo check -p utoo-pm, cargo clippy --all-targets -D warnings, cargo fmt all clean; Cargo.lock diff is the exact inverse of #3187. Co-Authored-By: Claude Opus 4.8 (1M context) --- Cargo.lock | 14 -------------- crates/pm/Cargo.toml | 1 - crates/pm/src/util/downloader.rs | 24 ++++++++++++++---------- 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 444952bb7f..47d1331683 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6140,7 +6140,6 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", "web-sys", ] @@ -10932,19 +10931,6 @@ dependencies = [ "wasmparser 0.244.0", ] -[[package]] -name = "wasm-streams" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "wasm_thread" version = "0.3.3" diff --git a/crates/pm/Cargo.toml b/crates/pm/Cargo.toml index bb9d920f53..3e65417f51 100644 --- a/crates/pm/Cargo.toml +++ b/crates/pm/Cargo.toml @@ -48,7 +48,6 @@ reqwest = { version = "0.12", default-features = false, features = [ "json", "rustls-tls-native-roots", "socks", - "stream", ] } serde = { workspace = true } serde_json = { version = "1.0", features = ["preserve_order"] } diff --git a/crates/pm/src/util/downloader.rs b/crates/pm/src/util/downloader.rs index ca7f1570ca..f3f0253b4f 100644 --- a/crates/pm/src/util/downloader.rs +++ b/crates/pm/src/util/downloader.rs @@ -2,7 +2,6 @@ use std::sync::atomic::{AtomicU32, Ordering}; use anyhow::{Context, Result}; use bytes::{Bytes, BytesMut}; -use futures::StreamExt; use once_cell::sync::Lazy; use reqwest::{Client, StatusCode}; use tokio_retry::RetryIf; @@ -45,27 +44,32 @@ pub async fn download_bytes(url: &str, auth_token: Option<&str>) -> Result { - // Stream chunks instead of buffering the whole body in one - // await: each chunk feeds the live byte counter the spinner - // renders as `↓ 23.4 MB 8.2 MB/s`. The guard surfaces this - // request in the `N downloading` concurrency count. + // Read the body chunk-by-chunk via `Response::chunk` (which + // needs no reqwest `stream` feature) instead of buffering it + // in one await: each chunk feeds the live byte counter the + // spinner renders as `↓ 23.4 MB 8.2 MB/s`. The body is still + // fully buffered before return — the downstream extractor is + // not streaming — so `chunk` only buys the progress signal, + // not lower peak memory. The guard surfaces this request in + // the `N downloading` concurrency count. let _gauge = DownloadGuard::enter(); // Capacity hint only — capped so a bogus Content-Length // can't force a huge allocation; BytesMut grows as needed. const MAX_PREALLOC: u64 = 32 * 1024 * 1024; let hint = response.content_length().unwrap_or(0).min(MAX_PREALLOC); let mut buf = BytesMut::with_capacity(hint as usize); - let mut stream = response.bytes_stream(); - while let Some(chunk) = stream.next().await { - let chunk = chunk - .map_err(|e| RetryableError::Temporary(format!("Stream error: {e}")))?; + while let Some(chunk) = response + .chunk() + .await + .map_err(|e| RetryableError::Temporary(format!("Stream error: {e}")))? + { DOWNLOADED_BYTES.fetch_add(chunk.len() as u64, Ordering::Relaxed); buf.extend_from_slice(&chunk); }