Задержка трансграничной компиляции — это политика и физика: на удалённых Mac с Apple Silicon вы либо доставляете стабильные к препроцессору артефакты по сети через sccache и согласованный Redis, либо держите горячий путь на APFS и при необходимости подключаете NFS с ccache. Этот туториал с поисковым интентом помогает сравнить sccache и ccache для Clang, CMake и Ninja, зафиксировать префиксы ключей, таймауты и пороги повторов без гадания по флагам монтирования. Внутренние ссылки без входа: главная MacPull, каталог блога; смежный материал по распределённому кешу сборки — Bazel и remote cache на Mac CI.

Сравнительная таблица: sccache и ccache в общих пулах удалённого Mac

На один класс пайплайнов выбирайте один основной кеш компилятора. Смешение обёрток без изоляции переменных CC/CXX и каталогов быстро портит статистику и маскирует реальные промахи.

Инструмент Когда уместен Трансграничный сценарий Риски
sccache + Redis Redis с TLS уже в контуре комплаенса; сборки Clang, Rust или MSVC-совместимые цепочки Пространство ключей по продукту; сетевые задержки бьют по обёртке, а не по каждой записи объектного файла на NFS Политика maxmemory и eviction должны совпадать с готовностью к пересборке; монорепозитории дают всплески горячих ключей
sccache + S3 (опционально) Юристы предпочитают объектное хранилище Redis-памяти, доступны подписанные URL Географически шардированные бакеты; настраивайте параллелизм запросов на раннер Мелкие PUT и листинги могут съедать бюджет; локальный SCCACHE_DIR на NVMe обязателен
ccache + локальный APFS Выделенные однотенантные хосты, большой CCACHE_DIR на внутреннем SSD Трансграничность косвенная: выигрыш, если раннер рядом с командой и без «длинного» NFS Эфемерный диск сбрасывает тепло кеша без стадии восстановления из артефактов
ccache + NFS / HTTP secondary Однородные Clang-сборки; сторедж уже отдаёт POSIX или HTTP-кеш в одном регионе Межстрановые попадания работают только при низкой задержке к filer; иначе RTT метаданных доминирует Нужны выверенные опции монтирования, локальный CCACHE_TEMPDIR, реалистичный CCACHE_MAXSIZE

Ни один кеш не заменяет дисциплину закрепления тулчейна: обновление Xcode или драйвера Swift меняет отпечатки препроцессора — относитесь к доле попаданий как к сигналу, привязанному к матрице компиляторов, а не к «красивому проценту» ради отчёта.

Чеклист параметров: Redis, префиксы, NFS, параллелизм воркеров

Гигиена Redis для sccache: указывайте SCCACHE_REDIS_ENDPOINT с rediss://, если TLS завершается на брокере; выносите секреты в SCCACHE_REDIS_USERNAME и SCCACHE_REDIS_PASSWORD, не вшивайте их в URL. Ограничьте ACL одним индексом SCCACHE_REDIS_DB. Зафиксируйте в runbook таймауты клиента и tcp-keepalive, чтобы авторы CI понимали «зависшие» обёртки. Префикс логического пространства ключей — SCCACHE_REDIS_KEY_PREFIX (и при нескольких бэкендах — SCCACHE_NAMESPACE), чтобы эксперименты приложения не пересекались с драйверным ядром.

NFS для ccache: монтируйте с noatime, согласуйте rsize/wsize с вендором filer; не обещайте SLA по океану без микробенчмарка компиляции. CCACHE_TEMPDIR всегда на внутреннем SSD job — временные объекты не должны попадать на сетевой том.

Типовой пример для macOS-клиента (замените адрес и путь): mount -t nfs -o vers=4,rw,noatime,hard,intr,resvport filer01.internal:/export/ccache-shared /Volumes/ccache-nfs — затем export CCACHE_DIR="/Volumes/ccache-nfs/job-${CI_JOB_ID}" и отдельный подкаталог на job снижает вероятность скрытых гонок между пайплайнами на одном хосте. Для крайних случаев задержек метаданных рассмотрите локальный «горячий» CCACHE_DIR с периодической синхронизацией в enterprise NAS по расписанию, а не прямую запись тысяч мелких файлов через высокий RTT.

Параллельные воркеры: свяжите CMAKE_BUILD_PARALLEL_LEVEL или ninja -j с RAM; каждый линкер повышает пик RSS и может «голодать» обёртку при записи в Redis. На общих удалённых Mac начните с половины производительных ядер и масштабируйте, наблюдая счётчики ошибок в sccache --show-stats.

  1. В runbook закреплены канонический хост Redis, порт, индекс БД, имя ACL и процедура ротации пароля.
  2. SCCACHE_REDIS_KEY_PREFIX (и при необходимости SCCACHE_NAMESPACE) кодируют продукт, мажорный поток Xcode и поколение намеренного разрыва совместимости.
  3. Пути SCCACHE_DIR и CCACHE_DIR не пересекаются между тенантами без отдельных подкаталогов.
  4. Экспорт NFS совместим с политикой снимков и резервного копирования; известен владелец очистки зависших блокировок.
  5. После фазы компиляции в лог попадают sccache --show-stats или ccache -s.
  6. Есть пороги свободного места для локального staging обёртки и для общего тома, с алертами до обрыва линковки.

Переменные окружения, каталоги кеша, таймауты и пороги повторов

Скопируйте блок под ваш стек и подставьте реальные имена хостов. Секреты — только из vault; в логи попадают идентификаторы без паролей.

# --- sccache + Redis (TLS; секреты отдельными переменными) ---
export SCCACHE_REDIS_ENDPOINT="rediss://sccache-redis.internal:6379"
export SCCACHE_REDIS_USERNAME="ci-sccache"
export SCCACHE_REDIS_PASSWORD="${SCCACHE_REDIS_PASSWORD_SECRET}"
export SCCACHE_REDIS_DB="0"
export SCCACHE_REDIS_KEY_PREFIX="acme/mobile/xcode-16-3/"
export SCCACHE_REDIS_EXPIRATION="2592000"
export SCCACHE_NAMESPACE="acme-ci"
export SCCACHE_DIR="${CI_PROJECT_DIR}/.sccache-staging"
export SCCACHE_IDLE_TIMEOUT="1800"
export SCCACHE_CACHE_SIZE="64G"
export RUSTC_WRAPPER="$(command -v sccache)"
export CARGO_BUILD_RUSTC_WRAPPER="${RUSTC_WRAPPER}"
export CMAKE_C_COMPILER_LAUNCHER="$(command -v sccache)"
export CMAKE_CXX_COMPILER_LAUNCHER="$(command -v sccache)"
mkdir -p "${SCCACHE_DIR}"

# --- ccache + APFS (локально на job) + лимит размера ---
export CCACHE_DIR="${CI_PROJECT_DIR}/.ccache"
export CCACHE_TEMPDIR="${TMPDIR:-/tmp}/ccache-${CI_JOB_ID:-local}"
export CCACHE_MAXSIZE="32G"
export CCACHE_LIMIT_MULTIPLE="0.85"
export CCACHE_COMPRESS="1"
export CCACHE_COMPRESSLEVEL="6"
export CCACHE_SLOPPINESS="pch_defines,time_macros"
export CMAKE_C_COMPILER_LAUNCHER="$(command -v ccache)"
export CMAKE_CXX_COMPILER_LAUNCHER="$(command -v ccache)"
mkdir -p "${CCACHE_DIR}" "${CCACHE_TEMPDIR}"

# --- Лестница повторов при сбоях транспорта обёртки ---
run_with_cache_retries() {
  local attempt=1 max=4 delay=2
  while [ "${attempt}" -le "${max}" ]; do
    echo "compile phase attempt ${attempt}/${max}"
    if "$@"; then return 0; fi
    sleep "${delay}"
    delay=$((delay * 2))
    attempt=$((attempt + 1))
  done
  return 1
}
# Пример: run_with_cache_retries cmake --build build --parallel "${CMAKE_BUILD_PARALLEL_LEVEL:-8}"

Таймауты: выровняйте клиентские таймауты Redis с самой долгой ожидаемой LTO-единицей: если обрыв каждые девять минут, а линковка двенадцать, важнее поднять SCCACHE_IDLE_TIMEOUT, чем слепо наращивать -j. Для HTTP secondary в ccache 4.x начните с дефолтов вендора по connect/read и проверьте холодную сборку из каждого региона.

Пороги повторов: до четырёх попыток с паузами 2 с, 4 с, 8 с (экспоненциально); после исчерпания — падение job и полный дамп sccache --show-stats на последней попытке. Так вы отделяете кратковременный сетевой шум от деградации Redis или перегруженного filer.

FAQ

Заменяет ли sccache distcc? Нет: sccache хранит результаты компиляции, а не планирует удалённые фермы. Здесь фокус — экономика попаданий в кеш, а не топология распределённой компиляции.

Стоит ли включать все флаги ccache «sloppiness» ради процентов? Только те, что одобрены в вашей модели воспроизводимости; фиксируйте набор рядом с политикой SBOM.

Что делать, если Redis вытесняет горячие записи середины спринта? Считайте eviction нормой; расширяйте пространство имён только намеренно и планируйте ночной прогрев топ-таргетов.

Где в пайплайне Xcode DerivedData? Это другой слой; для полного iOS-контура см. матрицу DerivedData и прогрева на Mac CI.

Почему после обновления компилятора «внезапно» выросло число промахов? Меняются пути к заголовкам, макросы по умолчанию и версии стандартной библиотеки — это ожидаемо. Зафиксируйте в CI явный список поддерживаемых пар (Xcode × SDK × deployment target) и сбрасывайте префикс Redis или отдельный подкаталог ccache при мажорных переходах, иначе смешаются несовместимые объекты.

Стоит ли поднимать SCCACHE_CACHE_SIZE без лимита на общем раннере? Нет: локальный staging должен укладываться в квоту диска арендатора; сочетайте с алертами по свободному месту и политикой очистки между job, если оркестратор не гарантирует изолированный workspace.

Итог

sccache с Redis выигрывает, когда трансграничные раннеры должны делить одно управляемое пространство объектов; ccache на APFS с опциональным NFS или HTTP secondary сильнее, если комплаенс «про POSIX» и задержка к filer предсказуемо низкая. Зафиксируйте префиксы ключей, idle-таймауты и лестницу повторов рядом с матрицей компиляторов — тогда просадка попаданий читается как инцидент инфраструктуры, а не «магический флейк».

Когда понадобится выделить узлы Apple Silicon рядом с вашим кеш-слоем, откройте без регистрации страницы тарифов, покупки и аренды и центра помощи на macpull.com, сравните планы по числу параллельных job и объёму SSD под SCCACHE_DIR/CCACHE_DIR, затем вернитесь в блог за соседними матрицами по registry и сборочным пулам.

Если удобнее начать с международной витрины на английском, загляните на главную macpull.com (EN) — навигация к ценам и оформлению заказа так же не требует входа.