# Расширенная настройка кэширования Elixir

Начните с <.localized_link href="/guides/features/cache/elixir">руководства по настройке кэша Elixir</.localized_link>. Параметры ниже нужны только в том случае, если в вашем проекте есть дополнительные входные данные для компиляции, исключения зависимостей или другие пути для извлечения кода.

## Выбор зависимостей

`cache: true` автоматически кэширует поддерживаемые зависимости. Список ключевых слов использует тот же автоматический выбор с возможностью переопределения:

```elixir
cache: [exclude: [:a_dependency_with_compile_time_side_effects]]
```

Чтобы ограничить кэширование конкретными зависимостями:

```elixir
cache: [dependencies: [:nimble_options, :nimble_parsec]]
```

`dependencies: :all` явно выбирает все поддерживаемые зависимости; `dependencies: []` не выбирает ни одной. `exclude:` имеет приоритет над любым выбором. Пропуск `cache` или установка значения `false` или `nil` отключает кэширование.

Поддерживаемые зависимости загружаются через Hex или Git и используют стандартный конвейер компилятора Mix с исходниками Elixir/Erlang или обычную чистую BEAM-компиляцию rebar3. Зависимости Mix могут повторно использовать артефакты, если их вышестоящие зависимости используют rebar3. Конфигурация rebar, исходные/заголовочные файлы и отпечаток исполняемого файла rebar3 участвуют в формировании ключей. Также поддерживаются упакованные метаданные `ebin/*.app` и пассивные Makefiles. Скрипты конфигурации rebar уже оценены Mix до поиска; их исходный код и эффективная конфигурация входят в ключи. Исходные скрипты приложений (`*.app.src.script`) оцениваются позже rebar3 и требуют аудированного контракта сборщика, как и плагины сборки и хуки компиляции. Отсутствующий лаунчер rebar3 устанавливается перед снятием отпечатка с помощью обычного установщика Mix; холодным воркерам всё ещё нужен доступ к этому установщику.

Пользовательские трассировщики, символические ссылки на исходники, корни umbrella-проектов и цепочки с локальными зависимостями компилируются нормально. Собственные bootstrap-зависимости Tuist также компилируются нормально на холодных воркерах. Структурная пригодность не гарантирует герметичность произвольного кода компиляции.

## Объявление дополнительных входных данных

Деревья исходников, зафиксированные идентификаторы зависимостей, вышестоящие источники, BEAM-файлы и ресурсы, флаги времени выполнения/опциональные/приложения, разрешённые метаданные приложения, явная эффективная конфигурация приложения, параметры компилятора (включая параметры лексера и парсера), среда и цель Mix, а также идентичность инструментария включаются автоматически. Специфичные для зависимостей `env:` и `system_env:` входят в ключ, а конфигурация оценивается в этой среде. Значения по умолчанию приложения поступают через входные данные исходников, а не через состояние загрузки VM.

Для зависимостей, которые читают файлы вне своего дерева исходников или дополнительные переменные среды во время компиляции, объявите эти входные данные:

```elixir
cache: [
  inputs: ["schemas", "config/compiler-input.json"],
  env: ["YOUR_COMPILER_OPTION"]
]
```

Макросы Elixir и тела модулей могут читать произвольные файлы, среду, текущие пути кода, сетевые ответы и время. Tuist не обнаруживает такие чтения автоматически. Изменение необъявленных входных данных может привести к устаревшим результатам. Исключайте зависимости с компиляцией, зависящей от времени/сети, или побочными эффектами, которые должны выполняться при каждой сборке; используйте `dependencies:` для аудированного белого списка зависимостей, когда нужен более строгий контроль.

Стандартные переменные компилятора/среды выполнения (`ERL_COMPILER_OPTIONS`, `ELIXIR_ERL_OPTIONS`, `ERL_AFLAGS`, `ERL_FLAGS`, `ERL_ZFLAGS`, `SOURCE_DATE_EPOCH`, `ERL_LIBS`, `MIX_PATH` и `MIX_ARCHIVES`) захватываются автоматически. Идентичность инструментария включает сборку Elixir, полную версию OTP (если доступна), ERTS, байт-код компилятора, ОС/версию, архитектуру, размер слова и порядок байтов. Эффективный контекст фиксируется перед каждым поиском. Модели входных данных для каждой зависимости могут совместно использоваться для планирования поиска в рамках одной подготовки; компиляция очищает план, а восстановление делает недействительными записи установленного приложения. Поэтапная установка и публикация всегда проверяют свежие входные данные без повторного использования плана поиска. Промах поиска также фиксирует свежий ключ перед компиляцией. Изменённые входные данные отклоняют попадание из промежуточного состояния вместо установки кода, собранного для предыдущих входных данных.

Ключи используют детерминированное кодирование терминов и отсортированный обход файловой системы. Неупорядоченные объявления сборщиков и наборы объявленных входных данных сортируются и дедуплицируются; упорядоченные параметры компилятора сохраняют свой порядок. Важны содержимое файлов, имена, биты исполнения и отсутствующие пути, а не временные метки файловой системы. Объявления файлов не моделируют права владения, временные метки или другие метаданные файловой системы; исключайте сборщики, которые формируют выходные данные на основе этих значений. Манифесты вышестоящих компиляторов исключаются как инкрементальная учётная запись; код, который намеренно читает их содержимое, должен объявлять их как дополнительные входные данные. Конфигурация регулярных выражений использует свои исходники и параметры, никогда — скомпилированные ссылки, локальные для VM. Другие непрозрачные значения времени выполнения обходят кэширование. Обновлённые модели ключей делают недействительными старые записи без миграции.

Детерминированное кодирование ключей отделено от детерминированного вывода компилятора. Если метаданные вышестоящих BEAM меняются между иначе идентичными сборками на новых версиях Elixir, рассмотрите установку `ERL_COMPILER_OPTIONS='[deterministic]'` перед bootstrap SDK, посевом и использованием кэша. Объедините её с любыми существующими параметрами компилятора и пересоберите выходные данные зависимостей один раз при изменении настройки. SDK не устанавливает её автоматически; её точное значение остаётся частью ключа. Полные вышестоящие байты всё ещё хешируются, а не нормализуются или удаляются. Это не гарантия воспроизводимости между разными checkout или произвольными сборщиками. Параметр компилятора также исключает источники/параметры из `module_info(:compile)`, поэтому проверьте инструменты покрытия, отладки и обнаружения источников перед его внедрением. Флаги компилятора не исправляют неупорядоченные генераторы времени компиляции. SDK требует Mint 1.10.2 или новее, включая его обновления безопасности. Этот релиз сортирует карты, используемые для генерации функций фреймов HTTP/2; обновляйте старые записи блокировки Mint при обновлении SDK. Другие зависимости и проходы компилятора всё ещё могут создавать различные bootstrap BEAM, даже при включённом `deterministic`.

Выводы лексера и парсера считаются производными файлами только когда они отсутствуют в зафиксированном дереве Git или манифесте файлов пакета Hex. Поставляемые файлы `.erl` и непрозрачное содержимое директорий/подмодулей остаются обычными входными данными; регенерированные поставляемые файлы должны проходить ту же пост-стейджинговую сверку входных данных. Шаблоны генераторов из `parsetools` OTP также входят в идентичность инструментария. Держите checkout исходников зависимостей изолированными между параллельными сборками: генераторы и нативные сборщики могут писать в `deps/`. Вложенные/пользовательские макеты генераторов rebar всё ещё могут требовать аудированного объявления `generated`.

## Нативные и пользовательские сборщики

Для аудированного сборщика Mix, rebar3 или Make объявите контракт для каждой зависимости:

```elixir
cache: [
  builders: [
    bcrypt_elixir: [
      platform: "build-image@sha256:YOUR_IMMUTABLE_IMAGE_DIGEST",
      outputs: ["priv/bcrypt_nif.so"],
      tools: ["cc", "make"],
      env: ["YOUR_BUILDER_OPTION"]
    ]
  ]
]
```

`platform` определяет совместимую нативную среду сборки. Используйте неизменяемый идентификатор образа/инструментария, охватывающий компиляторы, SDK, системные заголовки и общие библиотеки, а не повторно используемую метку, такую как `linux`. Проверки платформы Elixir/OTP и обычные проверки платформы всё ещё применяются. Это явный аудит сборщика, а не автоматическое обнаружение нативных входных данных или требуемых побочных эффектов. Недействительные явные контракты сборщиков вызывают ошибку конфигурации, требующую действий, вместо того чтобы молча оставлять кэширование отключенным.

`outputs` перечисляет необходимые пути относительно директории сборки приложения зависимости. Укажите фактические библиотеки/исполняемые файлы, необходимые во время выполнения. Отсутствие выходных данных вызывает восстановление или компиляцию, даже если Mix считает BEAM-файлы актуальными; неполные снимки отклоняются. Объявите дополнительные переменные окружения через `env` сборщика и внешние файлы/директории через его `inputs` относительно потребляющего проекта. Нативные ключи автоматически включают стандартные переменные окружения сборки, такие как `PATH`, `CC`, `CXX`, `CFLAGS`, `LDFLAGS`, `SDKROOT` и `RUSTFLAGS`, используя эффективные переопределения `system_env` зависимости. Разрешенная конфигурация проекта Mix также входит в ключи нативных сборщиков; непрозрачная конфигурация со значениями-функциями не может быть закэширована.

Необязательные имена `tools` разрешаются в этом эффективном `PATH`; относительные пути к инструментам задаются относительно исходной директории зависимости. Байты исполняемых файлов и разрешенные пути входят в ключ, а недоступные объявленные инструменты обходят кэширование. Отпечаток лаунчера или обертки компилятора не покрывает его SDK, подпроцессы или системные библиотеки: их должны покрывать неизменяемый `platform` и дополнительные `inputs`. `MAKEFLAGS` сохраняется дословно, так как пользовательские сборщики могут читать его. Наследуемые идентификаторы jobserver GNU make поэтому могут вызывать безопасные промахи кэша между запусками; используйте стабильные флаги сборки в изолированной задаче кэша, а не предполагайте, что эти значения удаляются из ключа.

Некоторые сборщики генерируют файлы в `deps/` вместо только `_build/`. Перечислите эти пути относительно исходной директории этой зависимости в `generated`, например `generated: ["priv/native"]`. Только эти сгенерированные пути исключаются из исходных входных данных. Не исключайте оригинальные ресурсы или исходные файлы: они должны оставаться входными данными. Пути являются буквальными, без глобов или обхода. Контракт, платформа, дополнительные входные данные, значения окружения и любая пользовательская команда компиляции зависимости участвуют в формировании ключей. `compile: false` остается способом отказа от компиляции.

Аудированные снимки материализуют ресурсы `ebin`, `priv`, `include` и `src`, основанные на исходном коде, в восстановленное приложение. Нативные библиотеки, исполняемые файлы портов, заголовки и сгенерированные ресурсы поэтому сохраняются при холодном checkout без изменения исходников его зависимостей. Режимы исполнения и содержащиеся относительные символьные ссылки ресурсов сохраняются; выходящие за пределы, абсолютные и циклические ссылки отклоняются. Все ресурсы вышестоящих приложений входят в нисходящие ключи, включая обычные ресурсы `priv` и пользовательские директории вывода, а не только нативные библиотеки или BEAM-файлы. Код, который напрямую читает пути исходников производителя, не перемещается; сборщикам с обязательными побочными эффектами вне артефакта приложения всё ещё нужны эти шаги.

Держите пользовательские сборщики исключенными, пока их входные и выходные данные не будут аудированы. Контракт не устраняет необходимость холодной компиляции при загрузке SDK, ограничение артефакта в 64 МиБ или потребность в совместимой среде сборки.

## Повторное использование между путями checkout

Пути checkout участвуют в ключах по умолчанию. Если ваши зависимости не зависят от путей производителя, включите повторное использование между разными местами checkout:

```elixir
cache: [portable: true]
```

Настроенные значения, которые внедряют путь checkout, такие как пути, построенные из `__DIR__` в `config/config.exs`, не меняют портативные ключи. Как и место установки вашего инструментария Elixir: архивы и rebar3 учитываются по их содержимому.

Закэшированные BEAM-файлы сохраняют пути отладки/исходников производителя и значения времени компиляции `__DIR__`, `__ENV__.file` и `File.cwd!()`. Портативное повторное использование не перезаписывает их. Ссылки на ресурсы указывают на текущий checkout исходников. Байты вышестоящих BEAM-файлов также входят в ключи и могут содержать пути, поэтому повторное использование может потребовать восстановления вышестоящего замыкания из того же кэша, а не его независимой перекомпиляции.

## Хранение и доступ

| Опция | По умолчанию | Назначение |
| --- | --- | --- |
| `remote` | `true` | Использовать общий удаленный кэш после локального промаха. |
| `upload` | `true` | Публиковать новые пакеты удаленно. `false` всё ещё позволяет чтение и локальные снимки. |
| `path` | Директория кэша Tuist, внутри `mix/dependencies/v1` | Выбрать другое локальное хранилище. |
| `endpoint` | Обнаруживается автоматически | Переопределить HTTPS REAPI endpoint аккаунта. |

`upload: false` управляет поведением клиента, а не авторизацией. Права доступа к кэшу проекта обеспечиваются сервером. Учетные данные, которые могут только читать кэш проекта, автоматически пропускают загрузку. Опции проекта `endpoint:` и обнаруженные endpoints требуют HTTPS, за исключением loopback (`localhost`, `127.0.0.1`, `::1`) endpoints для разработки.

Непустой `TUIST_CACHE_ENDPOINT` переопределяет как `endpoint:`, так и обнаружение. Хостинговые раннеры используют его для доступа к своему частному внутрикластерному кэшу. Это переопределение окружения, контролируемое оператором, также разрешает HTTP на хостах, не являющихся loopback. HTTP отправляет токен кэша, ограниченный проектом, и артефакты в незашифрованном виде, поэтому используйте его только в доверенной сети. Недопустимое переопределение возвращает к локальным снимкам без попытки обнаружения.

Локальные снимки и удаленные пакеты ограничены 64 МиБ; более крупные артефакты компилируются без кэширования. Локальное хранилище не имеет автоматической эвикции; удаляйте его содержимое при необходимости. Удаленные артефакты используют существующие правила хранения, эвикции и очистки проектов Kura.

> [!IMPORTANT]
> Манифесты компилятора и BEAM-файлы могут содержать пути производителя и значения времени компиляции, включая секреты. Читатели кэша проекта могут получить доступ к этим артефактам. Записывающие в кэш являются доверенными производителями кода: проверки дайджеста обнаруживают повреждение, а не вредоносные исполняемые BEAM-файлы. Не предоставляйте учетные данные для записи в кэш недоверенным задачам PR и не компилируйте недоверенные зависимости с доступом к этим учетным данным.

## Устранение неполадок с отсутствующими попаданиями

- Запустите `mix tuist.setup` после установки пакета и зафиксируйте сгенерированную конфигурацию Mix. Настройка сохраняет другие алиасы и опции Tuist; она отказывается от пользовательских действий компиляции, а не молча удаляет их.
- Сохраните сгенерированный алиас компиляции. Прямое обнаружение задач `mix tuist.compile` может скомпилировать зависимости до того, как кэш сможет их проинспектировать.
- Уже актуальные, валидные выходные данные для выбранных поддерживаемых зависимостей считаются локальными попаданиями с причиной `up_to_date`. Они не публикуются ретроспективно и не имеют ключа артефакта, байтов передачи, задержки восстановления или оцененного сэкономленного времени. Чтобы проверить восстановление снимка, очистите сборку только поддерживаемой зависимости с помощью `mix deps.clean NAME --build` после того, как она заполнила кэш, а затем скомпилируйте снова.
- Согласуйте `MIX_ENV`, `MIX_TARGET`, настройки компилятора и объявленные входные данные. Изменения путей также меняют ключи, если не включено портативное повторное использование.
- Неподдерживаемые зависимости отображаются как **Не кэшировано**, а не как промахи. Они исключаются из процента попаданий. Сборка без запросов и без актуальных локальных попаданий имеет недоступный процент, а не 0%.
- Отсутствующие учетные данные, недоступные конечные точки и поврежденные снимки приводят к откату на компиляцию. Ошибки компилятора по-прежнему приводят к сбою сборки.
- Записи CAS могут поступить после отчета о сборке. Обновите страницу, чтобы увидеть недавно полученные передачи. Локальные попадания и устаревшие сборки не имеют удаленных выходов CAS.

Серверам необходима поддержка контракта отчета о компиляции `0.2` для записи локальных попаданий `up_to_date`. Если старый сервер явно отвергает данные кэша или значение перечисления схемы, пакет повторяет отчет один раз без этих новых наблюдений за повторным использованием. Детали компиляции и другие результаты кэша сохраняются; новое локальное повторное использование остается недоступным до обновления сервера. Развертывание сервера в первую очередь позволяет избежать этого дополнительного запроса. Версия контракта не согласовывает поддержку, а ошибки аутентификации, ошибки транспорта и другие сообщения валидации не повторяются. Ошибки перечисления не определяют свое поле на старых серверах; сохраненное недопустимое поле все равно приведет к сбою при единственной повторной попытке.

## Ручная интеграция компиляции

Если настройка не может безопасно изменить конфигурацию вашего проекта, добавьте этот алиас и эти функции в модуль проекта Mix вручную, сохранив любые пользовательские действия компиляции:

```elixir
# In aliases/0:
compile: &compile/1

# In the same Mix project module:
defp compile(args) do
  if "--no-compile" not in args do
    compile_tuist_ex()
  end

  Mix.Task.run("deps.loadpaths", ["--no-compile", "--no-deps-check"])
  Mix.Task.run("tuist.compile", args)
end

defp compile_tuist_ex do
  deps =
    ["tuist_ex"]
    |> Mix.Dep.filter_by_name(Mix.Dep.load_and_cache(), include_children: true)
    |> Enum.reject(&(Mix.Dep.ok?(&1) and &1.scm.fetchable?()))
    |> Enum.map(&Atom.to_string(&1.app))

  if deps != [], do: Mix.Task.run("deps.compile", deps)
end
```

Эти функции компилируют и загружают Tuist перед другими зависимостями, в том числе при холодных параллельных сборках, и пропускают эту работу, когда Tuist и его зависимости уже скомпилированы или команда выполняется с `--no-compile`. Те же функции работают с установками Hex, Git и по пути; путь к скрипту не требуется.

Если ваш алиас начинается с `deps.compile tuist_ex --include-children`, каждая `mix compile` пересобирает зависимости Tuist и может перекомпилировать весь ваш проект. Запустите `mix tuist.setup` снова, чтобы заменить его.

## Как это работает

Кэширование Elixir использует общий конвейер REAPI ActionCache/CAS, аутентификацию и аналитику с подписью Kura, используемую Bazel, а не отдельный сервис хранения Mix. Моделируемый ключ ввода ссылается на ActionResult, содержащий дайджест SHA-256 сырых байтов полного пакета компиляции. Tuist проверяет его файлы, метаданные приложения, объявленные BEAM-файлы и манифест SCM перед поэтапной установкой. Поврежденные записи исправляются после успешной компиляции.

На OTP 28 и более поздних версиях удаленные сеансы согласовывают сжатие zstd для передач ByteStream объемом не менее 1 КиБ. Несжимаемые загрузки, старые среды выполнения OTP и серверы без сжатия используют транспорт без преобразований. Дайджесты, ограничения артефактов и сообщаемые байты относятся к несжатому содержимому. Декодированные байты ограничиваются и проверяются перед установкой. Каждая зависимость остается единым пакетом CAS, что позволяет избежать запросов по файлам или чанкам для небольших артефактов.

Удаленные учетные данные/подключения разрешаются лениво; бездействующие сборки и локальные попадания (включая уже актуальные выходные данные) не обращаются к удаленным конечным точкам. Один сеанс закрывается при каждом выходе, включая сбой компилятора. Клиентские RPC имеют крайний срок 30 секунд. Mix остается ответственным за проверки блокировок, локальную компиляцию зависимостей и неподдерживаемую работу.

Задержка/байты в панели управления сохраняют локальное или удаленное происхождение и различают недоступные значения от измеренного нуля. Удаленное время измеряет передачи клиентского CAS, тогда как локальное время включает проверку и установку; ни то, ни другое не включает компиляцию. Выходы CAS используют успешные передачи с подписью Kura, а не локальные копии или ключи ввода действий. Пропускная способность — это байты, деленные на измеренное время; сжатые размеры недоступны. Оцененное избегаемое время компиляции — это исходная длительность компиляции производителя, а не измеренная экономия времени сборки от начала до конца.

Независимые, готовые зависимости загружаются окнами до четырех одновременных запросов через одно соединение HTTP/2. Это ограничивает буферизацию декодированных пакетов четырьмя артефактами, каждый из которых подчиняется лимиту 64 МиБ. Планирование ключей, состояние Mix и установка остаются последовательными. Нижестоящие ключи ждут вышестоящие артефакты; предварительно полученные байты потребляются только под их точным ключом ввода, причем входные данные перепроверяются перед удаленной установкой. Уже актуальные выходные данные и локальные снимки не вызывают спекулятивные удаленные запросы. Компиляция и загрузки остаются последовательными для выбранных промахов кэша; конкурентность не является обещанием того, что каждая сборка будет быстрее.

## Может ли каждая зависимость пропустить компиляцию?

Совместимое попадание может пропустить компиляцию поддерживаемой зависимости, но `cache: true` работает в режиме fail-open, а не гарантирует нулевую компиляцию. Отсутствующие/эвиктированные записи, неподдерживаемые сборщики и холодная загрузка SDK все еще могут приводить к компиляции. Высокий процент попаданий сам по себе не гарантирует ускорения реального времени.

Для CI с одной сборкой и множеством тестов используйте
<.localized_link href="/guides/features/test-sharding/elixir">Elixir test sharding</.localized_link>
для распределения точно подготовленной сборки, вместо того чтобы ожидать, что каждый воркер восстановит ее из попаданий кэша по каждой зависимости. Повторное использование полной сборки, особенно содержащей нативные библиотеки, все еще требует совместимой платформы, инструментария и конфигурации. Произвольные пользовательские сборки не могут безопасно пропускать свои шаги сборки без моделирования их внешних входных данных, выходных данных и требуемых побочных эффектов.
