Skip to content

fix(cache): 缓存损坏或临时云存储错误时降级重建 - #32

Merged
tigerBeA merged 5 commits into
mainfrom
lisw/cache-corruption-resilience
Jul 9, 2026
Merged

fix(cache): 缓存损坏或临时云存储错误时降级重建#32
tigerBeA merged 5 commits into
mainfrom
lisw/cache-corruption-resilience

Conversation

@tigerBeA

@tigerBeA tigerBeA commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

解决什么问题

quack S3 缓存链路依赖第三方代理,近期在 merge queue 中多次出现同一类不稳定故障:

  • 下载阶段拿到被截断的 .tar.zst,触发 zstandard.ZstdError: did not decompress full frame
  • 上传阶段 S3 返回 IncompleteBody,即实际 body 长度小于 Content-Length

这些属于开发者无法直接控制的 transient 基础设施问题,不应该让业务 job 随机失败;但配置、权限、bucket 不存在等开发者能感知并修复的问题也不应该被 catch 隐藏。

这版补上了 boto3/botocore transfer wrapper 的真实异常路径,并把 catch 边界收敛到 SDK 错误基类,transient 降级仍由白名单统一判断。

证据背景

2026-07-08 下午(Asia/Shanghai)merge queue 连续 4 次被同一个 sandbox:generate 缓存链路拖挂。前三次是下载后的 zstd 解压失败,第四次扩散成上传阶段 IncompleteBody

  • 13:05 moxt run #33753 / PR #10792:project-lintsandbox-build 依赖 sandbox:generate,从云端下载缓存后解压失败。
  • 13:07 moxt run #33755 / PR #10784:同类 zstd decompress 失败。
  • 13:12 moxt run #33756 / PR #10784:同类 zstd decompress 失败。
  • 13:49 moxt run #33773 / PR #10784:上传缓存时 IncompleteBody 失败,说明不仅下载会拿到残缺 body,上传路径也会被代理截断。

关键日志形态:

zstandard.backend_c.ZstdError: decompression error: did not decompress full frame
boto3.exceptions.S3UploadFailedError: Failed to upload ... An error occurred (IncompleteBody) when calling the PutObject operation: You did not provide the number of bytes specified by the Content-Length HTTP header

当时 GitHub Status 为 All Systems Operational;结合连续 run、同一缓存 target、多 job 扩散,以及下载/上传两端都出现 body 截断,判断问题在 quack S3 缓存经过的第三方代理链路,而不是业务代码或 GitHub Actions 本身。

受影响 job 包括但不限于 project-lintsandbox:buildsandbox-pi-cloudflare:buildsandbox-test。这些 job 对 sandbox:generate 缓存有依赖,所以缓存层 transient failure 会随机阻塞 merge queue。

怎么解决

  • 缓存解压失败时自动自愈:
    • 本地缓存损坏:删除本地缓存后从云端重新下载
    • 云端缓存也损坏:清理本地缓存并让 target 重新构建
  • 新增 CloudStorageTransientError,只把明确的 transient 云存储错误降级处理:
    • IncompleteBody
    • timeout / connection closed / proxy connection / endpoint connection 等网络异常
    • 500 / 503 / SlowDown / throttling 等临时服务端错误
  • boto3/botocore 的 SDK 错误统一包装为 CloudStorageError / CloudStorageTransientError,具体是否 transient 由错误码、网络异常和 wrapped error chain 的白名单判断。
  • 缓存命中检查阶段遇到 transient 云存储错误时,NORMAL 模式按 cache miss 处理并重新构建;LOAD_ONLY 仍保持 fail-fast。
  • 缓存归档损坏统一包装为 CacheCorruptionErrorTarget.execute() 只处理缓存领域错误和 transient 云存储错误,不再直接依赖 zstd/boto3 底层异常。
  • transient 缓存错误或缓存损坏时 NORMAL 模式 fallback 重新构建;上传阶段仅对 transient 云存储错误跳过云端缓存。
  • AccessDenied、认证错误、bucket/path 配置错误等非 transient CloudStorageError 继续 fail-fast。
  • cache hit 后更新访问时间的 metadata 上传改为 transient-only best effort,避免维护性写入阻塞 job。

验证

  • uv run ruff format --check
  • uv run ruff check
  • uv run basedpyright
  • uv run pytest
  • uv run pytest src/quack/utils/cloud_test.py src/quack/cache_test.py src/quack/models/target_test.py
  • git diff --check
  • uv run quack test
  • uv run quack test
  • uv run quack test --cache=false

tigerBeA and others added 2 commits July 9, 2026 15:20
代理链路不稳定导致 S3 body 截断,出现两类故障:
1. ZstdError:从 S3 下载的 .tar.zst 残缺,本地解压失败
2. IncompleteBody:上传阶段 body 与 Content-Length 不一致

修复:
- cache.py load():本地缓存 ZstdError 时删除本地并从云端重新下载;
  云端缓存也损坏时清理本地并 re-raise,让 target.py 走重建路径
- target.py execute():NORMAL 模式下 load() 抛出 ZstdError 时
  fallback 到重新构建并保存新缓存,不再 crash job
- 上传失败(CloudStorageError)从 sys.exit(1) 改为 warning,
  缓存上传失败不影响构建结果
- 新增对应测试:corrupt local cache fallback、corrupt cloud cache re-raise

Co-Authored-By:  via Moxt <noreply@moxt.ai>
@cursor

cursor Bot commented Jul 9, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@cursor

cursor Bot commented Jul 9, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

1 similar comment
@cursor

cursor Bot commented Jul 9, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@tigerBeA
tigerBeA requested a review from iven July 9, 2026 08:16
@tigerBeA
tigerBeA force-pushed the lisw/cache-corruption-resilience branch from 127df0a to 4cade92 Compare July 9, 2026 08:17
@cursor

cursor Bot commented Jul 9, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@cursor

cursor Bot commented Jul 9, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@tigerBeA
tigerBeA requested a review from fankaidev July 9, 2026 08:53
@tigerBeA tigerBeA changed the title fix: 对 transient S3 缓存异常做自愈处理 fix(cache): 缓存损坏或临时云存储错误时降级重建 Jul 9, 2026
@tigerBeA
tigerBeA added this pull request to the merge queue Jul 9, 2026
Merged via the queue into main with commit 2238d15 Jul 9, 2026
2 checks passed
@tigerBeA
tigerBeA deleted the lisw/cache-corruption-resilience branch July 9, 2026 09:21
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