- убраны неверные утверждения: покрытие forbidigo сужено до честного, таблица классов доменного отказа больше не претендует на полноту, механизация не подаётся как факт канона - введены недостающие определения (доменная и внешняя границы, объявление пути), критерий постоянного поля сведён к одному на R16.1 и R17 - kebab-case имени файла убран из правил в прозу: обоснование не формулировалось, номер R16 оставлен свободным
12 KiB
Категории директорий приложения
Всё, что приложение пишет на диск, делится на три категории по принципу
создания и ценности содержимого: конфигурация, данные, кеш. Категория сразу
отвечает на два вопроса, которые иначе выясняются чтением кода приложения:
кто создаёт содержимое и что будет, если его потерять. Из категорий
механически выводится состав бэкапа. Форма записи — 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), поэтому всё, что приложение туда записало, следующий деплой затирает без предупреждения. Вдобавок директория конфигурации может быть подключена только на чтение — тогда запись отказывает в рантайме. Ни то ни другое не видно в момент, когда приложение настраивают.