fix(download): 补齐 download_bytes 与等许可两处无界 await,预算耗尽不再重试(M289 r1)
r1 复审 p2-1:`stall.rs` 那句「全仓只有两处无界 await」是假话。真实清单五处, M289 之前只修了两处;这次补齐: * `bytes.rs::fetch_bytes` 的 `send()` 改走 `http::send_bounded`(与文件下载同一个构造点); 整包 `response.bytes()` 换成 `bytes_stream` + `http::read_step` 的空闲判定,并边读边卡 `MAX_IN_MEMORY_BYTES`(`Content-Length` 可缺失/可撒谎)。生产调用点:版本清单、 版本 JSON、Modrinth 元数据。 * `engine::acquire` 的 `acquire_owned()` 加上界(`EngineConfig::permit_timeout`,默认 120min ≥ 单个作业总期限),到点报新阶段 `StallStage::AcquirePermit`(「等许可」), 不冒充「服务器不给数据」。这就是「卡死作业攥着许可把队列堵死」的连坐形态。 * `stall.rs` 模块头改成一张「哪些入口有界、谁托管给谁」的表,并写清五处 await 的真实清单。 r1 复审 p2-2:总期限用完是链路级判决,不再按满预算重试/换源: * `DownloadError::job_budget_exhausted()` + `DownloadError::is_retryable()`(覆盖 `kind.is_retryable()`),引擎与 `download_bytes` 都改用它; * 引擎在预算耗尽时直接结束候选循环,不再换源(同一份字节、同一条链路,换源只会再 烧满一整个预算)。188MB/20 KiB/s 的用户不再从「1.6 小时慢慢下完」变成「几小时反复重下」。 * `stall.rs` 写明「慢链路照样活」的前提 = **平均吞吐 ≥ 32 KiB/s**,以及它挡掉哪类链路; `download.*` schema 接线留给另一个 mission(`setting/keys.rs` 不在 scope)。 顺带(复审 p3): * `transfer.rs` 的读空闲统一走 `EngineConfig::deadlines()`(不再各读字段); * `verify_part` 补一条代价说明:`spawn_blocking` 到点不可取消,可能让同名重建拿到 `Access is denied`(**该假设未验**,注释里已标); * `engine.rs` 因这轮新增又贴到 410 行 ⇒ `finish` / `materialize` / `millis` 逐字节搬到 `engine/finish.rs`(`engine::millis` 路径用 `pub(crate) use` 保持可用)。 新增用例(全离线、<2s):`download_bytes` 静默服务端 / 正文中途卡住、等许可超时、 预算耗尽不重试(`max_attempts_per_source = 3` 下仍只发一轮)、预算耗尽不换源 (官方源 `hits == 0`)。
This commit is contained in:
1 parent
cc0def3415
commit
d6f862a43c
13 files changed
+571
-146
No files matched your search
@@ -215,6 +215,12 @@ impl DownloadEngine {
|
||||
// 整文件重算是同步阻塞 IO:挪到 blocking 池并套上界,否则「校验阶段卡住」
|
||||
// (网络盘掉线、杀软锁文件)照样是永久挂住,且它发生在字节全部收完之后,
|
||||
// 从进度上看与「下载完成」一模一样。
|
||||
//
|
||||
// 代价(复审 r1 p3-1,未验):`spawn_blocking` 的线程**不可取消**——到点后它还在
|
||||
// 读文件、还持着句柄,重试会再堆一个(每源最多 3 个)。**假设未验**:Windows 上
|
||||
// 那个句柄可能让随后的同名重建(`attempt_chunked` 的 `OpenOptions::create(true)`)
|
||||
// 拿到 `Access is denied`,表现为「预分配中间文件失败」而不是干净重试
|
||||
// (`PartFile::discard` 忽略错误)。仍然有界、有分类,只是诊断会拐一道。
|
||||
let on_disk = part.len();
|
||||
let path = part.path().to_path_buf();
|
||||
let algo = integrity.algo;
|
||||
|
||||
Reference in new issue
Block a user