feat(download): download.* 三个超时/阈值键接进 schema 与引擎(M292 2/2)
P21 第 1 条:M289 的四个超时/阈值只挂在 `EngineConfig`,`DownloadTuning::apply_to` 只搬并发与
每源尝试次数 ⇒ 慢链路用户没有任何自救手段。出厂等价于「平均吞吐 ≥32 KiB/s 才活得下来」:
188MB 在 20 KiB/s 上要 160min > 100.3min 预算,必被杀;被杀后不再重试、不再换源,所以是
「下不完」而不是「慢一点」。
## 键(schema 1.9.0,新键推 minor)
| 键 | 默认 | 范围 | 落到 |
|---|---|---|---|
| `download.throughput_floor_kib` | 32 KiB/s | 4~4096 | `EngineConfig::min_avg_throughput`(×1024 → 字节/秒)|
| `download.stall_timeout_secs` | 120s | 30~600 | `EngineConfig::chunk_stall_timeout` |
| `download.response_timeout_secs` | 60s | 10~600 | `EngineConfig::response_timeout` |
三个默认值与 M289 的硬编码**逐值相等** ⇒ 不设 = 当前行为。上界(`MAX_*` 常量)就是「把挂住变成
无界」那道兜底:越界值在写入口被 schema 拒、引擎侧再回落一次出厂值。
## 通路
`RawTuning`(宿主只按 `KEY_*` 常量取裸值)→ `DownloadTuning::from_raw`(默认/区间/回落/KiB→字节
全在 core 一处)→ `apply_to` → `EngineConfig` → `stall::job_budget(total, floor, min_throughput)`。
`job_budget` 内部 `min_throughput.max(1)` 挡除零(设置层已拒 0,这是纵深防御)。
两个宿主都读六个键:`crates/cli/src/download.rs::tuning`、`presenter::host::settings::download::
tuning_of`。CLI 在**非出厂值**时往 stderr 打一行 `DownloadTuning::summary()`(`--dry-run` 也会经过
它;实测 `ull --data-dir … java install 21 --dry-run` 的 stderr 是
「下载调优:并发=8 单源尝试=3 吞吐下限=16KiB/s 无进展窗口=300s 单次请求上界=180s gh_proxy=跟随内置默认」,
出厂/非法值时一行都不打)。
## 文档口径(各只写一处)
- `stall.rs` 模块头:「改这几个数要付的代价」(调低下限换到的是「慢」不是「无界」;两档窗口是
单次尝试的界,最坏要乘候选数×尝试次数)+「数字从哪来」改成「默认值 / 能不能改」+ 补上 P21
第 3 条要的「`permit_timeout` 是按一次尝试反推,不是一个作业」。
## 两个纯搬移(为 400 行硬线)
- `config.rs` 423 → 261:`DownloadTuning` / `RawTuning` / `secs_in_range` / `impl` 整段搬到
`config/tuning.rs`(194 行),`pub use tuning::{DownloadTuning, RawTuning};` 保住原路径;
搬移块 172 行原样 diff = 0(无归一化)。
- `stall.rs` 440 → 343:测试搬到 `stall/tests.rs`(103 行),唯一变换是整体去 4 格缩进;两个
未改动的用例函数体与 HEAD 逐字符(去空白)相同。
## 用例
core(`config/tests.rs` + `stall/tests.rs` + `keys/download.rs`):默认值与 schema/引擎三处同源、
改设置 ⇒ 引擎配置与总期限都变、0 与越界回落且不除零、doc 里的数字、`summary()` 的生效值、键顺序。
cli:生产入口 `build_engine` 拿到三个新值 + `announce` 只在非出厂值时给行 + 非法值回落。
presenter:文件 → `tuning_of` → `EngineConfig` 三段一致 + 键名常量与 schema 登记绑定。
This commit is contained in:
1 parent
f4b5a73594
commit
2ecb33afae
14 files changed
+950
-211
No files matched your search
@@ -1,19 +1,25 @@
|
||||
//! `download.*` 的基础四键(M292 从 `keys.rs` 纯搬移拆出:那边 396 行、贴着 400 行硬线)。
|
||||
//! `download.*` 的七个键:四个基础键(M292 从 `keys.rs` 纯搬移拆出:那边 396 行、贴着 400 行硬线)
|
||||
//! + M292 新增的三个超时/阈值键。
|
||||
//!
|
||||
//! 搬移范围与顺序都未改:并发 / 单源尝试次数 / 地区判定 / 镜像偏好,四项逐字搬来,主表只留
|
||||
//! `.with_all(download::specs())` 一行。语义与「谁消费」仍写在各自的 `doc` 里(那是给界面与
|
||||
//! AI 的 JSON Schema `description`,不是本文件的注释),所以这里不再抄第二份。
|
||||
//!
|
||||
//! 与 [`super::proxy`] 的分工:代理 / gh-proxy 三个键在那边(它们问的是「怎么把包拿到手」,
|
||||
//! 而本文件这四个问的是「怎么排队、怎么重试、从哪个源拿」)。M292 新增的三个超时键
|
||||
//! (吞吐下限 / 无进展窗口 / 单次请求上界)也登记在本文件,它们与并发、重试同属「下载调优」,
|
||||
//! 而**改它们的代价**只写在一处:`core::download::stall` 的模块头。
|
||||
//! 而本文件这些问的是「怎么排队、怎么重试、从哪个源拿、允许慢到什么程度」)。
|
||||
//!
|
||||
//! **三个超时键的默认值与 M289 的硬编码逐值相等**(32 KiB/s / 120s / 60s),所以「不设」等于
|
||||
//! 「M292 之前的行为」。**改它们的代价**只写在一处:`core::download::stall` 的模块头——本文件与
|
||||
//! `download/config.rs` 都只指回那里,不各写一份。
|
||||
//!
|
||||
//! 三者的上界(`download::config` 的 `MAX_*` 常量)就是「把挂住变成无界」那道兜底:比它更大的值
|
||||
//! 在写入口被 schema 拒、引擎侧再回落一次出厂值。
|
||||
|
||||
use crate::setting::spec::{AiPolicy, SettingSpec};
|
||||
|
||||
use super::{MIRROR_PREFERENCES, REGIONS};
|
||||
|
||||
/// 四个基础键的完整登记项(主表一行 `.with_all(download::specs())`)。
|
||||
/// 七个键的完整登记项(主表一行 `.with_all(download::specs())`)。
|
||||
#[must_use]
|
||||
pub fn specs() -> Vec<SettingSpec> {
|
||||
vec![
|
||||
@@ -45,5 +51,68 @@ pub fn specs() -> Vec<SettingSpec> {
|
||||
AiPolicy::audited(),
|
||||
"镜像偏好:auto(健康度评分)/ official(官方源优先)/ mirror(镜像优先)",
|
||||
),
|
||||
// M292:三个超时/阈值键。默认值与 M289 的硬编码逐值相同(不设 = 当前行为),
|
||||
// 「改它们要付什么代价」只写在 `core::download::stall` 的模块头,这里只给范围与默认值。
|
||||
SettingSpec::int(
|
||||
crate::download::config::KEY_THROUGHPUT_FLOOR,
|
||||
i64::from(crate::download::config::DEFAULT_THROUGHPUT_FLOOR_KIB),
|
||||
(
|
||||
i64::from(crate::download::config::MIN_THROUGHPUT_FLOOR_KIB),
|
||||
i64::from(crate::download::config::MAX_THROUGHPUT_FLOOR_KIB),
|
||||
),
|
||||
AiPolicy::silent(),
|
||||
"分块下载的最低下限平均吞吐(KiB/s,4~4096,出厂 32):下载总期限 = 总字节 ÷ 这个数。调低能让慢链路下完,代价是真挂住的作业占用更久(见 core::download::stall 模块头)",
|
||||
),
|
||||
SettingSpec::int(
|
||||
crate::download::config::KEY_CHUNK_STALL,
|
||||
crate::download::config::DEFAULT_CHUNK_STALL.as_secs() as i64,
|
||||
(
|
||||
i64::from(crate::download::config::MIN_CHUNK_STALL_SECS),
|
||||
i64::from(crate::download::config::MAX_CHUNK_STALL_SECS),
|
||||
),
|
||||
AiPolicy::silent(),
|
||||
"分块下载「这么久没有任何新字节」就判失速(秒,30~600,出厂 120)。调大 = 卡住时报错更晚;与重试次数相乘后才是最坏等待",
|
||||
),
|
||||
SettingSpec::int(
|
||||
crate::download::config::KEY_RESPONSE_TIMEOUT,
|
||||
crate::download::config::DEFAULT_RESPONSE_TIMEOUT.as_secs() as i64,
|
||||
(
|
||||
i64::from(crate::download::config::MIN_RESPONSE_TIMEOUT_SECS),
|
||||
i64::from(crate::download::config::MAX_RESPONSE_TIMEOUT_SECS),
|
||||
),
|
||||
AiPolicy::silent(),
|
||||
"单次请求等响应头的上界(秒,10~600,出厂 60):只算到响应头到手,不含正文,所以对慢链路下大文件是安全的一档",
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// 七项的键名与顺序。搬移若换了顺序,`launcher_schema()` 的 `specs()` 投影顺序也跟着变
|
||||
/// (那是可见行为:`ull config list` / AI 的 JSON Schema 都按它排),而 `keys.rs` 里那行
|
||||
/// `.with_all(download::specs())` 看不出这件事。
|
||||
///
|
||||
/// 默认值 / 区间 / 与 `EngineConfig` 的同源关系不在这里守——那是
|
||||
/// `download/config/tests.rs` 的 `the_timeout_keys_are_single_sourced_and_default_to_todays_values`
|
||||
/// (跨 crate 模块的那份口径只写一处,别在这里抄第二份)。
|
||||
///
|
||||
/// 变异证明:把三个新键挪到 `region` 前面 ⇒ 红;把 `stall_timeout_secs` 改名 ⇒ 红。
|
||||
#[test]
|
||||
fn the_download_keys_are_registered_in_this_order() {
|
||||
let keys: Vec<&str> = specs().iter().map(|spec| spec.key).collect();
|
||||
assert_eq!(
|
||||
keys,
|
||||
[
|
||||
"download.concurrency",
|
||||
"download.retry_limit",
|
||||
"download.region",
|
||||
"download.mirror_preference",
|
||||
"download.throughput_floor_kib",
|
||||
"download.stall_timeout_secs",
|
||||
"download.response_timeout_secs",
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user