Bazel, stamping, remote cache (часть 2)
В Bazel есть две крайне полезные фичи:
stamping
— позволяет встроить в артефакт данные о том, от какого коммита можно собрать аналогичный артефакт;remote cache
иremote build
— позволяет иметь общий кэш между сборщиками или даже собрать артефакты на ферме.
Ранее, к сожалению, эти фичи были взаимоисключающими, но с версии Bazel 7.0 можно использовать stamping с remote cache при помощи scrubbing-а. Сегодня вышла версия Bazel 7.1, в которой появилась возможность использовать stamping с remote build.
Подробнее об этой проблеме я писал ранее в статье Bazel, stamping, remote cache.
Что такое stamping?
Stamping позволяет добавить в артефакт информацию о том, от какой версии можно собрать аналогичный артефакт.
Не ту версию, для которой была запущена сборка, а ту, от которой можно получить аналог.
К примеру, есть два коммита, которые отличаются только README-файлом. Тогда собранный из этих коммитов исполняемый файл может содержать один и тот же коммит в качестве информации о том, из какой ревизии можно его собрать, так как изменения между этими ревизиями никак на него не влияют.
Это позволяет с одной стороны иметь информацию о том, от какой ревизии можно собрать эквивалентный артефакт, а с другой стороны не пересобирать его на каждый коммит.
Как работает stamping?
Внутри stamping реализован просто: файлы, передаваемые для встраивания версии в артефакт исключаются (в случае Bazel: bazel-out/volatile-status.txt
) из ключа кэширования.
Таким образом, пересборка артефакта происходит только в том случае, если поменялось хотя бы что-то из входных параметров, кроме файла с данными для версии.
В чем проблема с remote cache?
В Bazel есть несколько кэшей. Внутренний кэш Bazel и удалённый кэш имеют разные ключи кэширования. Bazel для disk cache/remote cache/remote build используют один и тот же ключ кэш (disk cache это частный случай remote cache).
Проблема в том, что ключом кэширования action для сборки на ферме или remote cache является хэш от задачи для сборки. На этот хэш влияют все входные данные и семантика файлов для stamping-а не распространяется. То есть файлы для stamping-а влияют на хэш задачи для сборки.
Таким образом, мы получаем ситуацию, когда любая сборка всегда получает уникальный файл с данными информации для версии и никогда не попадает в кэш.
Самое неприятное, что даже отметка правила со stamping-ом для локальной сборки через тэги не исправляет ситуацию — мы будем получать одинаковый артефакт, только если попадём в кэш с предыдущей сборкой на том же сборщике.
Что такое scrubbing?
В Bazel 7.0 появился scrubbing. Он позволяет влиять на ключ кэширования для remote cache.
К примеру:
добавлять соль при хэшировании;
подменять аргументы сборки;
исключать входные файлы из ключа кэширования.
В случае stamping-а можно исключить из ключа кэширования файл bazel-out/volatile-status.txt
и мы получим при использовании remote cache то же поведение, что и при локальной сборке.
Помимо этого scrubbing позволяет решить проблему, когда нужно использовать какое-то производное от bazel-out/volatile-status.txt
для встраивания данных о версии.
Пример использования scrubbing
Для использования scrubbing нужно создать файл с конфигурацией scrubbing-а, например:
rules {
matcher {
kind: "stamping"
mnemonic: "Example"
}
transform {
omitted_inputs: "^bazel-out/volatile-status\\\\.txt$"
}
}
Список допустимых полей можно подсмотреть здесь: https://github.com/bazelbuild/bazel/blob/master/src/main/protobuf/remote_scrubbing.proto
На сборочное действие применяется трансформация первого правила, которое подходит под заявленные критерии.
Для того, чтобы конфигурация scrubbing-а использовалась при сборке, её надо передать параметром --experimental_remote_scrubbing_config
.
В чем проблема scrubbing и remote cache?
В Bazel 7.0 при попытке использовать параметр --experimental_remote_scrubbing_config
с удалённой сборкой, мы получим ошибку: Cannot combine remote cache key scrubbing with remote execution
К счастью, в Bazel 7.1 поведение изменилось (https://github.com/bazelbuild/bazel/pull/21384): вместо глобальной ошибки выполнение действий, которые подвергаются scrubbing-у, происходит на локальном хосте.
Это позволяет использовать stamping и сборку на ферме, но надо очень аккуратно подбирать matcher
-ы для правил:
нужно, чтобы под них попадало всё, что использует stamping, так как иначе будет постоянный промах мимо кэша (вне зависимости от того, меняет ли данная трансформация значение ключа кэширования);
нужно, чтобы под них не попадало лишнее, так как оно перестанет собираться на ферме и будет собираться локально (но при этом будет использоваться удаленный кэш).
Подведем итог
С версии Bazel 7.1 наконец-то появилась возможность использовать stamping и удалённую сборку, хотя и не без проблем.
Я надеюсь, что через какое-то время интерфейс трансформации для scrubbing-а будет зафиксирован и его поддержка появится в протоколе для удалённой сборки. Это должно будет снять ограничения на локальное выполнение и позволит выполнять на ферме все задачи для сборки.