Витрины стенда: даг dm_transform, revenue_daily и daily_traffic #154

Closed
opened 2026-08-25 15:01:03 +03:00 by ddmitry · 1 comment
Owner

Part of #6.

Цель

Слой DM рождается: база dm, даг dm_transform и два ритма пересборки на
двух витринах — revenue_daily (окно K заменой партиций) и
daily_traffic (полный пересчёт вслед за картой). Учебный результат
(dm.md): форма витрины — решение по роли; пересборка агрегатов
конвейером — производственный паттерн, ради которого слой и строится.

Что войдёт

  • База dm в sql/ddl/00-databases.sql (карта таблиц её обещает, DDL
    пока не создаёт).
  • dm.revenue_daily_rep / _dist / _v: состав — спека §7
    (report_date, product_category через dictGet каталога +
    ARRAY JOIN позиций, orders, units, revenue, aov; только
    заказы paid); ритм — от заказов: замена партиций окна K. Ключ
    шардирования решается вместе с DDL — по правилу партиции-донора
    storage.md.
  • dm.daily_traffic_rep / _dist / _v: пара «посетители / известные»;
    источники — сессии и карта идентичностей, локальное соединение по ключу
    ко-локации; ReplacingMergeTree(_load_ts) без партиций, полный
    пересчёт каждый прогон (ритм от карты, dm.md). Точную форму локального
    обогащения через публичные _v проверить эмпирически — пункт спеки §11
    уходит из списка тем же PR; если локальность через _v не выходит —
    вопрос владельцу, не тихая замена на физические объекты.
  • Список читателей в event.md устарел: daily_traffic читает сессии и
    карту, не события (dm.md) — поправить тем же PR.
  • Даг dm_transform по образцу слойных: без расписания, один активный
    прогон; имя задачи — имя витрины (revenue_daily, daily_traffic);
    задачи витрин менти добавятся их ТЗ.
  • Грант потребительской роли: bi_reader переводится с SELECT ON dm.*
    на явные _v — по образцу DDS; дальше тикеты DM добавляют свои.
    Таблица DDL-файлов в storage.md — тем же PR.
  • Служебные колонки собравшего запуска; вставка в _dist — с осознанным
    parallel_distributed_insert_select.

Границы

  • Сверка purchase_vs_orders — свой тикет; dq_summary — тикет контура
    DQ; учебные витрины — тикет ТЗ; etl_pipeline — тикет конвейера (пока
    dm_transform запускается рукой).
  • Superset-дашборд — этап 8 (#10).

Кому что

DDL и SQL витрин — Opus: revenue_daily — образец, на который будут
ссылаться ТЗ менти. Codex — опыты ритмов по брифу: день за окном не
тронут, полный пересчёт не плодит дублей в _v.

Сначала прочитать

  • docs/architecture/dm.md — целиком, это спека тикета;
  • мастер-спека, раздел 7 — состав колонок витрин;
  • docs/architecture/dds/identity.md и session.md — источники
    daily_traffic;
  • docs/architecture/storage.md — правило донора, раскладка SQL;
  • docs/architecture/etl.md — «Объём прогона».

Проверка

  • make lint, make config-test; на стенде make smoke,
    make check-clickhouse и make check-services (родился даг) зелёные.
  • revenue_daily: все меры дня (orders, units, revenue, aov)
    равны прямым агрегатам по dds.order_v со статусом paid.
  • «Дыхание»: после следующего слепка числа paid-дней внутри окна K
    меняются, дни за окном — нет.
  • daily_traffic: обе меры дня равны прямым агрегатам по источникам —
    посетители по dds.session_v, известные после склейки с
    dds.identity_map_v; покупка куки делает её известной во всех её
    прошлых днях (ретроспективность склейки).
  • Повтор прогона: деловое содержимое _v обеих витрин без изменений
    и без дублей.
  • Запрос ролью bi: оба новых _v читаются, запись запрещена, STG недоступен.
Part of #6. ## Цель Слой DM рождается: база `dm`, даг `dm_transform` и два ритма пересборки на двух витринах — `revenue_daily` (окно K заменой партиций) и `daily_traffic` (полный пересчёт вслед за картой). Учебный результат (dm.md): форма витрины — решение по роли; пересборка агрегатов конвейером — производственный паттерн, ради которого слой и строится. ## Что войдёт - База `dm` в `sql/ddl/00-databases.sql` (карта таблиц её обещает, DDL пока не создаёт). - `dm.revenue_daily_rep` / `_dist` / `_v`: состав — спека §7 (`report_date`, `product_category` через `dictGet` каталога + `ARRAY JOIN` позиций, `orders`, `units`, `revenue`, `aov`; только заказы `paid`); ритм — от заказов: замена партиций окна K. Ключ шардирования решается вместе с DDL — по правилу партиции-донора storage.md. - `dm.daily_traffic_rep` / `_dist` / `_v`: пара «посетители / известные»; источники — сессии и карта идентичностей, локальное соединение по ключу ко-локации; `ReplacingMergeTree(_load_ts)` без партиций, полный пересчёт каждый прогон (ритм от карты, dm.md). Точную форму локального обогащения через публичные `_v` проверить эмпирически — пункт спеки §11 уходит из списка тем же PR; если локальность через `_v` не выходит — вопрос владельцу, не тихая замена на физические объекты. - Список читателей в event.md устарел: `daily_traffic` читает сессии и карту, не события (dm.md) — поправить тем же PR. - Даг `dm_transform` по образцу слойных: без расписания, один активный прогон; имя задачи — имя витрины (`revenue_daily`, `daily_traffic`); задачи витрин менти добавятся их ТЗ. - Грант потребительской роли: `bi_reader` переводится с `SELECT ON dm.*` на явные `_v` — по образцу DDS; дальше тикеты DM добавляют свои. Таблица DDL-файлов в storage.md — тем же PR. - Служебные колонки собравшего запуска; вставка в `_dist` — с осознанным `parallel_distributed_insert_select`. ## Границы - Сверка `purchase_vs_orders` — свой тикет; `dq_summary` — тикет контура DQ; учебные витрины — тикет ТЗ; `etl_pipeline` — тикет конвейера (пока `dm_transform` запускается рукой). - Superset-дашборд — этап 8 (#10). ## Кому что DDL и SQL витрин — Opus: `revenue_daily` — образец, на который будут ссылаться ТЗ менти. Codex — опыты ритмов по брифу: день за окном не тронут, полный пересчёт не плодит дублей в `_v`. ## Сначала прочитать - docs/architecture/dm.md — целиком, это спека тикета; - мастер-спека, раздел 7 — состав колонок витрин; - docs/architecture/dds/identity.md и session.md — источники `daily_traffic`; - docs/architecture/storage.md — правило донора, раскладка SQL; - docs/architecture/etl.md — «Объём прогона». ## Проверка - [x] `make lint`, `make config-test`; на стенде `make smoke`, `make check-clickhouse` и `make check-services` (родился даг) зелёные. - [x] `revenue_daily`: все меры дня (`orders`, `units`, `revenue`, `aov`) равны прямым агрегатам по `dds.order_v` со статусом `paid`. - [x] «Дыхание»: после следующего слепка числа paid-дней внутри окна K меняются, дни за окном — нет. - [x] `daily_traffic`: обе меры дня равны прямым агрегатам по источникам — посетители по `dds.session_v`, известные после склейки с `dds.identity_map_v`; покупка куки делает её известной во всех её прошлых днях (ретроспективность склейки). - [x] Повтор прогона: деловое содержимое `_v` обеих витрин без изменений и без дублей. - [x] Запрос ролью `bi`: оба новых `_v` читаются, запись запрещена, STG недоступен.
ddmitry added a new dependency 2026-08-25 15:01:24 +03:00
Author
Owner

Дополнение к постановке после #150 и #165

Этот комментарий уточняет места основного текста, которые устарели после более поздних решений. Остальная постановка, границы и проверки сохраняются.

Доступ bi_reader

Пункт о переводе bi_reader с SELECT ON dm.* на отдельные _v и критерий отказа на _rep / _dist больше не действуют. При исполнении #150 проверено ограничение ClickHouse для представлений над Distributed, после чего ADR 0007 закрепил широкое право: bi_reader читает ODS, DDS и DM, а публичную поверхность задают договор _v, документация и ревью, не гранты.

Для #154 это означает:

  • GRANT SELECT ON dm.* остаётся без изменений; отдельных грантов на новые представления не требуется;
  • запросы потребителей и SQL следующих витрин называют только dm.revenue_daily_v и dm.daily_traffic_v;
  • приёмка ролью bi: оба новых _v читаются, запись запрещена, STG по-прежнему недоступен; отказ на _rep и _dist не проверяется.

Объём пересборки revenue_daily

После #165 константы K на стороне стенда нет. Под словами «окно K» в основном тексте следует понимать дни последнего слепка, состояние которых источник ещё может изменить. Границы берутся из наблюдаемого в ODS последнего snapshot_date, по тому же правилу, что в sql/dds/order_scope.sql; world_position и копия числа K для этого не используются. Сам агрегат читает только dds.order_v: обращение к ODS нужно лишь задаче объёма, чтобы назвать изменяемый отрезок. Первый прогон дополнительно строит всю доступную историю заказов.

Договор dm.revenue_daily_v

Зерно — report_date × product_category. Деловые колонки и их смысл:

  • report_date — dds.order_v.order_date;
  • product_category — категория item_sku из dic.products;
  • orders — число уникальных оплаченных заказов, в которых есть позиция категории;
  • units — сумма item_quantity позиций категории;
  • revenue — сумма item_price × item_quantity этих позиций; discount, delivery и общий total заказа по категориям не распределяются;
  • aov — revenue / orders в том же срезе, денежный результат с двумя знаками после запятой.

Массивы позиций разворачиваются синхронно, один заказ с несколькими позициями одной категории считается в orders один раз. Публичное _v перечисляет эти колонки и _load_id, _load_ts явно.

Договор dm.daily_traffic_v

Зерно — один день, без измерений, унаследованных от витрины предшественника. Деловые колонки:

  • report_date — dds.session_v.session_date;
  • visitors — uniqExact(client_id) среди сессий дня;
  • known_users — uniqExact(user_id) после обогащения сессий полной текущей dds.identity_map_v.

Соединение сохраняет анонимные сессии: отсутствие пары в карте не исключает посетителя. Несколько кук одного человека увеличивают visitors, но дают одного known_user. Поэтому known_users <= visitors, а новая покупка ретроспективно меняет прошлые дни при следующем полном пересчёте. Публичное _v перечисляет report_date, обе меры и _load_id, _load_ts явно.

Эмпирическая проверка локального соединения через публичные _v остаётся такой, как записано в задаче: если нужная локальность через них не получается, исполнение останавливается у владельца; физические объекты молча не подставляются.

## Дополнение к постановке после #150 и #165 Этот комментарий уточняет места основного текста, которые устарели после более поздних решений. Остальная постановка, границы и проверки сохраняются. ### Доступ `bi_reader` Пункт о переводе `bi_reader` с `SELECT ON dm.*` на отдельные `_v` и критерий отказа на `_rep` / `_dist` больше не действуют. При исполнении #150 проверено ограничение ClickHouse для представлений над `Distributed`, после чего ADR 0007 закрепил широкое право: `bi_reader` читает ODS, DDS и DM, а публичную поверхность задают договор `_v`, документация и ревью, не гранты. Для #154 это означает: - `GRANT SELECT ON dm.*` остаётся без изменений; отдельных грантов на новые представления не требуется; - запросы потребителей и SQL следующих витрин называют только `dm.revenue_daily_v` и `dm.daily_traffic_v`; - приёмка ролью `bi`: оба новых `_v` читаются, запись запрещена, STG по-прежнему недоступен; отказ на `_rep` и `_dist` не проверяется. ### Объём пересборки `revenue_daily` После #165 константы K на стороне стенда нет. Под словами «окно K» в основном тексте следует понимать дни последнего слепка, состояние которых источник ещё может изменить. Границы берутся из наблюдаемого в ODS последнего `snapshot_date`, по тому же правилу, что в `sql/dds/order_scope.sql`; `world_position` и копия числа K для этого не используются. Сам агрегат читает только `dds.order_v`: обращение к ODS нужно лишь задаче объёма, чтобы назвать изменяемый отрезок. Первый прогон дополнительно строит всю доступную историю заказов. ### Договор `dm.revenue_daily_v` Зерно — `report_date` × `product_category`. Деловые колонки и их смысл: - `report_date` — `dds.order_v.order_date`; - `product_category` — категория `item_sku` из `dic.products`; - `orders` — число уникальных оплаченных заказов, в которых есть позиция категории; - `units` — сумма `item_quantity` позиций категории; - `revenue` — сумма `item_price × item_quantity` этих позиций; `discount`, `delivery` и общий `total` заказа по категориям не распределяются; - `aov` — `revenue / orders` в том же срезе, денежный результат с двумя знаками после запятой. Массивы позиций разворачиваются синхронно, один заказ с несколькими позициями одной категории считается в `orders` один раз. Публичное `_v` перечисляет эти колонки и `_load_id`, `_load_ts` явно. ### Договор `dm.daily_traffic_v` Зерно — один день, без измерений, унаследованных от витрины предшественника. Деловые колонки: - `report_date` — `dds.session_v.session_date`; - `visitors` — `uniqExact(client_id)` среди сессий дня; - `known_users` — `uniqExact(user_id)` после обогащения сессий полной текущей `dds.identity_map_v`. Соединение сохраняет анонимные сессии: отсутствие пары в карте не исключает посетителя. Несколько кук одного человека увеличивают `visitors`, но дают одного `known_user`. Поэтому `known_users <= visitors`, а новая покупка ретроспективно меняет прошлые дни при следующем полном пересчёте. Публичное `_v` перечисляет `report_date`, обе меры и `_load_id`, `_load_ts` явно. Эмпирическая проверка локального соединения через публичные `_v` остаётся такой, как записано в задаче: если нужная локальность через них не получается, исполнение останавливается у владельца; физические объекты молча не подставляются.
ddmitry self-assigned this 2026-08-26 20:30:09 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: ddmitry/clickstream-data-platform#154