Dual-platform fan-out · Gradle/CocoaPods/DerivedData tiers · Queue gates and single-node fallback
Small teams shipping a Flutter or React Native monorepo across multiple Mac Mesh nodes often lose wall time to cache stomping, queue starvation, and cross-region dependency pulls. This article provides a serial vs dual-node fan-out vs dedicated per-platform decision matrix, splits Gradle, CocoaPods, and DerivedData cache keys with read-only consumer boundaries, and adds a six-step reproducible gate plus fallback to a single node. Read together with monorepo affected builds and cross-region artifacts fan-out.
Parallel Android and iOS is not simply running two pipelines on one machine without IO modeling; mis-scheduled parallelism moves flakiness from compilers into schedulers. When the signals below appear, return to the matrix instead of adding more concurrency.
Gradle vs Xcode disk contention: ~/.gradle and DerivedData grow together on one node and IO wait dominates wall time.
CocoaPods resolution vs Android NDK drift: lockfiles missing from cache keys create green-remote, red-integration false hits.
Metro and emulators fighting CPUs: interactive RN checks and unattended CI jobs share cores, queue depth rises while throughput falls.
Fan-out without read-only edges: consumers rewrite shared prefixes and race artifact pointer swaps.
Cross-region cache pulls without retry budgets: network retries balloon and starve concurrent seats.
These dimensions target mobile monorepos on a multi-region remote Mac pool; the goal is to make fan-out an auditable field. If the repo also ships heavy native modules, align with full-build short-circuit rules in the affected-builds guide first.
| Dimension | Prefer serial | Prefer dual fan-out | Split dedicated nodes |
|---|---|---|---|
| Change shape | Single-platform assets or copy tweaks | Both platforms bump deps or binary surfaces together | Android needs multi-ABI matrices while iOS needs Archive windows in parallel |
| Queue health | Stable depth and predictable P95 wall time | Sum of queued times exceeds fan-out savings | Archive on one side blocks PR validation on the other |
| Cache policy | One prefix with lockfiles and toolchain fingerprints in keys | Separate writable prefixes per platform, consumers read-only | Dedicated nodes use isolated generation numbers and lease ids |
| Multi-region | Single region meets SLA | Primary writes, satellites mirror read-only | Satellites must not rewrite either prefix |
| Fallback | Merge to serial on queue overflow or disk alerts | Freeze fan-out after two consecutive verification failures | Force single-platform exclusivity during major Xcode or AGP migrations |
Freeze reproducible inputs before debating dual-platform parallelism; reversing the order turns parallelism into parallel failures.
Assume runners can log into every node with shared-pool trust; if artifacts fan across oceans, also implement manifest and lease fields from the artifacts fan-out guide.
Freeze a five-part input bundle: short commit hash, Podfile.lock/Gemfile.lock, Gradle lock, xcodebuild -version, and Android SDK/NDK major in the pipeline header.
Split writable prefixes: keep IOS_CACHE_GEN and AND_CACHE_GEN monotonic; never share one writable directory across platforms.
Decide fan-out: build a parallel job graph only when the matrix cell says dual fan-out; otherwise stay serial.
Primary writes, consumers read-only: only primaries write tarballs; consumers verify hash and size before unpacking.
Queue budgets: exponential backoff with a hard cap for network errors; open an incident and release seats beyond the cap.
Fallback to one node: on disk or queue thresholds, collapse to serial automatically and tag FANOUT_DISABLED for postmortems.
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"
Note: key fields can be replaced with a custom manifest for your stack; keep monotonic generations and per-platform writable prefixes, and avoid re-parsing dependency graphs on consumers.
Treat the numbers below as review and capacity starting points; replace them with your real build histograms and queue metrics, never as an external SLA. Postmortems should include dual-platform P95, tarball expand time, and seat-hold duration together.
FANOUT_DISABLED and human inspection over more parallelism.| Team signal | Starter strategy | Artifacts fan-out link |
|---|---|---|
| Single-digit contributors | One region, serial dual-platform plus local caches | Low fan-out need, simpler keys |
| Multi-region PRs | Primary writes both prefixes, satellites read-only | Tightly coupled to rsync or object storage fan-out |
| Nightly AI agent batches | Dedicated heavy-compile nodes isolated from interactive pools | Prevents nightly jobs from blowing shared key spaces |
Warning: opening cross-region fan-out before consumers are read-only amplifies half-sync issues into simultaneous false greens.
Laptops running Android emulators and iOS Archives together routinely lose time to sleep, lock screens, and bandwidth jitter; pure SaaS mobile build farms often collide with native debugging and corporate network policy. Teams that need dual-platform continuous delivery and auditable node roles in one capacity story bleed observability and permission edges on DIY stacks.
Teams that need contracted nodes, reviewable bandwidth, and region choice usually ship faster by running critical dual-platform builds on orderable cloud Macs with explicit queue policy. For cross-platform delivery plus nightly AI agent load, VpsMesh Mac Mini cloud rental is typically the better fit: nodes split cleanly, links stay auditable, and parallelism is as measurable as queue depth.
Fan out when both legs stretch wall time, single-node IO or emulator vs xcodebuild contention is visible, or queue depth stays above threshold; stay serial when changes are single-platform. More trimming logic is in the monorepo affected-builds post.
Avoid a shared writable prefix without isolation; tier keys and directories with primary writers and read-only consumers. For cross-region fan-out read the artifacts fan-out matrix.
Read the pricing page and order page, then review remote access policy in the help center before onboarding.