chore(repo): влиты изменения задачи 155

- Зачем:
  - ветка ETL должна учитывать новую витрину `purchase_vs_orders` из `main`.
- Что:
  - `origin/main` влит без переписывания истории опубликованного PR;
  - `dm_transform` теперь собирает третью витрину внутри общего конвейера.
- Проверка:
  - `make config-test`, `make lint`;
  - `generator`: `make lint`, `make typecheck`, `make test` — 429 тестов;
  - `make rebuild-storage`, `make smoke`, `make check-clickhouse`, `make check-services`;
  - импорт DAG без ошибок, `purchase_vs_orders` заполнена.
This commit is contained in:
ddadmin committed 2026-08-27 12:23:02 +03:00
commit cec11edaf3
15 files changed
+391 -38

No files matched your search

+32 -2
View File
@@ -70,9 +70,17 @@
знать, какие связки новые, а полный пересчёт карты этот след затирает —
восстановление следа оставлено лабе повышенной сложности (issue #149).
У `purchase_vs_orders` два источника и одна непрерывная пересборка. Заказная
сторона задает изменяемое окно последним принятым слепком ODS, событийная —
последний прожитый день с `purchase`. Левая граница возвращает первую дыру и
самый ранний день с `awaiting_order`: предварительная строка уже есть в
витрине, поэтому поиск отсутствующего дня ее не заметит. Правая граница
доходит до более позднего из двух хвостов. Так один прогон догоняет пакетный
разгон длиннее K и не оставляет старое ожидание замороженным.
## Витрины стенда
Оба публичных представления перечисляют деловые колонки и координаты запуска
Публичные представления перечисляют деловые колонки и координаты запуска
`_load_id`, `_load_ts` явно. Новая колонка физической таблицы сама в договор не
попадет.
@@ -98,6 +106,29 @@
шесть категорий стартового мира близки по объему. Более сложный составной ключ
распределил бы эту маленькую таблицу без нового урока.
### `dm.purchase_vs_orders_v`
Зерно — заказ. Покупки из `dds.event_v` сначала сворачиваются по
`purchase_id[1]`: контракт источника обещает один номер и одну объявленную
сумму, а отдельный счет событий оставляет будущий класс `duplicate_event`
наблюдаемым. Заказы приходят из `dds.order_v`. Обе стороны записаны
подзапросами, затем соединены `FULL OUTER GLOBAL JOIN`: голая левая
Distributed-таблица вернула бы каждый непарный заказ по разу с каждого шарда
([исследование](../research/2026-08-22-distributed-join.md)).
Непарная сторона остается `NULL` благодаря `join_use_nulls = 1`. При
`join_use_nulls = 0` ClickHouse заполнил бы ее нулем или пустой строкой и скрыл
отсутствие источника. Клиентская `declared_revenue` и серверная `items_total`
сравниваются как `Decimal(18, 2)`. Классы проверяются по порядку:
`awaiting_order` для покупки без заказа, затем `cancelled`, `lost_event`,
`duplicate_event`, `amount_delta`, `match`. Поэтому отмененный заказ без
события остается отмененным, а не потерянным.
Витрина нарезана по `order_day`, цель и донор шардируются по
`cityHash64(order_id)`. Диапазон собирается в доноре одной вставкой и
заменяется по дням. Первичная сборка охватывает всю историю обоих источников;
следующие прогоны идут по двустороннему ритму, описанному выше.
### `dm.daily_traffic_v`
В строке один день. `visitors` считает разные `client_id` из
@@ -163,7 +194,6 @@ Airflow. Партиция — день данных; прогон звена DQ
- Ключи шардирования остальных материализованных витрин — вместе с их
DDL; правило партиции-донора — в [доке хранилища](storage.md).
- Тексты учебных заданий и точный список проверок обоих контуров.
- Механика сверки A+C в SQL — рамка в спеке, разделы 4 и 7.
## Отклонённые варианты