双端扇出 · Gradle/CocoaPods/DerivedData 分层 · 队列门禁与回退单节点
维护Flutter或React Native单仓的小团队在多台Mac Mesh远程节点上同时跑 iOS 与 Android 时,常被缓存互踩、队列饥饿与跨区拉包拖垮 wall time。本文给出串行、双节点扇出与专用端节点的决策矩阵,拆解 Gradle、CocoaPods、DerivedData 的分层键与只读消费边界,并附六步可复现门禁与回退到单节点的条件。可与Monorepo 影响范围构建、跨区产物分发矩阵交叉阅读。
双端并行并不是把两条流水线绑在同一台机器上同时跑;若磁盘与 CPU 争用未被建模,「并行」会把 flaky 从编译器搬进调度器。下列信号出现时,应优先回到决策矩阵而不是继续加并发。
Gradle 与 Xcode 争用磁盘:同一节点上 ~/.gradle 与 DerivedData 同步膨胀,IO 等待主导 wall time。
CocoaPods 解析与 Android NDK 版本漂移:锁文件未进缓存键时出现「远端绿、集成红」的假命中。
Metro 与模拟器争用:RN 交互验证与 CI 无人值守任务抢同一套 CPU 核,队列深度上升但吞吐下降。
扇出无只读边界:消费节点回写共享前缀,半同步写入与双端产物指针切换竞态。
跨区拉缓存无超时预算:网络类重试无限放大,占用并发席位导致饥饿。
下列维度面向多地区远程 Mac 资源池上的跨端移动单仓;目标是让「是否扇出」成为可审计字段。若仓库同时包含 heavy native 模块,请先对照影响范围构建文中的全量短路条件。
| 维度 | 优先单节点串行 | 优先双节点扇出 | 拆专用端节点 |
|---|---|---|---|
| 变更形态 | 仅单端资源或文案微调 | 两端同时 bump 依赖或二进制接口 | Android 需多 ABI 矩阵且 iOS 需 Archive 分发并行窗口 |
| 队列健康 | 深度稳定、P95 wall time 可预测 | 双端排队时间之和大于扇出节省值 | 一端 Archive 阻塞另一端 PR 验证 |
| 缓存策略 | 单前缀即可,键包含锁文件与工具链指纹 | iOS 与 Android 使用独立写前缀,消费只读 | 端专用节点使用独立世代号与租约 id |
| 跨区 | 单区域可满足 SLA | 主区写、卫星只读镜像 | 卫星区禁止回写任一端前缀 |
| 回退 | 队列溢出或磁盘告警时合并为串行 | 校验失败连续两次则冻结扇出 | 大版本 Xcode 或 AGP 切换窗口强制单端独占 |
先冻结「可复现输入」再谈双端并行;顺序反了会把并行度变成并行故障。
以下步骤假设 Runner 已能稳定登录各节点并完成共享池互信;若产物需跨洋扇出,请同步落实产物分发文中的 manifest 与租约字段。
冻结输入五元组:提交短哈希、Podfile.lock/Gemfile.lock、Gradle 锁、xcodebuild -version 与 Android SDK/NDK 主版本写入流水线头。
拆分写前缀:IOS_CACHE_GEN 与 AND_CACHE_GEN 单调递增;禁止双端共用同一写目录。
判定扇出:命中决策表「优先双节点扇出」单元格时创建并行 job graph,否则串行。
主编译写、消费只读:仅主节点写 tarball;消费节点校验哈希与大小双字段后再解压。
队列预算:为网络类错误配置指数退避与硬上限;超过上限记入 incident 并释放席位。
回退单节点:磁盘或队列超阈时自动合并为串行并打标 FANOUT_DISABLED 供复盘。
IOS_KEY="${CI_COMMIT_SHORT_SHA}:pods:${PODFILE_LOCK_SHA}:$(xcodebuild -version | shasum -a 256 | cut -c1-10)"
AND_KEY="${CI_COMMIT_SHORT_SHA}:gradle:${GRADLE_LOCK_SHA}:${ANDROID_SDK_MAJOR}"
export FASTLANE_SKIP_UPDATE_CHECK=1
echo "{\"ios\":\"$IOS_KEY\",\"android\":\"$AND_KEY\"}" > "${CI_PROJECT_DIR}/dual-cache.manifest"
提示:键字段可按团队栈替换为自定义 manifest;关键是单调世代与端隔离写前缀,不要在消费机重复解析依赖图。
下列数值为评审与容量规划起点,须用你们仓库真实构建时长直方图与队列指标替换;不得直接宣称为对外 SLA。复盘时应同时给出双端 P95、缓存 tarball 展开耗时与席位占用时长三条序列。
FANOUT_DISABLED 与人工巡检,而不是继续并行。| 团队规模信号 | 推荐起步策略 | 与产物扇出关系 |
|---|---|---|
| 个位数贡献者 | 单区域串行双端 + 本地缓存 | 扇出需求低,键可简化 |
| 多地区并行 PR | 主区写双前缀 + 卫星只读 | 与 rsync 或对象存储扇出强耦合 |
| AI Agent 夜间任务 | 专用重编译节点与交互池隔离 | 避免夜间批量任务打爆共享键空间 |
注意:在未关闭消费节点写权限前打开跨区扇出,会把半同步问题放大为「多地同时假绿」。
依赖个人笔记本同时承担 Android 模拟器与 iOS Archive,常在休眠、锁屏与带宽波动上同时欠账;纯 SaaS 移动构建则在原生调试与企业内网策略上频繁碰壁。对要把双端持续交付与可审计节点角色放在同一容量故事里的团队,自建拼凑方案在观测与权限边界上持续失血。
相较之下,需要合同化节点、可复核带宽与区域可选的团队,把关键双端构建跑在可订购的云 Mac 与配套队列策略上更利于落地门禁;对同时承担跨端交付与 AI Agent 夜间负载的场景,VpsMesh 的 Mac Mini 云端租赁通常是更优解:节点可拆分、链路可审计,让并行度与队列深度同样可验收。
当 wall time 被两条链路同时拉长、单节点 IO 或模拟器与 xcodebuild 争用明显,或队列深度持续高于阈值时优先扇出;若变更集中在单端可先串行。更多裁剪逻辑见Monorepo 影响范围构建文。
不建议无隔离共用写前缀;应分层键与目录并保证主编译写、消费只读。跨区扇出请同步阅读产物分发矩阵。