Использование кэшей в GitHub Actions

Использование кэшей в GitHub Actions может существенно ускорить выполнение ваших пайплайнов, а потому игнорировать эту возможность глупо. Тем не менее без нюансов не обошлось. Дело в том, что кэши в GitHub Actions представляют из себя достаточно закрытый сервис, который предлагает пользователям минимум настроек, но обо всем по порядку.

Использование кэшей в GitHub Actions

В качестве задачи нужно подключить использование кэшей Gradle для сборки Java-приложений. Обычно это позволяет значительно ускорить процесс сборки за счет переиспользования существующих артефактов вместо повторного их сбора с нуля.

В использовании кэшей вам поможет экшн actions/cache@v3 1 и официальная документация 2. На вход он принимает совсем немного параметров, но некоторые из них имеют принципиальное влияние на эффективность переиспользования кэшей в последующем. Всего доступно три опции, описание которых вы найдете в официальной документации, а я постараюсь дать их расширенную трактовку, чтобы пролить свет на принцип работы экшна. Итак.

path

Path – это список путей до файлов или директорий, которые нужно будет сохранить (закэшировать). То есть GitHub загрузит их себе с раннера, на котором запускалась сборка вашего проекта, и сохранит в свое хранилище, предварительно заархивировав. Все это произойдет разумеется только при успешном выполнении задания.

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

Примечание: важно понимать, что прямого доступа до этого хранилища вы не имеете, ровно как у вас нет возможности самостоятельно просматривать, удалять или добавлять туда файлы или каталоги.

Пример использования экшна:

В примере выше будет создан архив, содержащий в себе два каталога (/root/.gradle/{caches,wrapper}).

Примечание: тут специально использован абсолютный путь, а не относительный через ~ (например ~/.gradle/caches). Связано это с тем, что раннер считает своей home-директорией каталог /github/home, а Gradle данные складывает именно в /root.

Теперь подробнее об остальных.

key

Key – Это основной параметр, правильная установка которого очень важна. Собственно это ни что иное, как индекс вашего сохраненного архива с кэшами. По этому индексу будет оцениваться факт попадания в кэши (cache hit).

Основной нюанс в том, что кэши будут устаревать и если вы сохраните кэш со статическим ключом, например ${{ runner.os }}-gradle-cache, то впоследствии каждая сборка вероятно будет всегда использовать именно его. По мере развития вашего проекта с каждым коммитом процент попадания в кэши будет все меньше и меньше, а следовательно эффективность их использования радикально упадет. Перезаписывать существующий кэш при этом нельзя.
Нужен механизм обновления кэшей и он есть. Можно формировать индекс кэша динамически на основе хэша файлов проекта. Для этого используется функция hashFiles.

Примечание: hashFiles возвращает одиночный хэш для одного или нескольких файлов проекта. Подробнее читайте в документации Expressions – hashFiles.

В итоге ключ может выглядеть следующим образом:

Идея в том, что при изменении файлов проекта будет меняться и их суммарный хэш. То есть в итоге у вас будет множество сохраненных архивов кэшей с разными индексами.

Ошибки

Если вдруг столкнетесь с ошибкой:

Причиной могут служить дополнительные каталоги/файлы, которые создает ваш проект во время сборки. hashFiles вероятно не может получить к ним доступ и вываливается с ошибкой. В качестве решения попробуйте указать пути до файлов более конкретно (либо выставить необходимые разрешения), например так:

Примечание: разница между двойной и одинарной звездочкой в том, что одинарная учитывает все символы, кроме слэша, а двойная и его в том чиле. Подробнее читайте в статье Filter pattern cheat sheet.

restore-keys

Параметр отвечает за порядок подбора ключей (каждому ключу, напомню, сопоставлен архив с кэшами), если вдруг точного попадания не получилось. Например ваш ключ был такой:

При вычислении хэшей получили значение:

Потом файлы, попадающие под маску **/*.gradle*, поменялись и при слующей сборке ключ стал таким:

Понятно, что точного попадания уже не произойдет, но это и не требуется. Можно отбросить часть с хэшем и сделать маску более общей:

Первая строка тут не нужна и добавлена больше для наглядности, ведь сначала и так будет проверяться факт 100% попадания key в какой-либо существующий в хранилище ключ. Если попаданий несколько, то будет взят тот ключ, у которого последнее время использования самое свежее. Дальше экшн загрузит на раннер и распакует архив с кэшами как раз в то место, которое указано в path.

Примечание: кстати, если вдруг вы стали собирать проект на другой ОС, то существующие кэши вообще теряют актуальность, ведь при новых сборках никаких cache hit не будет из-за изменения переменной ${{ runner.os }}.

Если даже в этом случае попаданий не произошло, то экшн проверит кэши в вышестоящей и основной ветках. Именно туда и только туда у него есть доступ. Кэши соседних веток он посмотреть уже не сможет. При этом разные workflow будут иметь доступ к соответствующим кэшам одной и той же ветки.

Важно отметить, что у кэшей есть срок “годности”, который по умолчанию составляет 7 дней. После этого кэш удаляется. Есть также и максимальный объем хранилища кэшей – 10ГБ. Если вдруг максимальный объем достигнут, но вы продолжаете складывать кэши, то данные будут вытесняться из хранилища по принципу “первый пришел, первый ушел”, то есть будут удаляться самые старые.

При использовании self-hosted раннеров у вас также есть возможность пользоваться кэшами, но вы должны учитывать время, в течение которого данные будут выкачиваться с серверов GitHub до вашего раннера. В итоге собрать проект без кэшей может получиться быстрее, чем выкачивать их.

Примечание: пропускная способность канала в данном случае играет ключевую роль и всегда будет бутылочным горлышком, особенно при параллельных сценариях, когда например десяток раннеров выкачивает кэши почти одновременно. Для инфраструктуры GitHub Actions это не критично, поскольку все вероятно происходит в локальной сети одного из дата-центров Azure.

В любом случае проектирование своего собственного решения не такая простая задача. Если все же этот путь для вас актуален, то важно учесть все нюансы, иначе потом придется заплатить за ошибки в разы больше. Технически это все реализовать не так сложно, а с проектированием и аудитом я могу помочь. На этом все, успехов!

Яндекс.Метрика