# Категории директорий приложения Всё, что приложение пишет на диск, делится на три категории по принципу создания и ценности содержимого: конфигурация, данные, кеш. Категория сразу отвечает на два вопроса, которые иначе выясняются чтением кода приложения: **кто создаёт** содержимое и **что будет, если его потерять**. Из категорий механически выводится состав бэкапа. Форма записи — `common/language.md`. ## Область действия Раскладка меняется вместе с миграцией данных, поэтому правила распространяются на **новые приложения**; существующие переезжают по мере касания, отдельной кампанией не переписываются. Разделять данные и кеш задним числом имеет смысл тогда, когда кеш заметен по объёму в бэкапе, а не ради самой схемы. ## Правила ### R1. Записываемые пути разложены по трём категориям **ДОЛЖЕН.** Каждая директория, в которую пишет приложение или деплой, относится к одной из трёх категорий: | № | Категория | Директория | Создаёт | Потеря содержимого | В бэкапе | |---|---|---|---|---|---| | R1.1 | конфигурация | `config/` | деплой | восстанавливается прогоном деплоя | нет | | R1.2 | данные | `data/` | приложение | невосполнима | да | | R1.3 | кеш | `cache/` | приложение | приложение перегенерирует | нет | Имена в таблице — умолчание для случая «одна директория на категорию». **Почему.** Все дальнейшие решения — что попадает в бэкап (R4), что можно снести при нехватке места, что переживает переезд на другой диск — читаются из категории, а не выясняются по коду приложения. Без единой классификации каждое такое решение принимается заново и каждый раз чуть по-другому, а цена ошибки несимметрична: лишний кеш в снапшоте стоит места, потерянные данные не стоят ничего, потому что их больше нет. ### R2. Категория может состоять из нескольких директорий **ДОПУСКАЕТСЯ.** Директорий в категории столько, сколько нужно раскладке; принадлежность к категории задаётся не именем, а участием в списке бэкапа. **Почему.** Явное разрешение снимает вопрос, не читается ли R1 как «ровно три директории». Крупные файлы отделяют от базы, чтобы двигать их между дисками независимо (`media/`, `uploads/` — та же категория «данные», что и `data/`); запрет на такое деление вынуждал бы либо держать всё на одном диске, либо выводить директорию из-под категорий вовсе. Категорию нельзя задавать именем ровно поэтому: имён в категории несколько, и выбираются они по содержимому. ### R3. Данные и кеш разделяются по тесту на пересоздание **ДОЛЖЕН.** Записываемый путь относят к данным или к кешу по содержимому: | № | Что лежит | Категория | |---|---|---| | R3.1 | база, загруженные файлы, сгенерированные артефакты | данные | | R3.2 | миниатюры, распакованные ассеты, кеш внешних ответов, перестраиваемые индексы | кеш | | R3.3 | спорный случай | `rm -rf` и запуск приложения заново: поднялось и наверстало само — кеш; не поднялось или поднялось пустым — данные | **Почему.** Без внешнего теста граница проводится по ощущению «жалко потерять», а оно смещено в одну сторону: дорогой в пересборке кеш переезжает в данные и раздувает каждый снапшот. Дорого ≠ невосполнимо, и разделяет эти два свойства именно способность приложения пересоздать содержимое. Обратная ошибка — данные, названные кешем, — тестом обнаруживается в тот же момент, но дёшево: при попытке пересоздать, а не при попытке восстановить. ### R4. В бэкап идут данные, и только они **ДОЛЖЕН.** Директории категории «данные» — в списке бэкапа; конфигурация и кеш — нет. **Почему.** Кеш раздувает снапшот содержимым, которое приложение восстановит само. Конфигурацию бэкапить не только незачем, но и вредно: там лежат секреты, а бэкапы уезжают в облако — источник истины для конфигурации остаётся в репозитории и хранилище секретов, а не в снапшоте. Ошибка в другую сторону дороже: директория данных, не попавшая в список, обнаруживается в единственный момент, когда исправить её уже нечем. ### R5. Список бэкапа ссылается на те же пути, что и создание директорий **ДОЛЖЕН.** Список выводится из категорий по R4 и ссылается на те же объявления путей, по которым директории создаются, а не набирается независимо. **Почему.** Правило вывода механическое, но применяет его человек или шаблон — то есть ошибиться можно. Общая ссылка делает целый класс ошибок невозможным: переименование директории отражается в обоих местах сразу. Независимо набранный список расходится тихо — ни деплой, ни прогон бэкапа на это не жалуются, — и расхождение между тем, что бэкапится, и тем, что нужно, проявляется при восстановлении. ### R6. Способ попадания данных в бэкап зависит от того, кто отвечает за консистентность **ДОЛЖЕН.** Способ выбирается по тому, самодостаточны ли файлы на диске: | № | Данные | В бэкап | |---|---|---| | R6.1 | файлы самодостаточны на любой момент времени | копированием | | R6.2 | консистентность обеспечивает только сама СУБД | дампом: бэкапится директория дампов, сырой каталог базы — нет | **Почему.** Файловый снапшот работающей СУБД не гарантирует консистентности: скопированный каталог может не восстановиться, и узнают об этом при восстановлении. Директория дампов — тоже данные, просто производные, поэтому R4 покрывает её без оговорок. Сырой каталог базы из списка при этом исключается: он удваивает объём снапшота и добавляет к надёжной копии заведомо ненадёжную. ### R7. Способ выбирается при заведении приложения **ДОЛЖЕН.** Решение «копировать или дампить» (R6) принимается, когда приложение заводят. **Почему.** Неверный выбор ничем себя не проявляет, пока бэкап не понадобился: прогоны копирования проходят успешно и накапливают снапшоты, из которых база не поднимется. Отложить решение — значит принять его по факту первой неудачной попытки восстановления, то есть тогда, когда данных уже нет. ### R8. Приложение разводит записываемые пути по категориям **ДОЛЖЕН.** Конфигурация приложения задаёт отдельные пути для данных и для кеша, а не один каталог на всё. **Почему.** Снаружи категория определяется только тогда, когда разным категориям соответствуют разные директории. Всё, сложенное в один каталог, заставляет составлять список бэкапа вручную, читая код приложения, — и пересматривать его при каждом обновлении, потому что новый подкаталог появляется молча. Приложение, которое не умеет разделять, тем самым дефектно; раскладка под этот дефект не подстраивается. ### R9. Приложение не пишет в директорию конфигурации **НЕ ДОЛЖЕН.** Записываемые пути приложения не указывают внутрь конфигурации. **Почему.** Конфигурация восстанавливается прогоном деплоя (R1.1), поэтому всё, что приложение туда записало, следующий деплой затирает без предупреждения. Вдобавок директория конфигурации может быть подключена только на чтение — тогда запись отказывает в рантайме. Ни то ни другое не видно в момент, когда приложение настраивают. ## Связано