Repository navigation
fix(seed): make 189 CAS rapid upload carry the right file, hashes and slice size - #3341
Merged
Merged
Conversation
added 9 commits
November 4, 2026 22:43
CAS seeds only carry MD5 + slice MD5s, but the quick-save path rebuilt the rapid-upload request through a torrent container, which requires SHA-1 piece hashes. Every 189 秒传 therefore failed before any request was sent and reported 'target driver cannot reuse the available hashes'. - build the rapid-upload request directly from the seed file, keeping the whole MD5, slice MD5s, the drive's slice size and the legacy slice digest - let 189pc run initMultiUpload/checkTransSecond/commitMultiUploadFile when slice hashes are available, falling back to the legacy whole-MD5 upload - surface the real rapid-upload error instead of the generic message
…error The CAS path now reaches 189's upload API, and the first real rejection is UserDayFlowOverLimited (account daily upload flow exhausted). Treat it as a terminal account state instead of retrying: - add errs.DailyFlowLimitExceeded and map UserDayFlowOverLimited / 'flow is out' onto it (189 returns it as a business code inside HTTP 200) - skip the legacy whole-MD5 fallback once the account is limited - report 'rapid upload failed: ...' with the real reason and mark the rest of the batch unavailable instead of one request per file
Generating a .cas seed from a .cas file produced an artifact with exactly the same name in the same folder, so PutDirectly overwrote the original seed with a seed-of-seed: the payload then described the few-hundred-byte metadata file instead of the video. Rapid upload of that seed 'succeeds' and only creates another .cas, which looks like CAS 秒传 doing nothing. - skip .oss/.torrent/.cas/.cas.torrent when expanding a directory - reject an explicit selection of a seed container with an actionable message - refuse to write an artifact over one of its own source files
…igest CAS 判重只比较最终分片摘要(逐片 MD5 汇总后再取 MD5),而切法由文件大小固定:10MiB / 20MiB / 更大文件按 1999 片上限向上取 10MiB 的整数倍。之前把 sliceMd5 当成「可变分片的哈希」、总是从逐片列表现算,切法一变就对不上云端。 - add torrent.CASSliceSize / CASPartCount / CASUniformSliceSize as the single source of truth; 189pc.partSize delegates to it - .cas writes the final digest in sliceMd5 and the per-slice list in partsMd5; slice_md5s stays readable for old files - rapid upload prefers the explicit sliceMd5 and only derives it from parts when absent - generation, recalculate and inline conversion hash CAS files at CASSliceSize per file and drop parts that do not match that slicing - piece_size no longer influences CAS hashing
A .cas generated from another .cas documents the metadata file itself (name ends with .cas, a few hundred bytes). Rapid-uploading it just creates another same-named .cas in the target folder, which looks like CAS 秒传 doing nothing. Reject that case with an actionable message instead.
- extract seedRapidUploadBlocker so the guard is testable - add an end-to-end test proving a legacy 5-field .cas resolves to the video name, size, slice size and final slice MD5 - add a test that seed containers are refused for rapid upload
Turn the error strings, task statuses and progress labels of the transfer-seed / torrent feature into English, including the 189 CAS rapid-upload path and the front-end facing guards. Comments and unrelated log lines are untouched.
…oad rule CAS slice size must be exactly what 189 uses when uploading, otherwise the sliceMd5 submitted for rapid upload can never match the digest the drive stored. - CASSliceSize now copies 189pc partSize expression by expression (same DEFAULT constant, same float expression, same 5x lower bound) - expectedCASPartCount mirrors fastUpload's count: one slice when size <= sliceSize, otherwise ceil(size/sliceSize) - add cross-package tests comparing CASSliceSize/CASPartCount against a verbatim copy of the 189 rule, and partSize against CASSliceSize
P1 FsStream hashed uploads with seedPieceSize() while the sidecar declared the CAS slice size; unknown-size CAS uploads now fail before writing the file instead of after. P1 buildCASFileEntry only compared piece counts, so 8MiB slices could be reused as 10MiB CAS slices; it now requires the slicing to match. P2 multi-slice payloads without sliceMd5 are rejected instead of falling back to the whole-file MD5, and oversized CAS slice sizes are clamped for the seed container only.
xrgzs
approved these changes
Nov 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary / 摘要
修复
189pc(天翼云)CAS 秒传的四个独立缺陷,以及一个会覆盖用户种子的设计问题。用户可感知的行为变化:
.cas秒传不再必然报target driver cannot reuse the available hashes,请求会真正发到云端。.cas」。秒传失败:云端不存在该文件(fileMD5=…, sliceMD5=…, size=…)。天翼云盘账号当日上传流量已用尽…,同批其余文件不再逐个请求。.cas文件再执行「生成种子」会被拒绝,且不会覆盖原.cas。.cas。重要实现变化:
EncodeSeed(seed, "torrent") → Decode中转),避免 torrent 容器强制要求 SHA-1 pieces 导致 CAS 种子必然失败。
189pc:有分片信息时走initMultiUpload → checkTransSecond → commitMultiUploadFile,否则退回旧的
createUploadFile.action;CAS 报错更具体时优先上报。sliceMd5是最终分片摘要(云端判重唯一比较值),显式值优先,逐片列表只在缺失时用于推算。
torrent.CASSliceSize(文件大小)决定(10MiB / 20MiB / 更大文件按 1999 片上限向上取 10MiB 的整数倍),
piece_size对 CAS 不再生效;189pc.partSize改为委托同一实现。.cas新增partsMd5(逐片 MD5),仅在切法与天翼规则一致时写出;sliceMd5恒写最终摘要。CASSliceSize计算哈希;多文件跨分片档位时种子级只保留一种切法,其余文件只留最终摘要。
errs.DailyFlowLimitExceeded终止态错误。配置、存储、API 或兼容性变化:
.cas载荷新增可选字段partsMd5;不再写出旧的slice_md5s(仍可读取)。兼容性:新代码可读旧
.cas(含只有md5+sliceMd5的五字段格式);旧代码读新.cas时可只凭
sliceMd5+slice_size完成秒传。无配置项变更;
piece_size对 CAS 的语义变更为「忽略」。This PR has breaking changes.
/ 此 PR 包含破坏性变更。
This PR changes public API, config, storage format, or migration behavior.
/ 此 PR 修改了公开 API、配置、存储格式或迁移行为。
This PR requires corresponding changes in related repositories.
/ 此 PR 需要关联仓库同步修改。
Related repository PRs / 关联仓库 PR:
只读提示(后端已忽略该值,当前 UI 对大文件仍显示 10 MiB,属提示性问题)。
Related Issues / 关联 Issue
无关联 Issue(问题由实际使用中复现并定位),本节可删除。
Testing / 测试
go test ./...执行过的命令:
go build ./... go vet ./server/handles/... ./internal/fs/... ./pkg/torrent/... ./drivers/189pc/... ./internal/driver/... go test -p 1 ./server/handles/... ./internal/fs/... ./pkg/torrent/... ./drivers/189pc/... -count=1结果:以上全部通过(含新增回归测试)。未执行全量
go test ./...(仓库含大量驱动用例,本轮只覆盖受影响包)。并行跑多包时曾出现一次
server/handles中 media review 用例偶发失败,连续重跑(含
-count=1与-p 1)均通过,与本次改动无共享代码或全局状态。手动验证:在真实 189 实例上复现并定位了三个阶段的报错(哈希不可用 / 账号限流 / 生成
.cas),对照实例日志确认请求已按预期到达云端;成功路径由单测覆盖(五字段
.cas→ 秒传请求名、size、分片大小、
sliceMd5均取原值)。未在真实账号上完成一次成功的大文件秒传(受账号当日流量配额限制)。新增测试:
TestLegacyCASToRapidRequestKeepsVideoName:五字段.cas解析后,秒传请求名必须是视频名,size / 分片大小 /
sliceMd5取原值,且不臆造逐片哈希TestSeedRapidUploadBlockerRejectsSeedOfSeed:描述种子容器的种子禁止秒传TestBuildSeedFileRapidUploadRequestKeepsCASHashMatrixTestCASSliceSizeMatches189Rule/TestCASUniformSliceSizeTestEncodeCASStoresFinalSliceMD5AndParts/TestDecodeCASWithFinalSliceMD5Only/TestDecodeCASAcceptsLegacySliceMD5s/TestCASSliceDigestPrefersFinalDigestTestIsSeedContainerName/TestIsDayFlowOverLimited手工验收步骤
md5+sliceMd5的.cas(例如 3GB 视频)秒传到 189:请求名应为视频名,命中后生成视频文件;云端没有该数据时返回带
fileMD5/sliceMD5/size的具体错误。.cas文件执行「生成种子」:应被拒绝,且原.cas不被覆盖。.cas自身」的种子:应报错说明,而不是生成新的.cas。Checklist / 检查清单
/ 我已阅读 CONTRIBUTING。
/ 我确认此贡献符合仓库许可证、贡献规范和行为准则。
gofmt,go fmt, orprettierwhere applicable./ 我已按适用情况使用
gofmt、go fmt或prettier格式化变更代码。/ 我已在适用情况下请求相关维护者或代码所有者审查。
AI Disclosure / AI 使用声明
/ 此 PR 包含 AI 辅助内容。
Tools used / 使用工具:
Usage scope / 使用范围:
Code generation / 代码生成
Refactoring / 重构
Documentation / 文档
Tests / 测试
Translation / 翻译
Review assistance / 审查辅助
I have reviewed and validated all AI-assisted content included in this PR.
/ 我已审核并验证此 PR 中的所有 AI 辅助内容。
I have ensured that all AI-assisted commits include
Co-Authored-Byattribution./ 我已确保所有 AI 辅助提交都包含
Co-Authored-By归属信息。I can reproduce all AI-assisted content included in this PR without any AI tools.
/ 我可以在没有任何 AI 工具的情况下重现此 PR 中包含的所有 AI 辅助内容。