fix: a MemoryError out of a share write no longer orphans the staged file

Host hit 0 GB physical free / 76.8 of 79.6 GB commit while both tiers ran (pagefile
capped at 32 GB on a 97%-full D:), and the Dokan write of the batch's largest output
raised MemoryError. except OSError does not catch that, so the item was logged
'!! FAIL MemoryError:' with an empty message and the complete 493 MB <live>.new.mp4
stayed on the share; 8 such orphans had accumulated.

- _sha_remote / copy_remote_to_local / copy_local_to_remote: except (OSError, MemoryError)
  so a transient host hiccup retries instead of failing the item
- same widening for both partial-file cleanups
- per-item handler: delete <dest>.new.mp4 unconditionally (os.remove + ignore
  FileNotFoundError, never gated on os.path.exists - stale metadata on this share)

Verified by injecting MemoryError: the upload path returns False with the partial
deleted, the source read retries then raises a clean IOError, the hash retries twice.
This commit is contained in:
2026-10-09 11:38:40 +08:00
parent 4385724c5e
commit bd415d2af5
3 changed files with 73 additions and 7 deletions
+42 -2
View File
@@ -1,10 +1,13 @@
# STATE — 樊登读书会媒体库转码(2026-10-08 10:50 更新)
# STATE — 樊登读书会媒体库转码(2026-10-09 11:40 更新)
给下一个会话的交接。**权威运行器:`scripts/library_run.py`**(不要再恢复旧的 `hevc_lib.py`)。
## 现在在跑什么
两个 HEVC 档在跑,各自独立的锁(`run-x265.lock` / `run-nvenc.lock`;x265 = E 核,nvenc = GPU ASIC,互不争抢)。
**两档都停着** —— 2026-10-09 11:30 业主叫停,先修 bug 再重启;进程已按 PID 全部清掉,GPU encoder 0 %。
**重启前先读「第 10 个 bug:MemoryError」和「8 个 `.new.mp4` 残留」两节**,并先腾内存。
原设计:两个 HEVC 档在跑,各自独立的锁(`run-x265.lock` / `run-nvenc.lock`;x265 = E 核,nvenc = GPU ASIC,互不争抢)。
**它们不在 Hermes 的进程管理里** → `process_manage` 看不到,也没有退出通知:只能看日志、按 PID 杀。
@@ -112,6 +115,43 @@ python scripts/library_run.py --tier x265 --limit 3 --keep-local
python scripts/library_run.py --dry-run --limit 20
```
## 第 10 个 bug:MemoryError 会在共享盘上留孤儿(2026-10-09)
现象:11:19:31 与 11:19:37 连续两次 `!! FAIL MemoryError:`(消息空白),两次相隔 6 秒 ——
一次是《崇祯传》**装盘**(写 493 MB 到 RaiDrive),一次是下一项**读源**。
根因是**整机内存/提交额度耗尽**,不是文件问题:
```
物理可用 0.0 GB / 48 GB 已提交 76.8 GB / 上限 79.6 GB(96.5 %)
pagefile d: 初始 8 GB / 上限 32 GB —— 但 D: 只剩 19 GB(97 % 满),长不上去
大户:Chrome ≈4 GB、Hermes ≈3.7 GB(3 个进程)、upscayl 1.17 GB、whisper-server 0.83 GB、Discord 0.76 GB
```
`copy_*` / `_sha_remote` 当时只 `except OSError`,**而 `MemoryError` 不是 OSError** → 直接冒到
per-item 兜底;删半截 `remote_tmp` 的清理写在 `except OSError` 里 → **半截/完整的 `.new.mp4` 留在共享盘上**。
已修(本轮,`scripts/library_run.py`):
1. `_sha_remote` / `copy_remote_to_local` / `copy_local_to_remote` 的 `except OSError` → `except (OSError, MemoryError)`(瞬时抖动变重试,不是 FAIL);
2. `copy_local_to_remote` 内的清理、读回不符时的清理同样放宽;
3. **per-item 兜底加了无条件删除 `<dest>.new.mp4`**(用 `os.remove` + 忽略 `FileNotFoundError`,**不用** `os.path.exists`,因为该共享盘的元数据可能是陈旧的)。
实测(注入 MemoryError):上传路径不再抛异常、半截文件被删;读源路径重试后抛干净的 `IOError`;
`_sha_remote` 重试 2 次后抛出。
**内存仍是硬前提**:物理可用 0 GB / 提交 96.5 % 时重启,第一项照样会 FAIL(现在至少不留残留)。
重启前建议:关掉 upscayl / 多余 Chrome 标签 / Discord 腾出 5-6 GB;pagefile 上限别放在 97 % 满的 D:。
## 8 个 `.new.mp4` 残留(2026-10-09 11:35 逐个探过)
原档都在,没丢东西。7 个是**半截前缀**(内容早停 → 没用,删掉让下一轮重做);
**只有一个是完整的、可以救回来**:
| 文件 | 暂存 | 判定 |
|---|---|---|
| 2022年/0115《崇祯传》 | 493 MB | ✅ **完整**(3616.8 s 与源一致、内容到 3609 s)→ 校验后可改名安装 |
| 0622人生海海 / » 0819 李白 / 0527 洪业 / 0807东京传 / 20190601被管教的勇气 / 1230 不安的哲学 / 0511原生家庭 | 8 MB–165 MB | ❌ 半截,删 |
## 红线(用户明确要求过)
- **绝不删除/覆盖快照原档**(`#snapshot` 是唯一的干净档案)。