# 上传任务队列（类迅雷：5 并发 + 其余自动等待）

- 日期：2026-09-12
- 对象：本机下载中心 8899 上传页（`~/Downloads/download-server.js` 的 `UPLOAD_JS`）
- 需求：**一次只传 5 个任务，其他任务自动进入等待状态**（像迅雷那样）

---

## 一、已实现的行为

| 特性 | 说明 |
|---|---|
| **并发上限 5** | 同时最多 5 个**文件**在传；其余进入等待队列，有空位自动补位（不是按分块，是按任务） |
| **等待状态可见** | 等待中的文件行显示 `⬜ 文件名 — 等待中…`，并随队列推进自动补位 |
| **任务汇总条** | 上传区下方实时显示：`任务 45 个（并发上限 5）：进行中 5 · 等待中 32 · 已完成 3 · 已跳过 8 · 失败 1` |
| **逐行状态图标** | `⬜` 等待中 / `🔍` 检查中 / `⬆️` 上传中(带百分比与块序号) / `⏭️` 已跳过 / `✅` 完成 / `❌` 失败 |
| **不再自动刷新** | 全部结束后**不再自动 reload**（否则刚出的结果看不到），改为在汇总条上给出 `🔄 刷新文件列表` 按钮 |
| **可调并发数** | 服务端环境变量 `DL_UP_PARALLEL`（默认 **5**，范围 1~20），注入页面 `window.__UP_PARALLEL__`；改后重启服务生效 |

## 二、为什么这样更好（与旧行为对比）

| | 旧版（45 个文件一起发起） | 新版（5 并发 + 等待队列） |
|---|---|---|
| 浏览器连接使用 | 45 个文件的分块请求抢 6 条连接，FIFO 轮转，**每个文件每轮只推进 1 块** | 5 个文件各占 1 条连接**顺序传自己的分块**，另 1 条连接留给状态查询 |
| 文件完成观感 | 所有文件齐头并进（长期都卡在 20~26 块），**要到队尾才集中完成** | **逐个完成**，看得见进度 |
| 失败影响面 | 一个文件失败会占住连接槽、拖累整体观感 | 单个文件失败只影响自己，立刻腾出名额给等待任务 |
| 服务端压力 | 几十个会话同时写盘、集中合并 | 最多 5 路写盘 + 合并闸门（2 路），负载平稳 |

## 三、生效方式（重要）

1. **服务已重启**（22:4x）：新前端已上线，同时把上一轮发现的 **io_uring SQPOLL 空转修复**一并生效 ——
   - `UV_USE_IO_URING=0` + `UV_THREADPOOL_SIZE=8`；
   - 实测新进程 **`iou-sqp` 线程数 = 0**（此前该线程独占 54.5% 单核，占进程 CPU 的 82%）；
   - 压测：640MB 分块上传+合并 1.1s（约 600MB/s，本机环回），进程总 CPU 约 1.7 核，**说明关掉 io_uring 毫无性能损失**。
2. **你需要刷新上传页面**（浏览器里还是旧 JS），然后**重新选择 `models` 文件夹**：
   - 已完整落盘的 13 个模型会**秒跳**（`⏭️`）；
   - 45 个未完成文件从断点继续（约 67GB 已收数据全部保留，不会重传）；
   - 之后就是「5 个在传 + 其余等待中」的迅雷式队列。
3. ⚠️ 重启瞬间有 **12 个文件的分块请求被中断**（`chilloutmix`、`anything-v5`、`Anything-V3-pruned`、`cetusMix-v3.5`、`canistermix4_v11`、`nekorayxl_v06W3`、`aiceKawaice_channel`、`CounterfeitV30_v30`、`CounterfeitV30_v30-pruned`、`final-prune-fp16`、`AbyssOrangeMix2_sfw`、`SDXLPixelArtBeta_beta`），页面上这些行会显示 ❌ —— 重新选择后自动续传，进度不丢。

## 四、调参与回滚

```ini
# /etc/systemd/system/dl-server.service
Environment=DL_UP_PARALLEL=5      # 改成 3 / 8 等即可；改完 systemctl daemon-reload && systemctl restart dl-server
Environment=UV_USE_IO_URING=0     # io_uring SQPOLL 空转修复（若需 io_uring 可删除本行）
Environment=UV_THREADPOOL_SIZE=8
```

回滚前端队列逻辑：`cp ~/Downloads/download-server.js.bak-20260912-214503 ~/Downloads/download-server.js` 后重启（注意备份里没有今天后续的其它修复，仅作参考）。

## 五、验证记录

- 页面注入：`window.__UP_PARALLEL__=5` ✅
- 页面含等待队列（`waitQueue`/`pump`）、等待态文案（`— 等待中…`）、汇总条（`up-summary`）、手动刷新按钮，且已移除自动 reload ✅
- 页面内联 JS 通过 `node --check` ✅
- 上传功能回归 **23/23 通过**（跳过去重、改名保存、分块续传、子目录落盘、页面注入）✅
- 新进程 `iou-sqp` 线程 **0 个**；640MB 压测 600MB/s、合并 HTTP 200 ✅

## 六、相关文件
- 服务端：`~/Downloads/download-server.js`（`UP_PARALLEL` 注入、`waitQueue`/`pump`、`up-summary`、`done(status)` 统计）
- 单元：`~/Downloads/dl-server.service` → `/etc/systemd/system/dl-server.service`

## 七、附：22:33 的一次自动重启（原因未定论）+ 新增日志留痕

**事件**：22:32:27 我手动重启（部署队列 + io_uring 修复）后，**22:33:26 systemd 又自动重启了一次**（`NRestarts=1`，`ExecMainStartTimestamp=22:33:26`），被替换的进程只活了约 50 秒。

**排查结论：未能定论**。已排除：
- 内存不足：`dmesg` 无 OOM、无 killed process；`free` 正常（可用 12GiB）
- 段错误：dmesg 无 segfault
- JS 异常：应用日志无 `[fatal]`、无堆栈
- 探活巡检误杀：`journalctl -u dl-healthcheck.service` 在该时段**无任何失败记录**（该脚本只在失败时写日志），`/run/dl-server-healthcheck.fail` 也不存在
- 人为重启：全局 journal 中该时段只有一条 `systemctl restart`（我 22:32:27 那条）

**影响**：`Restart=always` 在约 50 秒内自动拉起，**上传未中断**（监控采样显示字节数持续增长）。

**改进（已上线）** —— 针对「无时间戳、退出无痕迹」这两个导致无法定论的根本缺陷：
1. **日志加时间戳**：每行形如 `[2026/9/12 22:38:08] ...`，时间线可直接对账；
2. **启动留痕**：`[start] pid=… node=… io_uring=关闭 上传并发上限=5 合并并发=2`；
3. **退出留痕**：捕获 `SIGTERM/SIGINT/SIGHUP` 与 `exit`，打印 `[exit] 进程退出 code=… pid=…`（SIGKILL 无法捕获，但 systemd 会记 `Main process exited, code=killed`）。

下次再发生同类事件，可直接从日志时间戳与退出码定位，而不是靠推测。
