# Superset: витрины глазами Документ описывает зону BI: куда Superset ходит за данными и за своими метаданными, из чего собран стендовый дашборд, как снять свежий экспорт после правки в интерфейсе и почему правка руками живет только до перезапуска. Состав сюжетов и колонок витрин здесь не повторяется: он живет в [спеке](../specs/2026-07-30-stand-v2-realism.md), раздел 7, и в [доке слоя DM](dm.md). ## Устройство зоны Superset читает ClickHouse пользователем `bi` **на ноде 2** — так задано подключением `ClickHouse` (`clickhousedb://bi@clickhouse-02:8123`). Нода 2 здесь — ловушка правильных ошибок из [спеки](../specs/2026-07-30-stand-v2-realism.md), раздел «Роли нод»: 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. 1. Собрать или поправить график и дашборд в интерфейсе — на стенде, а не в файлах. Ключи `params` графиков велики, недокументированы и меняются от версии к версии; неверный ключ не ошибается, а рисует пустоту. Интерфейс здесь единственный надежный оракул. 2. Снять экспорт и достать его из контейнера: ``` 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_<дата>` ровно той же формы, что и дерево импорта. Команда выгружает все дашборды сразу; на стенде он один. 3. Вычистить экспортный сор перед коммитом: - выбросить экспортный `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 из схемы выше. 4. Скопировать вычищенное дерево в `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` — то есть подтверждает живое подключение и хранилище пароля. Сами графики и дашборд ни одна цель не проверяет: их смотрят глазами после переинициализации. Полная карта целей — в [доке проверок](testing.md).