- Зачем:
- карту регионов и кросс-фильтрацию предложат снова, а доводы против них
стоили двух линий ревью и замеров на живом стенде.
- Что:
- в docs/architecture/superset.md добавлен раздел «Отклоненные варианты»:
карта регионов, регион как общее измерение витрин, кросс-фильтрация между
панелями, пороги на панелях.
- к каждому отказу записаны проверенные факты и поводы вернуться к разговору.
- Проверка:
- `git diff --check`.
- `grep -rn "0018" docs` — ссылок на неоставленный ADR нет.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
16 KiB
Superset: витрины глазами
Документ описывает зону BI: куда Superset ходит за данными и за своими метаданными, из чего собран стендовый дашборд, как снять свежий экспорт после правки в интерфейсе и почему правка руками живет только до перезапуска.
Состав сюжетов и колонок витрин здесь не повторяется: он живет в спеке, раздел 7, и в доке слоя DM.
Устройство зоны
Superset читает ClickHouse пользователем bi на ноде 2 — так задано
подключением ClickHouse (clickhousedb://bi@clickhouse-02:8123). Нода 2
здесь — ловушка правильных ошибок из
спеки, раздел «Роли нод»: DDL идет
с ноды 1, и забытый ON CLUSTER виден в дашборде.
Пароля в подключении нет. Его отдает штатное хранилище паролей Superset
(SQLALCHEMY_CUSTOM_PASSWORD_STORE в infra/superset/superset_config.py),
которое читает CLICKHOUSE_BI_PASSWORD из окружения. Поэтому файл подключения
безопасно лежит в git, и экспорт из интерфейса не должен возвращать пароль
обратно в YAML.
Свои метаданные — подключения, датасеты, графики, дашборды, пользователи —
Superset держит в базе superset служебного postgres-metadata, рядом с базой
Airflow. В ClickHouse он не пишет ничего.
Дерево импорта
infra/superset/import/ — это дашборд стенда в виде файлов. Каталог
смонтирован в контейнер как /app/import.
| Файл | Что описывает |
|---|---|
metadata.yaml |
версия формата, 1.0.0; больше в нем ничего нет |
databases/clickhouse.yaml |
подключение к ClickHouse: URI без пароля и постоянный uuid |
datasets/ClickHouse/*.yaml |
три витрины: revenue_daily_v, purchase_vs_orders_v, daily_traffic_v |
charts/*.yaml |
три графика: столбики выручки, сводная таблица сверки, линии трафика |
dashboards/business_stories.yaml |
дашборд «Бизнес-сюжеты» и раскладка панелей |
Связаны файлы не путями, а uuid. Датасет называет database_uuid, график —
dataset_uuid, а дашборд не называет графики вовсе: единственная связь с ними
лежит в его position, где узел CHART-* несет meta.uuid нужного графика.
График, которого нет в position, импортируется молча и не появляется нигде.
Uuid у стенда розданы рукой: общее начало
19600000-0000-4000-8000-, дальше номер объекта в последнем блоке — 1–3
у датасетов, 101–103 у графиков, 201 у дашборда
(…-000000000101). Экспорт из интерфейса приносит свои, сгенерированные;
их заменяют на эти, иначе повторный импорт заведет вторую копию объекта вместо
обновления первой.
Датасет несет русские имена колонок, формат денег и метрику — а график только ссылается на них по имени. Значит, подпись или формат правятся в одном месте, а не в каждом графике.
Датасеты объявлены физическими (schema: dm, sql: null), хотя за именем стоит
представление ClickHouse. Импорт в источник не ходит и разницы между таблицей и
представлением не знает, а виртуальный датасет со своим SQL просто повторил бы
запрос, который уже живет в sql/ddl/60-dm-views.sql.
Импорт при каждой инициализации
Последняя строка infra/superset/init.sh:
superset import-directory /app/import --overwrite
Служба superset-init отрабатывает перед подъемом самого Superset, то есть при
каждом make up. Без --overwrite второй прогон падал бы на первом же
знакомом uuid; с ним объект обновляется на месте, и повторная инициализация
проходит без ошибок.
Отсюда правило зоны: стендовый дашборд не расширяют руками в интерфейсе. Правка живет до следующей инициализации, а потом молча заменяется тем, что лежит в git. Пробовать интерфейс менти никто не мешает — но все, что должно пережить перезапуск, приезжает файлом.
Это правило относится только к стендовым объектам. Свои датасеты, графики и
дашборды менти живут в той же базе метаданных Superset, а импорт трогает
только объекты с известными ему uuid. Поэтому свои объекты переживают
make up и make rebuild-storage. Их стирает только make clean или
адресный сброс базы метаданных по рецепту ниже.
Как снять свежий экспорт
Маршрут один: правишь на живом стенде, забираешь экспорт, кладешь дерево в git.
-
Собрать или поправить график и дашборд в интерфейсе — на стенде, а не в файлах. Ключи
paramsграфиков велики, недокументированы и меняются от версии к версии; неверный ключ не ошибается, а рисует пустоту. Интерфейс здесь единственный надежный оракул. -
Снять экспорт и достать его из контейнера:
docker compose exec superset superset export-dashboards -f /tmp/export.zip docker compose cp superset:/tmp/export.zip /tmp/export.zip unzip -d /tmp/superset-export /tmp/export.zipВнутри архива — один каталог
dashboard_export_<дата>ровно той же формы, что и дерево импорта. Команда выгружает все дашборды сразу; на стенде он один. -
Вычистить экспортный сор перед коммитом:
- выбросить экспортный
databases/— нашclickhouse.yamlостается как есть, с постоянным uuid и без пароля; - оставить в
metadata.yamlодну строкуversion: 1.0.0; - убрать из графиков
params.datasource,params.slice_idиparams.dashboards— это идентификаторы строк того Superset, где снимали, а импорт выводит связи из uuid; - убрать
query_context— снимок запроса оттуда же. Импорт его не требует и обратно не заполняет: после переимпорта он остается пустым, на отрисовку это не влияет; - перезаписать русские строки читаемыми. Экспорт кодирует их
\uXXXX-последовательностями, и после каждого круга их приходится раскодировать заново: имена графиков и подписи колонок читает менти, и в файле, и в диффе. Годится любой повторный дамп YAML сallow_unicode=True; - вернуть постоянные uuid из схемы выше.
- выбросить экспортный
-
Скопировать вычищенное дерево в
infra/superset/import/и проверить импортом на пустых метаданных — иначе прогон проверит только обновление уже стоящих объектов, а молчаливый пропуск нового спрячется за старым. Активы Superset лежат в базеsupersetслужбыpostgres-metadata, поэтому адресный сброс — эта база:docker compose stop superset docker compose exec postgres-metadata sh -c \ 'psql -U "$POSTGRES_USER" -d postgres \ -c "DROP DATABASE superset WITH (FORCE)" \ -c "CREATE DATABASE superset OWNER \"$SUPERSET_METADATA_USER\""' docker compose run --rm superset-init docker compose up -d supersetБазу приходится создавать тут же: скрипт
infra/postgres/init/заводит ее один раз, при первой инициализации тома Postgres.make rebuild-storageдля этой проверки не годится — он гоняетworld_recreateв ClickHouse и метаданных Superset не касается.make cleanсбросит и их, но заодно снесет весь стенд, включая метаданные Airflow в той же службе.После импорта дашборд смотрят глазами: это единственное доказательство — см. ловушку ниже.
Ловушки
Импорт молчит. График, чей dataset_uuid не разрешился, отбрасывается без
единого слова; дашборд, у которого не сошлись ссылки на графики,
пропускается. Ошибки уходят в журнал, и init.sh
завершается нулем в любом случае. Поэтому импорт проверяют глазами или
запросом к API — но не тем, что команда отработала.
--overwrite синхронизирует датасет разрушительно. Колонки и метрики
берутся из YAML целиком: добавленная в интерфейсе метрика или подпись колонки
исчезнет при следующем старте контейнера. Правка датасета — это правка файла.
Что проверяет стенд
make check-services входит в Superset, находит подключение ClickHouse,
сверяет его uuid и безопасный URI и требует схему dm — то есть подтверждает
живое подключение и хранилище пароля. Сами графики и дашборд ни одна цель не
проверяет: их смотрят глазами после переинициализации. Полная карта целей — в
доке проверок.
Отклоненные варианты
Оживить дашборд предложат снова, поэтому отказы записаны с доводами. Замерено на живом стенде 7 сентября 2026 года, Superset 6.1.
Карта регионов России. Замысел стоял на клике: щелчок по региону
перестраивает весь экран. Плагин Country Map кросс-фильтр не отдает вовсе —
behaviors не объявлены, setDataMask не вызывается, а щелчок приближает
карту. Без клика остается картинка, и она рассказывает меньше, чем кажется: 16
городов стенда сворачиваются в 14 регионов из 83, и больше половины сессий
приходится на один. Карта отдана факультативу «Добавь названия регионов в
отчет»: там читатель собирает справочник сам, и она становится наградой за
него.
Регион как общее измерение витрин. Чтобы фильтр по региону действовал на
весь экран, регион пришлось бы завести в каждой витрине: Superset датасеты
между собой не соединяет, а фильтр по колонке, которой нет в датасете панели,
отбрасывается с причиной COL_NOT_IN_DATASOURCE. У заказа географии нет — она
бралась бы от человека через карту идентичностей и приписывалась бы сущности, у
которой ее в источнике не было. Вдобавок не сходятся ритмы: витрина от карты
идентичностей не замерзает никогда, а выручка живет окном последнего слепка
(дока DM). Стендовый словарь регионов отклонен раньше и по своей
причине — он повторил бы урок dictGet каталога вторым разом.
Кросс-фильтрация между панелями. Общих измерений у витрин почти нет:
категория есть только у выручки, а день у сверки называется order_day против
report_date у остальных. Клик перестроил бы часть экрана и оставил другую
нетронутой, а вывод из этого читатель делает про данные, а не про устройство
витрин. Панель фильтров этой болезнью не болеет — там колонка задается явно для
каждой витрины, — поэтому фильтр периода дашборду разрешен, а кросс-фильтрация
нет.
Пороги и раскраска панелей. Правило спеки, раздел 9: на дашборд смотрят глазами, красное и зеленое живет в проверках.
Поводы вернуться к разговору: плагин карты начинает отдавать кросс-фильтр; у заказа появляется адрес доставки; витрины получают общее измерение по своей причине, не ради дашборда.