# Использование распределённого кэша


Сборка больших проектов может быть ускорена с помощью распределённого кэша (YT-кэша). Сборочный кэш для определённых конфигураций и целей может быть сохранён в таблицах YT и использован при локальной сборке и сборке из Sandbox-задач.

Команда `devtools` поддерживает общий YT кэш, доступный на чтение всем разработчикам в Аркадии. Проекты могут создавать для себя собственные частные кэши с другими конфигурациями и сборочными целями.

{% note warning %}

Использование распределённого кеша может сломать отладку в Clion. Как это починить написано в [документации](https://docs.yandex-team.ru/ya-make/usage/ya_ide/clion#yt-cache-distbuild-i-otladka).

{% endnote %}


## Использование общего YT-кэша

Команда `devtools` наполняет общий YT кэш часто используемым кодом под платформы `[Linux, Linux+musl, Darwin]x[Debug, Release, Host]`. Для использования общего YT-кэша достаточно добавить в командную строку опции `--yt-store`.  Опция может быть зафиксирована либо локально на машине в файле `~/.ya/ya.conf`, либо в репозитории в файле `junk/<login>/ya.conf`.
```
yt_store = true
```
Рекомендуется использовать файл в `junk`, чтобы всегда иметь настройки, совместимые с ya.

Поскольку скачивание и сборка потребляют различные ресурсы, то на машинах с большим количеством ядер имеет смысл увеличивать количество потоков на скачивание дополнительным параметром `--yt-store-threads=<n>`.
Параметр `<n>` следует подобрать достаточно большим, чтобы скачивание с кластера [hahn](https://yt.yandex-team.ru/hahn/navigation?path=//home/devtools/cache) эффективно перекрывалось с локальной распаковкой данных. Например, в QYP-виртуалке этот параметр должен быть не меньше 16, если число ядер 32. Параметр можно также зафиксировать в `ya.conf`.

```
yt_store_threads = 16
```

{% note warning %}

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

{% endnote %}

{% note info %}

Для доступа к кэшу нужен будет сетевой доступ до кластера YT. Для большинства разработческих машин нужные сетевые доступы уже заказаны. Но если вы начинаете работу из новой проектной сети, то про нужные дырки можно прочитать в [документации YT](https://docs.yandex-team.ru/yt/gettingstarted#puncher).

{% endnote %}

{% note alert %}

Походы в кэш требуют авторизации. У всех разработчиков доступ на чтение есть по умолчанию. Для роботов, например, для сборки в Sandbox права на чтение надо заказывать явно через [IDM](https://nda.ya.ru/t/FYsZuuiX3zscLZ).

{% endnote %}


Если для работы важно, чтобы большинство общих библиотек аркадии находились в кеше, можно для отвода собственного рабочего бранча использовать не `trunk`, а `app/build-cache/hot`:
```bash
arc fetch app/build-cache/hot
arc checkout --no-track app/build-cache/hot -b my_work_branch
```
Система прогрева общего кеша гарантирует, что данный бранч всегда указывает на ревизию, для которой общие компоненты заведомо прогреты. В нормальном режиме бранч сдвигается на более свежую ревизию раз в час, а отставание этой ревизии от актуального транка обычно не превышает двух часов.

Данные в общем кеше хранятся неделю. Если какой-то файл был изменен более семи суток назад, то результаты его компиляции будут отсутствовать в кеше.

## Использование частного YT-кэша проекта

Частный кэш может быть размещён в собственной квоте проекта на выбранном класте YT. Для доступа к нему требуется настроить в одном из файлов `ya.conf` параметры доступа к кэшу. Для примера, для общего кэша эти параметры таковы:
```
yt_proxy = "hahn.yt.yandex.net"
yt_dir = "//home/devtools/cache"
```
Где `yt_dir` - путь в Кипарисе, `yt_proxy` - кластер в YT.

Соответствующие опции командной строки - `--yt-dir` и `--yt-proxy`.
Кроме этого может требоваться настроить авторизацию параметрами `--yt-token=<YT_TOKEN>` или `--yt-token-path=<YT_TOKEN_PATH>`. Опции для `ya.conf` соответственно `yt_token` и `yt_token_path`.

{% note alert %}

Никогда не складывайте `junk/<login>/ya.conf` секретные данные, например `yt_token`. Используйте `yt_token_path` или складывайте токен в локальный `~/.ya/ya.conf`

{% endnote %}


## Принцип работы

Заполнение кэша осуществляется во время запуска `ya make/package/ide` и т.п. с опцией `--yt-put` или с выставленным параметром в `ya.conf`:
```
yt_readonly = false
```

{% note warning %}

Не используйте эти параметры с общим кэшем. Права на заполнение общего кэша есть только у `devtools` и его наполнение делается специальными задачами в Sandbox.

{% endnote %}

Обычно настраивают планировщик в Sandbox для регулярного наполнения кэша. Вот, например, [список планировщиков для наполнения общего кэша](https://sandbox.yandex-team.ru/schedulers?task_type=YT_HEATER).

Чтобы не допустить неограниченного увеличения кэша, требуется во время заполнения кэша установить максимальный размер кэша с помощью опции `--yt-max-store-size=<size in bytes>` или параметра в `ya.conf`:
```
yt_max_cache_size = 57000000000000
```
Проверка размера делается до запуска сборки, поэтому требуется выставлять ограничение с запасом на текущую сборку. Ещё аккуратнее надо быть с параллельными прогревами.

Чтобы уменьшить нагрузку на сеть, можно при записи в кэш указать алгоритм сжатия c помощью опции `--yt-store-codec` или параметра `yt_store_codec`:
```
yt_store_codec = "zstd08_1"
```
Имя алгоритма сжатия хранится в YT-таблицах кэша и используется во время распаковки после скачивания.

Доступ к таблицам в YT осуществляется с помощью токена, который может быть получен по ssh-ключам на staff'е. Этот же токен можно получить запуском [`ya whoami --save-token`](https://docs.yandex-team.ru/yatool/commands/whoami) или по [ссылке](https://oauth.yandex-team.ru/authorize?response_type=token&client_id=f4d36b7671004ed9850148fa645acac6).

### Очистка кеша

Очистка кеша может производиться двумя способами. Первый способ использует встроенный в YT механизм TTL, когда фоновый процесс кластера YT удаляет строки, возраст которых превышает заданное время жизни. Второй - это самостоятельное удаление устаревших данных. Достоинства первого способа: не нужно беспокоиться о возрасте записей, удаление устаревших данных производится оперативно и сразу освобождает занятое ими дисковое пространство. Главный недостаток TTL: из кеша вымываются не только устаревшие, но и актуальные редко изменяемые файлы, например, библиотеки. В результате такие файлы внезапно становятся недоступны до следующего прогрева кеша.
Второй способ более гибкий, но немного сложнее в настройке и поддержке. Его достоинства: можно организовать LRU, что позволит держать в кеше актуальные данные сколь угодно долго, удалять данные не только по времени жизни, но и по превышению суммарного размера кеша. Недостатки: необходимость отдельной задачи для регулярной очистки кеша, фактическая очистка дискового пространства происходит с большой задержкой или вообще не происходит, что может потребовать дополнительных манипуляций с forced compaction.

## Подготовка своего YT-кэша к использованию

Перед первым использованием кэша нужно создать таблицы (`--yt-create-tables`) и настроить параметры (`--yt-store-ttl`):
```
ya make  --yt-dir=<dir> --yt-proxy=<proxy> --yt-store --yt-create-tables --yt-store-ttl=<n> --yt-put
```
Где `<n>` задает ttl в целых часах. Если предполагается самостоятельная очистка кеша, то необходимо явно указать `--yt-store-ttl=0`, потому что, если опцию не указать, будет использован ttl по умолчанию - 24 часа.

После этого надо настроить планировщик в Sandbox для заполнения или настроить окружение для заполнения во время локальной сборки. Окружение, в котором делается наполнение, должно быть "изолированным", то есть исходные файлы не должны меняться, пока идет сборка. Иначе кэш будет отравлен, так как подсчёт uid'ов в ymake делается раньше сборки файлов и никак не синхронизируется с изменениями в исходниках.

{% note warning %}

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

{% endnote %}

{% note warning %}

В случае нехватки лимита по таблетам при попытке создать таблицы для кэша возникнет такая проблема `'Account "mediabilling" is over tablet count limit'; ...`. На момент написания данного текста требовалось не мение 255 таблетов для успешного создания всех таблиц.

{% endnote %}

## Подготовка планировщиков для прогрева YT-кэша

В качестве примеров рекомендуется использовать примеры прогрева [общего кэша](https://sandbox.yandex-team.ru/schedulers?task_type=YT_HEATER).

Параметр задачи | Параметр ya
:--- | :---
ya_yt_store | yt_store
ya_yt_proxy | yt_proxy
ya_yt_dir | yt_dir
ya_yt_put | не - yt_readonly
ya_yt_store_codec | yt_store_codec
ya_yt_max_cache_size | yt_max_cache_size
ya_yt_store_threads | yt_store_threads

Токен задается с помощью
- owner'а, `ya_yt_token_vault_owner`, по умолчанию это owner задачи,
- и имени секрета, `ya_yt_token_vault_name`, по умолчанию YT_STORE_TOKEN.

Специальный параметр `ya_yt_replace_result` в задаче прогрева отключает ненужное построение того, что не должно быть загружено в YT-store.

Если кеш чистится явно (не через TTL), то есть возможность "удерживать" в нём актуальные данные. Для этого нужно передать параметр `ya_yt_store_refresh_on_read`. Он включает обновление времени последнего использования читаемых из кеша файлов, что препятствует их вымыванию из YT-store.

В прогреве рекомендуется выставить
- `check_return_code=False`,
- `keep_on=True`,
- `ignore_recurses=False`,
- `force_build_depends=True`
- `result_single_file=False`.

Параметры конфигурации для прогрева задаются параметрами задачи:
```
definition_flags
musl
target_platform_flags
```
и т.д.

Набор артефактов задается параметрами:
```
checkout_arcadia_from_url
targets
```

## Явное удаление устаревших записей в кеше

{% note info %}

Чтобы организовать LRU и явную очистку данных в таблицы кеша были добавлены новые поля (access_time и data_size в таблицу metadata, и create_time - в data). Если уже есть собственный кеш в старом формате, то для перехода на новую схему очистки данных проще всего удалить обе таблицы кеша и создать их заново с `--yt-store-ttl=0` и новыми полями.

{% endnote %}

Для явной очистки кеша реализована команда `ya gc dist_cache`. Её нет в выводе `ya gc --help`, потому что рекомендуемым способом очистки кеша является задача в Sandbox.
Пример команды:
```bash
ya gc dist_cache --yt-proxy=hahn --yt-dir=//home/some/private/cache/path --yt-token-path=~/.yt/token --yt-max-store-size=10000000000 --yt-store-ttl=72 --yt-put
```
После запуска этой команды из таблиц будут удалены все файлы старее 72 часов. Если же "нетто-размер" данных в кеше превышает 10000000000 байт, то удаляются строки новее TTL, начиная с самых старых, пока размер не станет меньше или равен заданному. "Нетто" учитывает только размер поля `data.data` умноженный на replication factor таблицы `data`, поэтому величину `yt-max-store-size` нужно подбирать опытным путём (примерно на 10-20% меньше, чем выделенная квота). Можно указывать только один из лимитов (размер или возраст), тогда очистка будет производится только по одному критерию. Если не указывать `--yt-put`, то команда напечатает сколько необходимо удалить, но фактического удаления не произойдёт (dry run). Примечание: в данной команде параметр `--yt-store-ttl` не включает встроенный в YT механизм TTL, в отличие от `ya make --yt-create-tables`.

`ya gc dist_cache` рекомендуется использовать только для отладки, а регулярную очистку лучше доверить задаче YT_CACHE_CLEANER, взяв за образец [задачи для поддержания порядка в коммунальном кеше](https://sandbox.yandex-team.ru/schedulers?task_type=YT_CACHE_CLEANER&limit=20&created=14_days). Параметр `mode` задачи задаёт выполняемые операции:
- `regular` - аналогично `ya gc dist_cache` (параметр `readonly=True` равноценен отсутствию `--yt-put` в консольной утилите). 
- `stat` - сливает (merge) чанки в таблице stat. Таблица небольшая, но пишется построчно, что создаёт чанк на каждую запись. В итоге, можно быстро исчерпать количество чанков в квоте. Чтобы такого не произошло, нужно запускать эту операцию раз в несколько дней.
- `gc` - очень тяжёлая задача, которая полностью считывает таблицу `data` и удаляет из неё строки, на которые нет ссылок из `metadata`. Сделана на случай потери ссылочной целостности между таблицами. Такая ситуация возможна и является штатной, если прогрев кэша выполняют несколько задач одновременно. Поддержка транзакционного обновления data и metadata могла бы исправить ситуацию, но это *очень* дорого с точки зрения клиента, поэтому в ya не реализовано. Запускать `gc` можно раз в неделю или реже.
- `forced_compaction` - инициирует процесс forced compaction, описанный в [справке YT](https://yt.yandex-team.ru/docs/description/dynamic_tables/sorted_dynamic_tables#remove_old_data). В настоящий момент способ устарел и вместо него рекомендуется установить на таблицу data и metadata атрибуты:
    ```
    "auto_compaction_period" = 86400000;  # периодичность очистки в миллисекундах
    "periodic_compaction_mode" = "partition"; # режим, оптимальный для таблиц yt store
    ```
    Пример установки атрибутов с помощью консольного клиента:
    ```
    ya tool yt --proxy=hahn set-attribute //home/path/to/cache/data auto_compaction_period 86400000
    ya tool yt --proxy=hahn set-attribute //home/path/to/cache/metadata auto_compaction_period 86400000
    ya tool yt --proxy=hahn set-attribute //home/path/to/cache/data periodic_compaction_mode partition
    ya tool yt --proxy=hahn set-attribute //home/path/to/cache/metadata periodic_compaction_mode partition
    ```
