feat(dm): добавлены витрины выручки и трафика
Зачем: - Показать два ритма пересборки агрегатов на готовых витринах DM. Что: - Добавлены физические модели и публичные договоры revenue_daily и daily_traffic. - Добавлен dm_transform с оконной заменой выручки и полным пересчетом трафика. - Отрицательный опыт локального соединения через публичные _v зафиксирован; выбран явный GLOBAL LEFT JOIN. - Замены партиций сериализованы после подтвержденного OOM при трех одновременных задачах. - Обновлены архитектурные документы и мастер-спека. Проверка: - make lint, make config-test, make smoke, make check-clickhouse, make check-services. - Чистая сборка world_recreate, dds_transform и dm_transform; меры сверены с прямыми агрегатами.
This commit is contained in:
1 parent
c9bcacd63b
commit
da0477f0f1
12 files changed
+436
-25
No files matched your search
@@ -89,10 +89,8 @@
|
||||
- Сборка сессий: ключи нарезки (`client_id`, `event_time`,
|
||||
`event_date`), тип события, страница, UTM, паспорт куки — категория
|
||||
устройства приходит уже расшифрованной.
|
||||
- Витрины `dm.events_enriched_v`, `dm.top_pages_daily_v`,
|
||||
`dm.utm_effectiveness_v` (`declared_revenue` — из `purchase_revenue`),
|
||||
`dm.daily_traffic_v` (пара «посетители / известные» через
|
||||
`dds.identity_map_v`).
|
||||
- Витрины `dm.events_enriched_v`, `dm.top_pages_daily_v` и
|
||||
`dm.utm_effectiveness_v` (`declared_revenue` берется из `purchase_revenue`).
|
||||
- Событийная сторона сверки `dm.purchase_vs_orders_v`: `purchase_id`,
|
||||
`purchase_revenue`, `event_date`.
|
||||
- DQ-сверка сессий с эталоном читает не его, а `ods.event_v` —
|
||||
|
||||
+54
-8
@@ -31,8 +31,8 @@
|
||||
витрины, на него ссылаются учебные задания;
|
||||
- `purchase_vs_orders` — сверка A+C с классами расхождений; зерно — заказ,
|
||||
шардирование сохраняет ко-локацию заказов по `cityHash64(order_id)`;
|
||||
- `daily_traffic` — пара «посетители» / «известные пользователи», локальное
|
||||
обогащение [картой идентичностей](dds/identity.md);
|
||||
- `daily_traffic` — пара «посетители» / «известные пользователи», обогащение
|
||||
[картой идентичностей](dds/identity.md);
|
||||
- `dq_summary` — сводка проверок качества (ниже).
|
||||
|
||||
Остальные витрины спеки — учебные задания: менти строит их сам по ТЗ с
|
||||
@@ -55,8 +55,8 @@
|
||||
|
||||
- **от событий и сессий** — день замерзает сразу; пересборка догоняет
|
||||
хвост несобранных дней заменой дневных партиций;
|
||||
- **от заказов** — день дышит окном K и закрывается; пересборка заменяет
|
||||
партиции окна, дни за ним закрыты по построению;
|
||||
- **от заказов** — день дышит, пока входит в последний слепок; пересборка
|
||||
заменяет партиции этого отрезка, дни за ним закрыты по построению;
|
||||
- **от карты идентичностей** — не замерзает никогда: склейка
|
||||
ретроспективна, покупка сегодня делает куку известной во всех её прошлых
|
||||
днях, а карта пересчитывается целиком.
|
||||
@@ -64,12 +64,58 @@
|
||||
Поэтому `daily_traffic` наследует у своего источника и ритм, и механику:
|
||||
полный пересчёт всех дней каждый прогон в `ReplacingMergeTree(_load_ts)`,
|
||||
как у самой карты. Дневных партиций у неё нет: партиция — единица замены,
|
||||
а эта витрина заменяется только целиком. Пересчёт дешёв:
|
||||
витрина читает сессии и карту локальным соединением по ключу ко-локации.
|
||||
а эта витрина заменяется только целиком. На стенде такая работа остается
|
||||
быстрой; витрина читает сессии и карту через их публичные договоры.
|
||||
Пересчитывать точечно только затронутые дни не из чего: для этого надо
|
||||
знать, какие связки новые, а полный пересчёт карты этот след затирает —
|
||||
восстановление следа оставлено лабе повышенной сложности (issue #149).
|
||||
|
||||
## Витрины стенда
|
||||
|
||||
Оба публичных представления перечисляют деловые колонки и координаты запуска
|
||||
`_load_id`, `_load_ts` явно. Новая колонка физической таблицы сама в договор не
|
||||
попадет.
|
||||
|
||||
### `dm.revenue_daily_v`
|
||||
|
||||
Зерно: день заказа × категория товара. Витрина читает только оплаченные
|
||||
заказы из `dds.order_v`, синхронно разворачивает три массива позиций и узнает
|
||||
категорию артикула через `dic.products`. Один заказ с несколькими позициями
|
||||
категории считается в `orders` один раз. `units` складывает количество единиц,
|
||||
`revenue` складывает цену позиции × количество. Скидка, доставка и итог заказа
|
||||
между категориями не делятся. `aov` равен `revenue / orders` в том же срезе и
|
||||
хранит два знака после запятой.
|
||||
|
||||
Первый запуск строит всю доступную историю. Следующие берут границы дней из
|
||||
последнего слепка ODS: только источник знает, какие даты еще может изменить.
|
||||
Пропущенный оплаченный день расширяет левую границу, чтобы частичный отказ
|
||||
не оставил дыру после следующего прогона. Агрегат при этом читает DDS; ODS
|
||||
отвечает только на вопрос об объеме.
|
||||
|
||||
`dm.revenue_daily_rep` нарезана по `report_date`; день собирается в
|
||||
таблице-доноре и заменяется через `REPLACE PARTITION`. Цель и донор шардируются
|
||||
по `cityHash64(product_category)`. Кроме дня, у витрины только категория, а
|
||||
шесть категорий стартового мира близки по объему. Более сложный составной ключ
|
||||
распределил бы эту маленькую таблицу без нового урока.
|
||||
|
||||
### `dm.daily_traffic_v`
|
||||
|
||||
В строке один день. `visitors` считает разные `client_id` из
|
||||
`dds.session_v`; `known_users` считает разные `user_id` после левого соединения
|
||||
с полной текущей `dds.identity_map_v`. Анонимная сессия остается в витрине.
|
||||
Несколько кук одного человека увеличивают `visitors`, но дают одного
|
||||
`known_user`, поэтому `known_users <= visitors`.
|
||||
|
||||
Сессии и карта лежат по `client_id`, но публичные `_v` скрывают
|
||||
`Distributed`-таблицы от локальной подстановки. На ClickHouse 26.3
|
||||
`EXPLAIN PLAN` показал чтение обоих шардов на инициаторе: настройка
|
||||
`distributed_product_mode = 'local'` локального соединения не дала. Поэтому
|
||||
витрина использует явный `GLOBAL LEFT JOIN` через публичные договоры, а не
|
||||
подменяет их физическими таблицами. Это дороже по сети, зато граница слоев
|
||||
остается явной. Новая связь куки меняет прошлые дни, поэтому каждый запуск
|
||||
пишет полную историю новой версией в `ReplacingMergeTree(_load_ts)`, а
|
||||
публичное представление выбирает текущие строки через `FINAL`.
|
||||
|
||||
## `dm.dq_summary`
|
||||
|
||||
Зерно — «день данных × проверка»: строка отвечает, каково качество дня
|
||||
@@ -113,7 +159,7 @@ Airflow. Партиция — день данных; прогон звена DQ
|
||||
|
||||
## Остаётся нарезке
|
||||
|
||||
- Имена задач `dm_transform`, звена DQ и дага самопроверки.
|
||||
- Имена звена DQ и дага самопроверки.
|
||||
- Ключи шардирования остальных материализованных витрин — вместе с их
|
||||
DDL; правило партиции-донора — в [доке хранилища](storage.md).
|
||||
- Тексты учебных заданий и точный список проверок обоих контуров.
|
||||
@@ -136,7 +182,7 @@ Airflow. Партиция — день данных; прогон звена DQ
|
||||
расшифровывать. Красное «ещё не построено» честнее.
|
||||
- **Самопроверка пишет в `dq_summary`.** Попытки менти смешались бы с
|
||||
историей качества конвейера.
|
||||
- **Окно K для `daily_traffic` — прошлое замерзает.** Дни за окном хранили
|
||||
- **Окно заказов для `daily_traffic` — прошлое замерзает.** Дни за окном хранили
|
||||
бы число известных на момент закрытия — витрина тихо расходится с картой
|
||||
и протаскивает обратно «знание на дату», отвергнутое развилкой карты
|
||||
(#133).
|
||||
|
||||
@@ -521,8 +521,10 @@ SQL-файлы исполняет штатный `SQLExecuteQueryOperator`. Пр
|
||||
| `20-ods-tables.sql` | типизированное событие, версии заказа и обе таблицы ошибок |
|
||||
| `30-ods-views.sql` | актуальные события, текущие заказы и матвью разбора в ODS |
|
||||
| `40-stg-views.sql` | матвью приёма: чтец в сырьё |
|
||||
| `45-dds-tables.sql` | физические модели заказа и сессии, доноры их партиций |
|
||||
| `50-dds-views.sql` | публичные договоры события, заказа и сессии на языке модели DDS |
|
||||
| `45-dds-tables.sql` | физические модели заказа, сессии и карты идентичностей, доноры партиций |
|
||||
| `50-dds-views.sql` | публичные договоры сущностей DDS |
|
||||
| `55-dm-tables.sql` | физические витрины выручки и трафика, донор партиций выручки |
|
||||
| `60-dm-views.sql` | публичные договоры витрин DM |
|
||||
|
||||
Справочники идут сразу за базами: зона `dic` не зависит ни от одного слоя, и
|
||||
правила порядка слоёв к ней не применяются.
|
||||
@@ -589,6 +591,11 @@ SQL-файлы исполняет штатный `SQLExecuteQueryOperator`. Пр
|
||||
| `dds` | `dds.session_v` | публичный договор сессии |
|
||||
| `dds` | `dds.identity_map_rep` / `_dist` | известная пара куки с пользователем; таблицы полного пересчёта |
|
||||
| `dds` | `dds.identity_map_v` | точный публичный договор карты идентичностей с `FINAL` |
|
||||
| `dm` | `dm.revenue_daily_rep` / `_dist` | дневная выручка по категориям |
|
||||
| `dm` | `dm.revenue_daily_stage_rep` / `_dist` | таблица-двойник, донор дневных партиций выручки |
|
||||
| `dm` | `dm.revenue_daily_v` | публичный договор дневной выручки |
|
||||
| `dm` | `dm.daily_traffic_rep` / `_dist` | версии дневных счетчиков трафика |
|
||||
| `dm` | `dm.daily_traffic_v` | точные текущие счетчики трафика с `FINAL` |
|
||||
| `dic` | `dic.products_file` | чтец CSV-каталога, общего с генератором |
|
||||
| `dic` | `dic.products` | словарь товаров поверх подложки |
|
||||
|
||||
@@ -598,9 +605,8 @@ SQL-файлы исполняет штатный `SQLExecuteQueryOperator`. Пр
|
||||
|
||||
В слое DDS построены событие, заказ, сессия и карта идентичностей. Событие
|
||||
остаётся представлением над ODS; заказ и сессия гасят повтор заменой партиции;
|
||||
карта пишет полный набор пар в версионную таблицу. Слой DM принесут следующие
|
||||
тикеты этапа. Его состав задан разделом 7 мастер-спеки и переносится сюда по
|
||||
мере постройки.
|
||||
карта пишет полный набор пар в версионную таблицу. В DM выручка заменяет
|
||||
партиции изменяемых дней, а трафик пишет полную историю новой версией.
|
||||
|
||||
## Что проверено
|
||||
|
||||
|
||||
@@ -367,8 +367,10 @@ CSV в репозитории (`data/catalog/products.csv`: `sku`, `name`, `cate
|
||||
ошибка в `*_errors`, а не молчаливые нули: имена CamelCase регистрозависимы,
|
||||
опечатка иначе не падает.
|
||||
- **Политика соединений**: по ключу ко-локации — обычное соединение с
|
||||
комментарием, почему локальный результат корректен; по любому другому ключу
|
||||
— только явный GLOBAL; `NOT IN` — только `GLOBAL NOT IN`. Сверка
|
||||
комментарием, почему локальный результат корректен; если публичные
|
||||
представления скрывают ко-локацию — решение записывается рядом с витриной.
|
||||
По любому другому ключу — только явный GLOBAL; `NOT IN` — только
|
||||
`GLOBAL NOT IN`. Сверка
|
||||
`purchase`↔заказ —
|
||||
легитимная GLOBAL-витрина (заказы малы).
|
||||
- **Конвейер без TRUNCATE**: поток версий — в ReplacingMergeTree, точное чтение
|
||||
@@ -505,8 +507,9 @@ Kafka день переигрывается генератором заново:
|
||||
счётчики `purchases`/`add_to_carts` оживают из таксономии, добавляется
|
||||
`declared_revenue` по UTM.
|
||||
- **`daily_traffic_v`** — расширяется парой «посетители» / «известные
|
||||
пользователи» (обогащение через `dds.identity_map_v`, локальное соединение
|
||||
по ключу ко-локации).
|
||||
пользователи». Обогащение идет явным `GLOBAL LEFT JOIN` через
|
||||
`dds.identity_map_v`: опыт на ClickHouse 26.3 показал, что публичные `_v`
|
||||
скрывают ко-локацию от локальной подстановки.
|
||||
- `events_enriched_v`, `top_pages_daily_v`, `session_overview_v`,
|
||||
`dq_errors_daily_v` — переезжают на новую модель без смены роли: источник —
|
||||
`dds.event_v`, не `ods.event`.
|
||||
@@ -684,10 +687,6 @@ Kafka Engine на двух нодах снят с этого списка при
|
||||
`ReplacingMergeTree`. Проверять то, что заведомо случается и заведомо
|
||||
обезврежено, — работа без ответа на конце.
|
||||
|
||||
- Точную форму локального обогащения `daily_traffic_v` через публичные `_v` —
|
||||
эмпирически при проектировании витрин #135. Общее поведение соединения двух
|
||||
Distributed-таблиц уже проверено в
|
||||
[исследовании](../research/2026-08-22-distributed-join.md).
|
||||
- Размер артефакта эталонного мира после пересборки.
|
||||
- Спорные API (Airflow Datasets/сенсоры, ClickHouse DDL) — перед кодом
|
||||
сверять через MCP Context7 (правило AGENTS.md).
|
||||
|
||||
Reference in new issue
Block a user