痛点拆解:缓存同步不是「多传几次就会好」
痛点 1:把 DerivedData 当成普通目录树。DerivedData 含瞬时索引与半一致子树;全量 --delete 或高频双向同步易留下「看似完整、缺索引」碎片,表现为偶发符号解析失败或 Swift 前端缓存错乱,日志却只剩「重编就好」的随机信号。
痛点 2:ccache/sccache 与 Xcode 缓存混策略。ccache/sccache 边界清晰,适合搬运;DerivedData 与 Xcode/Swift/插件强耦合,同一 rsync 与并发模型会在大版本升级后集中爆炸。
痛点 3:多 Job 并发写同一缓存卷。矩阵或夜间任务重叠时,锁、临时目录与 sqlite 索引竞态;与 并发 SFTP 同审客户端队列或分卷,避免传输未断、编译先花。
痛点 4:忽略 APFS 元数据。缓存仍可能踩 xattr/权限/硬链接语义,见 APFS rsync 文;与 签名产物分账号分路径,避免审计混写。
痛点 5:无脏缓存回滚。随机失败时要么全删浪费时间,要么不敢清累积幽灵态;应用编号操作卡,产物走 SHA256,缓存走体积与抽样。
痛点 6:交互式 SFTP 当生产同步。排障可用手工,生产须脚本化,并与 known_hosts、CI 凭据同表维护。
威胁模型与闸门分工:缓存加速 vs 产物可信
拆两条信任链:产物链须可审计、可哈希、可回滚;缓存链可快失败快清空,但不得污染签名与发布目录。卷/路径层隔离,Runbook 写明账号与 chroot 配额。
可量化基线:用指标结束「感觉更快」
监控建议:干净构建 vs 命中缓存耗时、ccache 命中率、sccache 队列、DerivedData 体积、rsync 退出码、partial 残留、并发 Job 与重试率。升级 Xcode/Swift 后跑样本并把 rsync --version 写入日志,口径对齐 openrsync 选型。
产物侧坚持 SHA256 与签名;缓存用体积告警与抽样存在性作弱闸门,避免对临时文件全量哈希。
决策矩阵:不同步、只搬编译缓存、子集 DerivedData 与专用卷
| 模型 | 适用 | 收益 | 风险与代价 |
|---|---|---|---|
| 不同步缓存,远程 Mac 自建 | 节点稳定、团队小、构建频次低 | 运维最简单;无竞态 | 冷启动慢;换机成本高 |
仅同步 ccache/sccache 根目录 | 编译瓶颈在 C/C++/Clang 模块,且可接受偶尔清缓存 | 边界清晰;易做会话级目录 | 仍需处理版本升级后的不命中与体积膨胀 |
同步 DerivedData 子集(如 SourcePackages 可控部分) | SPM 依赖网络差、希望保留解析结果 | 减少重复解析与下载 | 路径与 Xcode 版本强绑定;需要严格排除半一致子树 |
整棵 DerivedData 镜像 | 强离线、固定工具链、单 Job 独占 | 极限增量 | 锁与索引竞态风险最高;升级窗口复杂 |
| 专用 SSD 卷或 per-Job 子卷 | 多团队共用远程 Mac 池 | 隔离清晰;易配额与清理 | 需要自动化挂载与回收;成本上升 |
犹豫时先问:「缓存损坏会不会污染已签名产物目录?」若会,立即拆分账号与挂载点,并把清理脚本权限收敛到缓存卷。
实操步骤:从目录约定到可复现命令
# A) 为每个 CI Job 约定独立缓存根(示例)
# export CACHE_ROOT=/Volumes/cache/job-${CI_JOB_ID}
# mkdir -p "$CACHE_ROOT/ccache" "$CACHE_ROOT/DerivedData"
# B) rsync 仅同步 ccache(示意,路径按团队规范替换)
# rsync -a --partial --partial-dir=.rsync-partial \
# ./ccache-dir/ user@remote-mac:"$CACHE_ROOT/ccache/"
# C) 若必须触碰 DerivedData:强烈建议排除高抖动路径并谨慎使用 delete
# rsync -a --partial --partial-dir=.rsync-partial \
# --exclude '**/ModuleCache.noindex/**' \
# ./DerivedData/SubTree/ user@remote-mac:"$CACHE_ROOT/DerivedData/SubTree/"
# D) 产物通道仍走 SHA256 与签名验证(与完整性文对齐)
# shasum -a 256 -c manifest.sha256
# E) 脏缓存回滚(示例:仅清当前 Job)
# ssh user@remote-mac "rm -rf \"$CACHE_ROOT\""
生产请把上述片段封装为可重用 Action,并把 ServerAliveInterval、并发与 sshd 限次策略一起评审,避免「清缓存脚本」触发误封。
强相关 CTA:把缓存、产物、权限放在一张阅读顺序表
推荐阅读顺序:本文 → APFS xattr 与 rsync → SHA256 闸门 → 并发 SFTP → 签名分发 → 原子发布 → 首页了解托管节点、套餐与帮助页。
FAQ 与为什么考虑 SFTPMAC 托管远程 Mac
能不能 nightly 全量 rsync DerivedData?
可以,但要把「独占窗口、失败重试、升级冻结」写清楚;否则白天 Job 与夜间同步重叠仍会踩锁。更常见的是只同步编译缓存并把 DerivedData 留在会话内自生长。
sccache 远端一定要 Redis 吗?
不是唯一路径,但集中式后端更容易做多 Job 统计与淘汰;关键是网络分区时的降级策略,避免编译链路无限等待。
缓存同步失败要不要阻断发布?
产物链仍应阻断;缓存链可降级为冷构建,但要在同一流水线里显式打标签,避免静默混用半套缓存。
与 FSEvents 监听问题有关吗?
若你还在本机监听同步结果,需另读 FSEvents 与 rsync 热重载;本文聚焦编译缓存一致性而非 IDE 事件。
总结:2026 年在远程 Mac 上「搬缓存」要以会话隔离与闸门分工为前提;优先搬 ccache/sccache,谨慎触碰 DerivedData,并把 rsync 参数、升级窗口与脏缓存回滚写成可审计 Runbook。
局限:自建池需要持续维护卷、账号、并发、监控与清理脚本,人员流动时容易漂移。SFTPMAC 托管远程 Mac提供稳定 Apple 生态入口与可运营传输面,团队可把精力放在产物质量与发布节奏,而不是 nightly 救火。
把缓存卷与产物卷从账号与路径层拆开,托管环境更容易做到配额、审计与回归样本统一。
