feat(clickhouse): справочники вынесены в зону dic, словарь читает подложку

Зачем: место словаря в dds было унаследовано от первого объекта, а не решено.
Справочник читают несколько слоёв, а производит его в хранилище ни один —
прописка внутри одного из потребителей приписывала DDS владение, которого у
него нет.

Что: заведена база dic вне цепочки STG → ODS → DDS → DM, dds.products переехал
в dic.products. Под словарём появилась подложка dic.products_file на движке
File — намеренное усложнение ради урока: в бою источник словаря приезжает
процессом, а не лежит файлом у сервера. Источник объявлен формой query и
приводит цену из целых копеек каталога в Decimal(18, 2), как у денег бэкенда.
Обновление — окном LIFETIME(MIN 60 MAX 90) вместо ручной перезагрузки, ценой
заявленной неатомарности между нодами. У словаря свой беспарольный
пользователь dict с единственным правом на чтение dic. Суффикс _file добавлен
в конвенцию имён ADR 0006. Решение, отвергнутые варианты и условия пересмотра
— ADR 0012.

Проверка: make clean && make up на собранном заново стенде — зелено, make smoke
20 проверок и 0 ошибок, make lint чисто. Словарь LOADED со 180 строками, цена
Decimal(18, 2) и точна на копейках: 188990 → 1889.90. Пользователь bi читает
словарь dictGet-ом со второй ноды, analyst — подложку соединением, база dds
пуста. Минимальное право на движок замерено тремя пользователями: достаточно
GRANT FILE ON *.*, хотя отказ называет TABLE ENGINE ON File.

Closes #105

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 11:37:26 +03:00
co-authored by Claude Opus 5
parent 862dde9fa4
commit abc94ba406
10 changed files with 428 additions and 59 deletions
+25 -11
View File
@@ -4,19 +4,23 @@
## Решение
У кластера четыре пользователя и три роли.
У кластера пять пользователей и четыре роли.
- `default` — с паролем, только служебный: проверки здоровья нод и работа
изнутри контейнеров. Приложения им не ходят.
- `etl` с ролью `etl_writer` — чтение и запись во всех слоях. Им ходит
Airflow и применяется DDL при подъёме стенда.
- `bi` с ролью `bi_reader` — чтение витрин DM и слоя DDS. Им ходит Superset.
- `analyst` с ролью `analyst_reader` — чтение всех слоёв. Им человек
подключается снаружи, из своего клиента.
- `bi` с ролью `bi_reader` — чтение витрин DM, слоя DDS и справочников `dic`.
Им ходит Superset.
- `analyst` с ролью `analyst_reader` — чтение всех слоёв и справочников. Им
человек подключается снаружи, из своего клиента.
- `dict` с ролью `dict_reader` — чтение одной базы `dic`, пароля нет. Им
словарь читает свою подложку, и больше он никем не используется
([ADR 0012](0012-dictionary-home.md)).
Роль здесь шире прав на слои: образ ClickHouse требует отдельного разрешения
на запросы `ON CLUSTER` и на чтение системных таблиц, а без второго клиент
не открывает соединение вовсе. Состав прав каждой роли — работа тикета
на запросы `ON CLUSTER`, на чтение системных таблиц и на каждый движок
внешнего источника, а без второго клиент не открывает соединение вовсе. Состав прав каждой роли — работа тикета
реализации.
Пользователи, роли и права объявлены файлами настройки сервера, а не
@@ -46,7 +50,16 @@
описания. Поэтому пользователей мало, имена у них говорящие, а границы
проходят там, где их обычно проводят в компаниях: `bi` видит витрины и слой
DDS под ними, потому что так чаще всего и бывает; заодно расхождение витрины
со слоем ниже остаётся проверяемым одним и тем же пользователем.
со слоем ниже остаётся проверяемым одним и тем же пользователем. Справочники
видны всем читателям и никому на запись: зона входит в хранилище сбоку, и её
содержимым владеет файл репозитория, а не слой.
**Беспарольный `dict` — намеренное исключение.** Пароля у него нет не по
недосмотру: ходить этой учёткой некуда, кроме локального чтения одной базы, а
пароль пришлось бы вписать в текст DDL словаря — то есть положить секрет в git.
Право у него одно, `SELECT` на `dic`. Отдельная учётка под словарь взята из
промышленной практики, где источник словаря читают минимальной служебной
учётной записью, а не общим админом.
**Секрет, а не учётные данные по репликам.** У каждой реплики в описании
кластера своя учётка — вписанная явно или подразумеваемая, и тогда это
@@ -92,10 +105,11 @@ Postgres и Grafana. Цена выбора известна: объявленн
## Следствия
До появления слоёв DDS и DM читать `bi` нечего, и это намеренно. Дашборды
рисуются поверх модели данных, а не поверх типизированных событий, поэтому
доступ Superset к ODS не планировался и правами не выдаётся. Пока витрин нет,
подключение Superset проверяется связью, а не запросом к таблице.
До появления слоёв DDS и DM читать `bi` почти нечего, и это намеренно.
Дашборды рисуются поверх модели данных, а не поверх типизированных событий,
поэтому доступ Superset к ODS не планировался и правами не выдаётся.
Единственное, что ему доступно сегодня, — справочники: с ними зона `dic`
пришла раньше своих потребителей.
Владелец матвью приёма определяется тем, кто применил DDL. Весь DDL стенда
идёт через `IF NOT EXISTS`, поэтому на существующих томах три матвью