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
@@ -264,3 +264,82 @@ fn missing_or_illegal_values_fall_back_to_the_factory_defaults_silently() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// **M292 / P21 判据的 CLI 侧闭合**:三个超时键从 `launcher.json` 进到**生产路径**的引擎配置,而且
|
||||
/// 改过设置时终端上那行说明写的是**生效值**。
|
||||
///
|
||||
/// 为什么断言 [`super::announce`]:台账原文的判据是「改完能在 `--dry-run` 或日志里看到生效值」。
|
||||
/// CLI 没有打印调优的 `--dry-run`(那几处 `--dry-run` 只出计划清单 / 版本串),所以这一行走
|
||||
/// stderr,而它的内容由 `announce` 产出——**断言这个函数就是断言终端上那行**(不是另拼一份旁证)。
|
||||
/// 走 stderr 而不是 `logs/launcher.log` 的理由见 `announce` 的文档:那个日志的目录是进程级全局,
|
||||
/// 在装配点调 `set_dir` 会让一个读路径改全局状态(M292 第一版就是这么写的,`commands::sessions`
|
||||
/// 的 launcher.log 用例当场红了)。
|
||||
///
|
||||
/// 抓得住:① `download::tuning` 少读一个键 ⇒ 引擎字段与那行说明同时红;② `announce` 对出厂值也返回
|
||||
/// `Some` ⇒ 第二段红(每个 `ull install` 都多一行噪声);③ 把汇总写成常量 ⇒ 说明里的数字对不上。
|
||||
/// 抓不住:真机慢链路上「预算到底够不够」(离线环境没有真链路,见 M292 报告的未验项)。
|
||||
#[test]
|
||||
fn the_timeout_settings_reach_the_engine_and_the_console_line() {
|
||||
use std::time::Duration;
|
||||
|
||||
let dir = TempDir::new("timeouts");
|
||||
let ctx = ctx_at(&dir);
|
||||
write_launcher_config(
|
||||
&ctx,
|
||||
json!({ "download": {
|
||||
"throughput_floor_kib": 16,
|
||||
"stall_timeout_secs": 300,
|
||||
"response_timeout_secs": 180,
|
||||
}}),
|
||||
);
|
||||
|
||||
// 出厂档不该打那一行(否则每个命令都多一行噪声)。
|
||||
assert!(
|
||||
super::announce(&DownloadTuning::default()).is_none(),
|
||||
"出厂值不该打说明"
|
||||
);
|
||||
|
||||
// 生产路径:`ull install` 取引擎的那个入口(四条命令共用这一处装配)。
|
||||
let engine = crate::commands::install::build_engine(&ctx);
|
||||
assert_eq!(
|
||||
engine.config().min_avg_throughput,
|
||||
16 * 1024,
|
||||
"throughput_floor_kib=16 ⇒ 引擎里是 16 KiB/s(字节/秒)"
|
||||
);
|
||||
assert_eq!(
|
||||
engine.config().chunk_stall_timeout,
|
||||
Duration::from_secs(300)
|
||||
);
|
||||
assert_eq!(engine.config().response_timeout, Duration::from_secs(180));
|
||||
|
||||
// 终端上那行(同一份 `summary()`,同一个 `announce`)。
|
||||
let line = super::announce(&super::tuning(&ctx)).expect("改过设置 ⇒ 该有一行说明");
|
||||
assert!(line.contains("吞吐下限=16KiB/s"), "{line}");
|
||||
assert!(line.contains("无进展窗口=300s"), "{line}");
|
||||
assert!(line.contains("单次请求上界=180s"), "{line}");
|
||||
|
||||
// 不设 ⇒ 引擎回到出厂值,终端也不再打说明(「不设 = 当前行为」的 CLI 侧证据)。
|
||||
write_launcher_config(&ctx, json!({ "download": {} }));
|
||||
let engine = crate::commands::install::build_engine(&ctx);
|
||||
assert_eq!(
|
||||
engine.config().response_timeout,
|
||||
Duration::from_secs(60),
|
||||
"未设置 ⇒ 出厂 60s"
|
||||
);
|
||||
assert!(
|
||||
super::announce(&super::tuning(&ctx)).is_none(),
|
||||
"全出厂值不该打说明"
|
||||
);
|
||||
|
||||
// 非法值:三个键一起写成 0 / 越界 ⇒ 回落出厂,且说明行不出现(不是「打一行假的」)。
|
||||
write_launcher_config(
|
||||
&ctx,
|
||||
json!({ "download": {
|
||||
"throughput_floor_kib": 0,
|
||||
"stall_timeout_secs": 0,
|
||||
"response_timeout_secs": 99_999,
|
||||
}}),
|
||||
);
|
||||
assert_eq!(super::tuning(&ctx), DownloadTuning::default());
|
||||
assert!(super::announce(&super::tuning(&ctx)).is_none());
|
||||
}
|
||||
Reference in new issue
Block a user