Skip to content

tools: 断点续传探针(复用生产字段构造) - #5

Merged
mingyangbest merged 2 commits into
mainfrom
tools/probe-resume
Nov 3, 2026
Merged

mingyangbest merged 2 commits into
mainfrom
tools/probe-resume

Conversation

@mingyangbest

Copy link
Copy Markdown
Owner

这是什么

tools/probe_resume.py:断点续传的前置验证,不涉及任何生产行为。它只回答一个问题——服务端还认不认几小时前的那个 uniqueId。这个问题的答案直接决定续传怎么做,所以先探再写。

脚手架,不混进 v0.2.1。合并后跑完再决定它是留作诊断工具还是删掉。

为了让探针「探的是生产」而做的小抽出

原来 upload() 里内联构造分片字段。探针如果照抄一份,测到的就是探针自己的实现,阳性结果迁移不到真续传上。所以抽出:

  • file_type_of() / file_info_plain() / enc_file_info() / new_unique_id()
  • Wopan.part_fields() —— 单片 multipart 字段的唯一构造点

upload() 与探针都走这一份。行为不变:用一个 fake _upload_part 抓包,逐字段比对过字段名集合、totalPart/partSize/partIndex 序列、uniqueId 复用、fileInfo 解密后的 fileName/fileSize/fileType/directoryId/batchNo。

探针自己负责的部分

  • 两阶段:start 传 1..100 片(云端留 800MiB 残片)→ 进程退出 → 隔 ≥2 小时 resume 从第 101 片续(默认总 150 片 / 1.2GiB)。不在进程里 sleep,被杀不白等。
  • journal 原子写(tmp + fsync + replace),记录 TP_DATA;start/resume 必须同一个 TP_DATA,否则 resume 找不到 journal 并明确提示。
  • token 指纹留痕:若这 2 小时里 token 续期过,「会话超时」和「会话绑旧 token」两种解释会同时成立,指纹与 token_renewed 一起写进报告,避免误判归因。
  • 四态判定:0 接受 / 2 报错 / 3 不完整 / 4 未观测到(网络三次无响应、或拿不到内容哈希)。1 留给脚本自身错误。断网不会伪装成服务端拒收。
  • SHA256 来源写死:优先云端返回的 sha256 字段;缺失则下载回来自己算(1.2GiB 一次可接受),报告记 hash_source=cloud|download;两者都拿不到判 4。
  • cleanup 独立子命令:读 journal(坏了也能跑)删云端文件,目录空了才删目录,再清本地与 journal。

验证到哪一步

  • 本地离线验证(未连真实云盘):四态判定各跑一次、hash_source 两条分支、下载失败→4、token 续期留痕、坏 journal 拦住、cleanup 三种情形(坏 journal / 目录非空 / 目录已空)。
  • pyflakes + compileall 全过。
  • 真机运行尚未执行,按约定先审代码再部署。

用法

docker cp tools/probe_resume.py tpbackup:/app/probe_resume.py
docker exec -it tpbackup python3 /app/probe_resume.py start
# 等 ≥2 小时
docker exec -it tpbackup python3 /app/probe_resume.py resume
docker exec -it tpbackup python3 /app/probe_resume.py cleanup

探针要回答的只有一个问题:服务端认不认几小时前的 uniqueId。
所以它不能自己手抄一份分片字段,否则测的是探针自己的实现。为此把
upload() 里的三件套抽成模块级函数(file_type_of / file_info_plain /
enc_file_info / new_unique_id)与 Wopan.part_fields(),upload 与探针共用。

行为不变:字段名、取值、顺序、partSize/partIndex 语义全部一致(用
fake _upload_part 抓包逐字段比对过)。

探针本身:两阶段(start 传 100 片 / 隔 2 小时 resume 从 101 片续),
journal 原子写,token 指纹留痕,四态判定 0/2/3/4,SHA256 优先云端字段、
缺了就下载回算并在报告里记 hash_source,cleanup 独立子命令。
- journal 收权限到 600(有 token 指纹与会话标识,按 token.json 同标准)
- 只有第 101 片一次定论;1..100 与 102..150 跟着 upload() 的 3 次重试走
  (频控 9999 抖动不该废掉一次 2 小时的探针),证据里照记 part/code
- verify 的 size 不符先隔 10 秒复查再判「不完整」,排除服务端还没拼完的中间态
- connect() 明确走 _connect(load + maybe_renew + 连通自检),并打印令牌剩余
  天数;不足 0.2 天时提前警告「这期间可能续期,归因会多一个变量」
@mingyangbest

Copy link
Copy Markdown
Owner Author

CI 记录:run #15(e750b20)的 docker job 挂了 19 秒,定位为镜像源瞬时不可用,与本 PR 无关:

#10 [5/6] RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
ERROR: Could not find a version that satisfies the requirement fastapi==0.115.6 (from versions: none)

requirements.txt 本 PR 未改动,同一份文件在 #14 已构建成功;失败点在 pip 拉包(不是 apt、不是 docker pull),且报的是 from versions: none——清华镜像当时没返回索引。

已 Re-run failed jobs:docker + static 均通过,run 状态 success。按 transient 处理,不改代码。

若以后同类失败反复出现(比如一天内多次),可以在 Dockerfile 里加一个 fallback index(--extra-index-url https://pypi.org/simple,或换阿里云镜像),那是独立的健壮性改动,不放进本 PR。

mingyangbest added a commit that referenced this pull request Nov 3, 2026
断点续传探针 #5:上传字段构造抽出(file_info_plain / enc_file_info /
new_unique_id / Wopan.part_fields)+ 探针脚本,并修掉 done 不落盘的缺陷。

合并前做的等价性核对:part_fields 与 v0.2.1 内联构造逐键一致(11 个键、
str() 口径相同),file_info_plain 仍为 6 键且 batchNo 可由外部固定,
fileType 分派 5/1/2 不变。单测 17 项、编译、pyflakes 均绿。

探针留作诊断工具:分片级续传实现后要用它回归。
@mingyangbest
mingyangbest merged commit e750b20 into main Nov 3, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant