остальные конвенции переведены на формальный язык
- 11 файлов разобраны на нумерованные правила: 220 правил в каноне, у каждого модальность и обязательный блок «Почему» - классифицирующие места оформлены таблицами, файловый статус снят отовсюду, локальные регионы сохранены под прежними именами
This commit is contained in:
+137
-80
@@ -1,89 +1,146 @@
|
||||
---
|
||||
status: рекомендуемая
|
||||
---
|
||||
|
||||
# Категории директорий приложения
|
||||
|
||||
Всё, что приложение пишет на диск, делится на три категории по принципу
|
||||
создания и ценности содержимого:
|
||||
|
||||
- **конфигурация** — то, что восстанавливается прогоном деплоя, в том числе
|
||||
секреты;
|
||||
- **данные** — то, что генерирует приложение и что нужно бэкапить;
|
||||
- **кеш** — то, что генерирует приложение и что не нужно бэкапить:
|
||||
приложение перегенерирует заново.
|
||||
|
||||
Цель — упростить оперирование данными. Категория сразу отвечает на два
|
||||
вопроса, которые иначе приходится выяснять по коду приложения: **кто
|
||||
создаёт** содержимое и **что будет, если его потерять**.
|
||||
|
||||
## Категории
|
||||
|
||||
| Категория | Директория | Создаёт | Потеря содержимого | Бэкап |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Конфигурация | `config/` | деплой | восстанавливается прогоном | не нужен |
|
||||
| Данные | `data/` | приложение | невосполнима | обязателен |
|
||||
| Кеш | `cache/` | приложение | приложение перегенерирует | не нужен |
|
||||
|
||||
Имена в таблице — умолчание для случая «одна директория на категорию».
|
||||
**Категория может состоять из нескольких директорий**, и это нормально:
|
||||
крупные файлы отделяют от базы, чтобы двигать их между дисками независимо
|
||||
(`media/`, `uploads/` — та же категория «данные», что и `data/`).
|
||||
Принадлежность к категории задаётся не именем, а участием в списке бэкапа.
|
||||
|
||||
Тест на границе данных и кеша: что будет, если сделать `rm -rf` и поднять
|
||||
приложение заново. Поднимется само и наверстает — кеш. Не поднимется или
|
||||
поднимется пустым — данные.
|
||||
|
||||
Конфигурацию бэкапить не только не нужно, но и не стоит: там лежат секреты,
|
||||
а бэкапы уезжают в облако. Источник истины для конфигурации — репозиторий и
|
||||
хранилище секретов, а не снапшот бэкапа.
|
||||
|
||||
## Данные, которые нельзя копировать на живую
|
||||
|
||||
Файловый снапшот работающей СУБД не гарантирует консистентности:
|
||||
скопированный каталог может не восстановиться. Поэтому у категории «данные»
|
||||
есть два способа попасть в бэкап:
|
||||
|
||||
- **копированием** — если файлы самодостаточны на любой момент времени;
|
||||
- **дампом** — если консистентность обеспечивает только сама СУБД. Тогда
|
||||
бэкапится директория дампов, а сырой каталог базы — нет.
|
||||
|
||||
Директория дампов — тоже данные, просто производные. Решение «копировать
|
||||
или дампить» принимается **при заведении приложения**, а не при первой
|
||||
неудачной попытке восстановления.
|
||||
|
||||
## Контракт с приложением
|
||||
|
||||
Категории — не только про деплой. Приложение **разводит свои записываемые
|
||||
пути по категориям в конфигурации**, а не складывает всё в один каталог:
|
||||
иначе категорию нельзя определить снаружи и список бэкапа приходится
|
||||
составлять вручную, читая код.
|
||||
|
||||
- Путь к БД, загруженным файлам, сгенерированным артефактам — данные.
|
||||
- Миниатюры, распакованные ассеты, кеш внешних ответов, индексы, которые
|
||||
перестраиваются, — кеш. Даже если их дорого перестраивать: дорого ≠
|
||||
невосполнимо.
|
||||
- Приложение не пишет в директорию конфигурации: она может быть доступна
|
||||
только на чтение.
|
||||
|
||||
Если приложение не умеет разделять, это его дефект, а не повод смешивать
|
||||
категории в раскладке.
|
||||
|
||||
## Список бэкапа выводится, а не составляется
|
||||
|
||||
Список бэкапа получается из категорий по правилу: туда идут данные, не идут
|
||||
конфигурация и кеш. Правило механическое — но его применяет человек или
|
||||
шаблон, поэтому список обязан ссылаться на **те же** переменные путей, что
|
||||
и создание директорий. Независимо набранный список — источник расхождения
|
||||
между тем, что бэкапится, и тем, что нужно.
|
||||
создания и ценности содержимого: конфигурация, данные, кеш. Категория сразу
|
||||
отвечает на два вопроса, которые иначе выясняются чтением кода приложения:
|
||||
**кто создаёт** содержимое и **что будет, если его потерять**. Из категорий
|
||||
механически выводится состав бэкапа. Форма записи — `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), поэтому
|
||||
всё, что приложение туда записало, следующий деплой затирает без
|
||||
предупреждения. Вдобавок директория конфигурации может быть подключена
|
||||
только на чтение — тогда запись отказывает в рантайме. Ни то ни другое не
|
||||
видно в момент, когда приложение настраивают.
|
||||
|
||||
<!-- local:отступления -->
|
||||
<!-- /local -->
|
||||
|
||||
Reference in New Issue
Block a user