Compare commits
3
Commits
3c3eb825e1
...
453a1d6ddf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
453a1d6ddf | ||
|
|
9394b789e5 | ||
|
|
4693bc5af9 |
No files matched your search
@@ -125,7 +125,7 @@ Kafka, затем заново создаст схему хранилища и
|
||||
из `CLICKHOUSE_STUDENT_PASSWORD` в `.env`. Он читает те же источники, а создавать,
|
||||
заполнять и удалять таблицы может только в базе `default`.
|
||||
Для создания учебных витрин используйте подключение, описанное
|
||||
в [инструкции к заданиям](docs/labs/README.md#как-применить-свой-ddl).
|
||||
в [инструкции к заданиям](docs/labs/README.md#выполнение-sql-в-dbeaver).
|
||||
Нативные порты `29000` и `29001` нужны `clickhouse-client`, но не DBeaver.
|
||||
Остальные пользователи и роли описаны в
|
||||
[справочнике стенда](docs/architecture/stand.md#пользователи-и-роли-clickhouse).
|
||||
|
||||
+12
-10
@@ -4,9 +4,11 @@
|
||||
и попадают в отчеты. В главах есть короткие примеры, которые можно выполнить
|
||||
на запущенном стенде перед переходом к учебным заданиям.
|
||||
|
||||
## Как читать
|
||||
## Порядок чтения
|
||||
|
||||
Читайте главы в порядке ниже. Незнакомые термины можно найти в
|
||||
Сначала запустите стенд по [инструкции](../../README.md#быстрый-старт).
|
||||
Затем читайте главы по порядку и выполняйте примеры из них.
|
||||
Незнакомые термины можно найти в
|
||||
[словаре понятий](../../CONTEXT.md), а подробности устройства —
|
||||
в [технических справочниках](../architecture/). К ним достаточно обращаться
|
||||
по мере необходимости. После путеводителя переходите к
|
||||
@@ -19,27 +21,27 @@ Gitea показывает HTML как текст. Откройте файл и
|
||||
или скачайте его кнопкой «Raw» и откройте в браузере.
|
||||
Сверяйтесь с картой при чтении главы «Хранилище и его слои».
|
||||
|
||||
## Главы
|
||||
## Содержание
|
||||
|
||||
### [Хранилище и его слои](storage-layers.md)
|
||||
|
||||
Как меняются данные при переходе из Kafka в слои STG, ODS, DDS и DM.
|
||||
В конце главы вы посмотрите события, сессии и выручку с помощью SQL.
|
||||
|
||||
### [Потоки Airflow и даги модельного времени](airflow-dags.md)
|
||||
### [Генерация и обработка данных в Airflow](airflow-dags.md)
|
||||
|
||||
Какие даги создают новые данные, а какие обрабатывают их в хранилище.
|
||||
Вы запустите генерацию и обработку за день и посмотрите результат в витринах.
|
||||
Вы сравните содержимое витрин до и после обработки нового дня.
|
||||
|
||||
### [Мониторинг: куда смотреть](monitoring.md)
|
||||
### [Мониторинг в Grafana](monitoring.md)
|
||||
|
||||
Что показывают панели Grafana и как меняются их показания, пока в стенд
|
||||
поступают события и заказы.
|
||||
поступают события и заказы. Вы увидите, почему заказы обновляются реже событий.
|
||||
|
||||
### [Немного Superset](superset.md)
|
||||
### [Отчеты в Superset](superset.md)
|
||||
|
||||
Как Superset строит графики по витринам и что происходит с вашими отчетами
|
||||
при перезапуске стенда.
|
||||
Как Superset строит графики по витринам. Вы сравните числа на готовом графике
|
||||
с результатом SQL и узнаете, какие отчеты сохраняются при перезапуске стенда.
|
||||
|
||||
### [Генератор: откуда берутся данные](generator.md)
|
||||
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
# Потоки Airflow и даги модельного времени
|
||||
# Генерация и обработка данных в Airflow
|
||||
|
||||
Airflow управляет генерацией данных и их обработкой в хранилище. Это разные
|
||||
этапы: сначала в Kafka поступают события и заказы за день работы магазина,
|
||||
затем по ним рассчитываются данные DDS и витрины DM.
|
||||
Даг описывает задачи Airflow и порядок их выполнения.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
world_initialize --> etl_pipeline
|
||||
world_live_day --> etl_pipeline
|
||||
manual[запуск рукой] --> etl_pipeline
|
||||
manual[ручной запуск] --> etl_pipeline
|
||||
etl_pipeline --> dds_transform --> dm_transform --> dq_check
|
||||
world_next_day --> sources[источники без ETL]
|
||||
```
|
||||
|
||||
## Два потока входят по-разному
|
||||
## Загрузка событий и заказов
|
||||
|
||||
События поступают в топик `hits` и обрабатываются по мере поступления:
|
||||
материализованные представления переносят строки из STG в ODS без участия
|
||||
@@ -24,7 +25,7 @@ Airflow.
|
||||
Даг `orders_ingest` загружает ее целиком. Такой способ приема называется
|
||||
[пакетным](../../CONTEXT.md).
|
||||
|
||||
## Даги мира
|
||||
## Генерация модельных дней
|
||||
|
||||
В документации миром называется учебный магазин с его посетителями,
|
||||
товарами и заказами. Время в нем идет отдельно от календарного.
|
||||
@@ -54,7 +55,7 @@ Airflow.
|
||||
номер дня остается прежним. Подробности — в [справочнике генератора
|
||||
и модельного времени](../architecture/world.md#даги-модельного-времени).
|
||||
|
||||
## Дневной конвейер
|
||||
## Обработка данных в хранилище
|
||||
|
||||
Даг `etl_pipeline` по очереди запускает `dds_transform`, `dm_transform`
|
||||
и `dq_check`, дожидаясь завершения каждого. Они рассчитывают данные DDS,
|
||||
@@ -84,20 +85,21 @@ Airflow.
|
||||
проверок описан в [разделе «Звено DQ
|
||||
конвейера»](../architecture/dm.md#звено-dq-конвейера).
|
||||
|
||||
## Как найти в интерфейсе
|
||||
## Поиск дагов в Airflow
|
||||
|
||||
Даги мира ищите по тегам «модельное время» и «жизненный цикл мира». Остальные
|
||||
даги ищите по тегам «заказы», `etl`, `dds`, `dm` и `dq`. `homework_check` с
|
||||
тегами `dm` и `labs` проверяет домашние работы; о нем рассказано в учебных
|
||||
заданиях.
|
||||
|
||||
## Посмотрите сами
|
||||
## Один день от генерации до витрин
|
||||
|
||||
Откройте Airflow по адресу из раздела [«Адреса и
|
||||
пароли»](../../README.md#адреса-и-пароли). В Admin → Variables найдите
|
||||
`world_position`. Затем запустите `world_next_day` на один день по [инструкции
|
||||
из README](../../README.md#добавить-данные-за-следующие-дни). Убедитесь, что
|
||||
`world_position` и запомните его значение. Затем запустите `world_next_day`
|
||||
на один день по [инструкции из README](../../README.md#добавить-данные-за-следующие-дни). Убедитесь, что
|
||||
`orders_ingest` завершился успешно, а в витринах нового дня еще нет.
|
||||
Значение `world_position` должно увеличиться на один.
|
||||
Запустите `etl_pipeline` вручную и дождитесь его завершения.
|
||||
|
||||
В DBeaver, подключенном по
|
||||
@@ -121,8 +123,8 @@ LIMIT 3;
|
||||
приходит слепок за день D−1. Выручка дня D появится после генерации
|
||||
дня D+1 и запуска конвейера.
|
||||
|
||||
## Куда дальше
|
||||
## Следующая глава и справочники
|
||||
|
||||
Детали — в справочниках [конвейера](../architecture/etl.md) и
|
||||
[мира](../architecture/world.md). Следующая глава — [«Мониторинг: куда
|
||||
смотреть»](monitoring.md).
|
||||
[мира](../architecture/world.md). Следующая глава —
|
||||
[«Мониторинг в Grafana»](monitoring.md).
|
||||
@@ -5,7 +5,7 @@
|
||||
магазина раз в модельные сутки выгружает состояние заказов в топик `orders`.
|
||||
Эта дневная выгрузка называется слепком.
|
||||
|
||||
## Что он производит
|
||||
## События сайта и заказы
|
||||
|
||||
Событие содержит сведения о действии посетителя: например, время,
|
||||
адрес страницы и идентификатор посетителя. Формат соответствует образцу
|
||||
@@ -20,11 +20,11 @@
|
||||
разобраны в [отдельном
|
||||
справочнике](../architecture/orders/snapshot.md).
|
||||
|
||||
## Почему данные всегда одни и те же
|
||||
## Почему начальные данные повторяются
|
||||
|
||||
Случайные решения генератора зависят от заданного числа —
|
||||
[зерна генерации](../architecture/generator/seeds.md) — и номера дня.
|
||||
При неизменных настройках и версии генератора одинаковые зерно и день
|
||||
При неизменных коде, каталоге товаров и настройках одинаковые зерно и день
|
||||
дают одинаковые события. Поэтому при создании стенда вы каждый раз
|
||||
получаете те же начальные данные. Повторный `make up` на работающем стенде
|
||||
сохраняет уже загруженные данные.
|
||||
@@ -34,10 +34,10 @@
|
||||
В документации этот файл называется паспортом мира. С ним можно сверить
|
||||
результат генерации и проверить, все ли события загрузились в ODS.
|
||||
|
||||
## Кто его запускает
|
||||
## Запуск генератора
|
||||
|
||||
На стенде генератором управляют [даги модельного
|
||||
времени](airflow-dags.md#даги-мира). Его можно запустить и напрямую. Команды
|
||||
времени](airflow-dags.md#генерация-модельных-дней). Его можно запустить и напрямую. Команды
|
||||
собраны в [справочнике генератора и модельного
|
||||
времени](../architecture/world.md#как-запустить-генератор).
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
В живом режиме события поступают постепенно, как в работающем магазине,
|
||||
но время идет ускоренно: модельные сутки занимают около 24 минут.
|
||||
|
||||
## Посмотрите сами
|
||||
## Сверка с паспортом мира
|
||||
|
||||
Подключитесь из DBeaver по [инструкции](../../README.md#подключиться-из-dbeaver)
|
||||
и выполните запрос:
|
||||
@@ -62,7 +62,7 @@ ORDER BY EventDate;
|
||||
Числа должны совпасть. Если вы уже генерировали следующие дни,
|
||||
в результате запроса будут дополнительные даты; в паспорте их нет.
|
||||
|
||||
## Куда дальше
|
||||
## Учебные задания и справочники
|
||||
|
||||
Подробнее генератор разобран в [README папки
|
||||
`generator/`](../../generator/README.md) и в [справочнике его
|
||||
|
||||
+18
-15
@@ -1,20 +1,19 @@
|
||||
# Мониторинг: куда смотреть
|
||||
# Мониторинг в Grafana
|
||||
|
||||
В Grafana настроены три дашборда. «Данные» показывает загрузку и обработку
|
||||
событий и заказов, «Кластер» — состояние узлов и самого ClickHouse,
|
||||
а «Запросы» — выполненные запросы и затраченные на них ресурсы.
|
||||
В Grafana настроены три дашборда — экрана с показателями и графиками.
|
||||
«Данные» показывает загрузку и обработку событий и заказов,
|
||||
«Кластер» — состояние узлов и самого ClickHouse, а «Запросы» — выполненные
|
||||
запросы и затраченные на них ресурсы.
|
||||
|
||||
Prometheus собирает метрики с двух узлов ClickHouse, ClickHouse Keeper
|
||||
и экспортера Kafka — службы, которая предоставляет метрики Kafka.
|
||||
Grafana получает данные из Prometheus и напрямую из ClickHouse.
|
||||
На панелях не заданы пороги для цветовых предупреждений: оценивать показания
|
||||
вы будете самостоятельно. Качество данных автоматически проверяет
|
||||
даг `dq_check`.
|
||||
|
||||
## Дашборд «Данные»
|
||||
|
||||
«Свежесть» показывает, сколько времени прошло с последнего события и с
|
||||
последнего заказа в ODS. Показатель для заказов растет весь модельный день
|
||||
«Свежесть» показывает, сколько реального времени прошло с поступления
|
||||
последних успешно разобранных событий и заказов. Время поступления хранится
|
||||
в колонке `_load_ts` слоя ODS. Показатель для заказов растет весь модельный день
|
||||
и снижается, когда поступает очередная дневная выгрузка — слепок.
|
||||
Это ожидаемо: заказы загружаются раз в день.
|
||||
|
||||
@@ -33,7 +32,11 @@ STG: по нему видно, поступила ли очередная выг
|
||||
Источники и правила чтения всех панелей разобраны в [справочнике дашборда
|
||||
«Данные»](../architecture/monitoring/data.md).
|
||||
|
||||
## Почему у панелей разные источники
|
||||
## Источники показателей
|
||||
|
||||
Grafana получает данные напрямую из ClickHouse и через Prometheus.
|
||||
Prometheus собирает метрики с двух узлов ClickHouse, ClickHouse Keeper
|
||||
и экспортера Kafka — службы, которая предоставляет метрики Kafka.
|
||||
|
||||
Свежесть считается по ODS: если новые сообщения перестали разбираться,
|
||||
показатель будет расти. Число поступивших сообщений берется из STG,
|
||||
@@ -45,7 +48,7 @@ STG: по нему видно, поступила ли очередная выг
|
||||
всех доставленных сообщений. Подробности — в [разделе о слоях
|
||||
панелей](../architecture/monitoring/data.md#почему-слои-у-панелей-разные).
|
||||
|
||||
## Два других дашборда
|
||||
## Состояние кластера и запросы
|
||||
|
||||
Открывайте «Кластер», чтобы проверить нагрузку на процессор, использование
|
||||
памяти и дисков, число частей таблиц и ход их слияний в ClickHouse. Здесь же
|
||||
@@ -58,11 +61,11 @@ STG: по нему видно, поступила ли очередная выг
|
||||
[справочник дашборда
|
||||
«Запросы»](../architecture/monitoring/queries.md).
|
||||
|
||||
## Посмотрите сами
|
||||
## Показатели за один модельный день
|
||||
|
||||
Откройте Grafana по адресу из раздела [«Адреса и
|
||||
пароли»](../../README.md#адреса-и-пароли), затем дашборд «Данные». Запустите в
|
||||
Airflow [`world_live_day`](airflow-dags.md#даги-мира): модельный день займет
|
||||
Airflow [`world_live_day`](airflow-dags.md#генерация-модельных-дней): модельный день займет
|
||||
около двадцати четырех минут.
|
||||
|
||||
Смотрите свежесть событий и свежесть заказов по отдельности. Пока день идет,
|
||||
@@ -73,11 +76,11 @@ Airflow [`world_live_day`](airflow-dags.md#даги-мира): модельны
|
||||
при генерации следующего дня. Даг пока продолжает работу, ожидая завершения
|
||||
обработки данных в дневном конвейере.
|
||||
|
||||
## Куда дальше
|
||||
## Следующая глава и справочники
|
||||
|
||||
Устройство мониторинга описано в [техническом
|
||||
справочнике](../architecture/monitoring/README.md), панели дашборда «Данные» —
|
||||
в [отдельном разделе](../architecture/monitoring/data.md).
|
||||
Почему выбраны именно эти показатели, объясняет
|
||||
[ADR 0002](../adr/0002-monitoring-scope.md). Следующая глава —
|
||||
[«Немного Superset»](superset.md).
|
||||
[«Отчеты в Superset»](superset.md).
|
||||
@@ -1,16 +1,17 @@
|
||||
# Хранилище и его слои
|
||||
|
||||
В хранилище приходят два потока из Kafka: в топике `hits` — события
|
||||
кликстрима, в `orders` — заказы магазина. Оба проходят один путь: STG → ODS →
|
||||
DDS → DM. В STG сообщения сохраняются как есть, в ODS разбираются по полям,
|
||||
в DDS из них собираются события, заказы и сессии, а в DM — витрины для отчетов.
|
||||
В Kafka поступают события сайта, или кликстрим, и заказы магазина.
|
||||
Для событий выделен топик `hits`, для заказов — `orders`.
|
||||
Из Kafka данные проходят четыре слоя хранилища. В STG сообщения сохраняются
|
||||
как есть, в ODS разбираются по полям, в DDS из них собираются события,
|
||||
заказы и сессии, а в DM — витрины для отчетов.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Kafka --> STG --> ODS --> DDS --> DM
|
||||
```
|
||||
|
||||
## Как читать имена
|
||||
## Имена таблиц и представлений
|
||||
|
||||
Для обычного чтения выбирайте представления с суффиксом `_v`. Они позволяют
|
||||
менять устройство таблиц, сохраняя привычные запросы к данным.
|
||||
@@ -20,7 +21,7 @@ flowchart LR
|
||||
Подробный разбор имен есть в [справочнике
|
||||
хранилища](../architecture/storage.md#имена-объектов).
|
||||
|
||||
## STG: как поступило
|
||||
## STG: исходные сообщения
|
||||
|
||||
STG — сырой слой. Он сохраняет сообщение целой строкой, без разбора. Вместе
|
||||
с ним записываются сведения о доставке: топик, партиция, смещение и время
|
||||
@@ -31,7 +32,7 @@ STG — сырой слой. Он сохраняет сообщение цело
|
||||
удаляются по правилу из [справочника
|
||||
хранилища](../architecture/storage.md#срок-жизни-сырых-данных).
|
||||
|
||||
## ODS: язык источника
|
||||
## ODS: разбор сообщений
|
||||
|
||||
В ODS содержимое сообщения разбирается на колонки с нужными типами данных:
|
||||
датами, числами, строками. Колонки сохраняют имена из источника. Например,
|
||||
@@ -41,7 +42,7 @@ STG — сырой слой. Он сохраняет сообщение цело
|
||||
`*_errors` вместе с исходной строкой и классом ошибки. Правильно записанные
|
||||
сообщения обрабатываются дальше.
|
||||
|
||||
## DDS: язык бизнеса
|
||||
## DDS: события, заказы и сессии
|
||||
|
||||
DDS — детальный слой: в нем данные подготовлены для анализа событий,
|
||||
заказов и сессий. Здесь же хранится карта идентичностей — таблица связей
|
||||
@@ -51,6 +52,8 @@ DDS — детальный слой: в нем данные подготовле
|
||||
|
||||
В стенде визитом, или сессией, считают подряд идущие события одного
|
||||
`ClientID` без пауз дольше 30 минут. Визит не пересекает границу модельных суток.
|
||||
Модельные сутки — день в календаре учебного магазина. Этот календарь
|
||||
не зависит от текущей даты на вашем компьютере.
|
||||
|
||||
Источник передает готовый идентификатор визита `VisitID` по образцу
|
||||
Яндекс Метрики. В учебном хранилище сессии рассчитываются заново по времени
|
||||
@@ -61,7 +64,7 @@ DDS — детальный слой: в нем данные подготовле
|
||||
Готовые сессии читайте через `dds.session_v`. События для самостоятельного
|
||||
расчета доступны в `dds.event_v`: в этом представлении `VisitID` скрыт.
|
||||
|
||||
## DM: данные для дашбордов
|
||||
## DM: витрины для отчетов
|
||||
|
||||
DM — слой витрин. Витрина — готовый набор показателей для отчета или
|
||||
дашборда. Здесь уже посчитаны, например, дневная выручка по категориям и
|
||||
@@ -71,7 +74,7 @@ DM — слой витрин. Витрина — готовый набор по
|
||||
путь расчета. Состав готовых витрин смотрите в [карте
|
||||
таблиц](../architecture/storage.md#карта-таблиц).
|
||||
|
||||
## Справочные данные: зона `dic`
|
||||
## Справочники в базе `dic`
|
||||
|
||||
В базе `dic` хранятся готовые справочные данные. Они загружаются отдельно
|
||||
от событий и заказов и могут использоваться в расчетах любого слоя.
|
||||
@@ -81,7 +84,7 @@ DM — слой витрин. Витрина — готовый набор по
|
||||
устроена зона, смотрите в [разделе о
|
||||
справочниках](../architecture/storage.md#справочники).
|
||||
|
||||
## Сквозные ключи
|
||||
## Связь данных между слоями
|
||||
|
||||
Идентификаторы позволяют связать данные из разных слоев. При переходе в DDS
|
||||
часть колонок переименовывается: например, `WatchID` становится `event_id`.
|
||||
@@ -99,7 +102,7 @@ DM — слой витрин. Витрина — готовый набор по
|
||||
[словарь понятий](../../CONTEXT.md#язык). Точные имена объектов и полей сверяйте с
|
||||
[картой таблиц](../architecture/storage.md#карта-таблиц).
|
||||
|
||||
## Посмотрите сами
|
||||
## Примеры запросов
|
||||
|
||||
Подключитесь к ClickHouse по инструкции из разделов [«Адреса и
|
||||
пароли»](../../README.md#адреса-и-пароли) и [«Подключиться из
|
||||
@@ -122,6 +125,8 @@ ORDER BY UTCEventTime
|
||||
LIMIT 20;
|
||||
```
|
||||
|
||||
Каждая строка здесь — отдельное действие посетителя.
|
||||
|
||||
Второй запрос покажет 20 сессий того же дня с наибольшим числом событий.
|
||||
Сессии рассчитаны по всем событиям дня:
|
||||
|
||||
@@ -140,7 +145,12 @@ ORDER BY events_total DESC
|
||||
LIMIT 20;
|
||||
```
|
||||
|
||||
Третий запрос покажет готовую выручку по категориям за тот же день:
|
||||
Теперь одна строка представляет целый визит. `events_total` показывает,
|
||||
сколько событий в него вошло; начало и конец визита записаны в
|
||||
`started_at` и `finished_at`.
|
||||
|
||||
Третий запрос покажет выручку магазина по категориям за тот же день.
|
||||
Эта витрина рассчитана по заказам:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
@@ -153,13 +163,13 @@ WHERE report_date = '2026-06-01'
|
||||
ORDER BY revenue DESC;
|
||||
```
|
||||
|
||||
Здесь одна строка содержит итоги дня для категории товаров.
|
||||
Все три запроса читают представления `_v`.
|
||||
|
||||
## Куда дальше
|
||||
## Следующая глава и справочники
|
||||
|
||||
Откройте [интерактивную карту](architecture-map.html) из своей копии
|
||||
репозитория и проследите путь данных.
|
||||
Список объектов, сроки хранения и устройство таблиц — в [справочнике
|
||||
хранилища](../architecture/storage.md). Как данные обрабатываются между слоями — в
|
||||
следующей главе: [потоки Airflow и даги модельного
|
||||
времени](airflow-dags.md).
|
||||
следующей главе: [«Генерация и обработка данных в Airflow»](airflow-dags.md).
|
||||
@@ -1,4 +1,4 @@
|
||||
# Немного Superset
|
||||
# Отчеты в Superset
|
||||
|
||||
Superset показывает витрины слоя DM в виде графиков. К ClickHouse он
|
||||
подключается пользователем `bi`. Дашборд стенда «Бизнес-сюжеты» собран из трех
|
||||
@@ -24,7 +24,7 @@ Superset показывает витрины слоя DM в виде графи
|
||||
Затем на основе датасета создается график: выбираются показатели и способ
|
||||
их отображения. Несколько графиков можно разместить на одном дашборде.
|
||||
|
||||
## Стендовые объекты и ваши
|
||||
## Готовые и собственные отчеты
|
||||
|
||||
Готовые датасеты, графики и дашборд сохранены в файлах репозитория
|
||||
и импортируются при каждом `make up`. Если изменить их через интерфейс,
|
||||
@@ -43,13 +43,14 @@ Superset](../architecture/superset.md#импорт-при-каждой-иниц
|
||||
Последняя дата с выручкой на день раньше последней даты с событиями
|
||||
кликстрима. Выгрузка заказов за день D поступает при генерации следующего
|
||||
дня D+1, а затем обрабатывается конвейером. Вы уже видели эту разницу
|
||||
в [главе об Airflow](airflow-dags.md#посмотрите-сами). Начальное состояние
|
||||
в [главе об Airflow](airflow-dags.md#один-день-от-генерации-до-витрин). Начальное состояние
|
||||
показано в разделе [«Стартовый мир»](../architecture/world.md#стартовый-мир).
|
||||
|
||||
## Посмотрите сами
|
||||
## Сверка графика с витриной
|
||||
|
||||
Откройте Superset по адресу из раздела [«Адреса и
|
||||
пароли»](../../README.md#адреса-и-пароли) и выберите дашборд «Бизнес-сюжеты».
|
||||
Найдите график «Посетители и известные пользователи».
|
||||
Затем откройте SQL Lab и выполните запрос:
|
||||
|
||||
```sql
|
||||
@@ -59,9 +60,14 @@ ORDER BY report_date DESC
|
||||
LIMIT 7;
|
||||
```
|
||||
|
||||
Как построить свой отчет, рассказывают [учебные задания](../labs/README.md).
|
||||
Выберите одну дату из результата. На дашборде задайте «Период», в который
|
||||
она входит, и обновите график. Наведите указатель на точку этой даты:
|
||||
числа посетителей и известных пользователей должны совпасть с `visitors`
|
||||
и `known_users` из SQL.
|
||||
|
||||
## Куда дальше
|
||||
Как построить собственный отчет, рассказывают [учебные задания](../labs/README.md).
|
||||
|
||||
## Следующая глава и справочники
|
||||
|
||||
Подключение и настройка Superset описаны в [справочнике
|
||||
Superset](../architecture/superset.md), а состав витрин — в [справочнике
|
||||
|
||||
@@ -1,37 +1,38 @@
|
||||
# Задание 1: `dm.events_enriched_v`
|
||||
# Задание 1: события для анализа, `dm.events_enriched_v`
|
||||
|
||||
**Чему учит:** подготовить удобный для анализа набор полей события
|
||||
и обойтись представлением, сохранив одну строку на событие.
|
||||
Вы подготовите представление с полями для анализа событий. Оно будет
|
||||
добавлять вычисляемые поля при чтении, сохраняя одну строку на событие.
|
||||
|
||||
## Сначала прочитать
|
||||
## Подготовка
|
||||
|
||||
- [«Хранилище и его слои»](../handbook/storage-layers.md).
|
||||
- [Как применить свой DDL](README.md#как-применить-свой-ddl): файл для решения
|
||||
- [Выполнение SQL в DBeaver](README.md#выполнение-sql-в-dbeaver): файл для решения
|
||||
и подключение с правами на создание витрины.
|
||||
|
||||
## Для чего нужна витрина
|
||||
## Данные для анализа событий
|
||||
|
||||
В `dds.event_v` 44 колонки, включая технические поля источника и массивы
|
||||
товаров. Для анализа событий в Superset нужны деловые поля и три готовых
|
||||
значения: час события, путь страницы и домен перехода. Тогда вам не придется
|
||||
товаров. Для анализа событий в Superset нужны сведения о посетителе и его
|
||||
действиях, а также три вычисляемых поля: час события, путь страницы и домен,
|
||||
с которого пришел посетитель. Тогда вам не придется
|
||||
повторять эти выражения в каждом запросе.
|
||||
|
||||
Витрины следующих заданий читают DDS напрямую. Использовать
|
||||
`dm.events_enriched_v` как источник для них не нужно: задания независимы, их
|
||||
можно выполнять в любом порядке.
|
||||
|
||||
## Что сделать
|
||||
## Создание представления
|
||||
|
||||
Создайте обычное представление `CREATE VIEW dm.events_enriched_v`.
|
||||
Единственный источник — `dds.event_v`. Зерно — **событие**: каждой строке
|
||||
источника соответствует одна строка витрины.
|
||||
Единственный источник: `dds.event_v`. Каждой строке источника должна
|
||||
соответствовать ровно одна строка витрины: одно событие.
|
||||
|
||||
Физическая таблица, таблица-донор и задача в `dm_transform` не нужны.
|
||||
Представление не хранит строки, поэтому ключ шардирования и пересборка ему
|
||||
не требуются.
|
||||
Представление выполняет запрос при чтении и не хранит отдельную копию данных.
|
||||
Для него не нужно создавать таблицы или добавлять задачу в `dm_transform`.
|
||||
|
||||
Укажите колонки явно. Иначе новая колонка источника попадет в витрину
|
||||
автоматически, и договор с потребителем изменится без вашего участия.
|
||||
автоматически. Отчет получит ее, даже если вы не собирались менять его набор
|
||||
полей.
|
||||
|
||||
| Группа | Колонки |
|
||||
|---|---|
|
||||
@@ -57,9 +58,7 @@ DDS читают из ODS без отдельной загрузки. Эти с
|
||||
## Типичная ошибка
|
||||
|
||||
Не разворачивайте массивы товаров через `ARRAY JOIN`. У события с тремя
|
||||
позициями получится три строки. Зерно изменится. Витрину с новым зерном на
|
||||
стенде хранят готовым агрегатом — причины описаны в разделе [«Как принято на
|
||||
ступени 1»](README.md#как-принято-на-ступени-1).
|
||||
позициями получится три строки вместо одной.
|
||||
|
||||
Главная опасность — потеря событий с пустым массивом товаров: `ARRAY JOIN`
|
||||
исключает их из результата. Товары есть только в событиях корзины и покупки.
|
||||
@@ -68,9 +67,9 @@ DDS читают из ODS без отдельной загрузки. Эти с
|
||||
по-прежнему будет выглядеть таблицей событий.
|
||||
|
||||
Сохраните массивы как в источнике. Для анализа отдельных товарных позиций
|
||||
нужна другая витрина с другим зерном и именем.
|
||||
нужна отдельная витрина, где одна строка соответствует товарной позиции.
|
||||
|
||||
## Как проверить себя
|
||||
## Проверка результата
|
||||
|
||||
После применения DDL откройте Airflow по [адресу из README](../../README.md#адреса-и-пароли),
|
||||
найдите даг `homework_check` и запустите его вручную. В новом запуске откройте
|
||||
@@ -79,16 +78,15 @@ DDS читают из ODS без отдельной загрузки. Эти с
|
||||
расхождениях с источником — дни и примеры несовпадений. Задачи еще не
|
||||
созданных витрин будут красными; это ожидаемо и не мешает проверке первой.
|
||||
|
||||
Деловой ключ — `event_id` внутри дня. Получите результат прямо из
|
||||
`dds.event_v` за тот же день. Соедините его с витриной полным внешним
|
||||
соединением по `event_id`. Посчитайте строки, где ключ отсутствует с одной
|
||||
из сторон или различаются значения деловых колонок. Расхождений должно быть
|
||||
ноль.
|
||||
Выберите день и прочитайте его события из `dds.event_v` и своей витрины.
|
||||
Сопоставьте строки по `event_id` через `FULL OUTER JOIN`.
|
||||
Найдите события, которые есть только с одной стороны, и события, у которых
|
||||
различаются перенесенные из источника поля. Расхождений должно быть ноль.
|
||||
|
||||
Отдельно сверьте `event_hour`, `page_path` и `referer_host` с теми же
|
||||
выражениями, рассчитанными прямо по `dds.event_v`.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] у вас есть `dm.events_enriched_v` с явно перечисленными колонками;
|
||||
- [ ] вы сверили число строк и набор `event_id` с `dds.event_v` за каждый
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
# Задание 2: `dm.top_pages_daily`
|
||||
# Задание 2: показатели страниц по дням, `dm.top_pages_daily`
|
||||
|
||||
**Чему учит:** собрать дневные показатели страниц и настроить их
|
||||
обновление в `dm_transform` через замену партиций.
|
||||
В этой работе вы рассчитаете показатели страниц за каждый день и сохраните
|
||||
их в таблице. Затем подключите обновление витрины к `dm_transform`:
|
||||
новый расчет будет заменять дневные партиции таблицы.
|
||||
|
||||
## Сначала прочитать
|
||||
## Подготовка
|
||||
|
||||
- [«Хранилище и его слои»](../handbook/storage-layers.md);
|
||||
- [«Потоки Airflow и даги модельного времени»](../handbook/airflow-dags.md).
|
||||
- [«Генерация и обработка данных в Airflow»](../handbook/airflow-dags.md).
|
||||
|
||||
## Для чего нужна витрина
|
||||
## Дневные показатели страниц
|
||||
|
||||
По дневным показателям страниц аналитик сравнивает просмотры, посетителей и
|
||||
действия с товарами. Добавление в корзину происходит на карточке товара,
|
||||
@@ -19,19 +20,23 @@
|
||||
действия происходят на разных страницах, а связи между ними в агрегате нет.
|
||||
Путь человека исследуют по событиям или сессиям.
|
||||
|
||||
## Что сделать
|
||||
## Создание витрины
|
||||
|
||||
Источник — `dds.event_v`. Зерно витрины — **день события × страница**.
|
||||
Читайте DDS напрямую: так выполнение задания не зависит от витрины
|
||||
задания 1.
|
||||
Читайте события из `dds.event_v`. В результате нужна одна строка на страницу
|
||||
за день. Например, все просмотры одной карточки товара за один день попадут
|
||||
в одну строку. Витрина задания 1 для этого расчета не нужна.
|
||||
|
||||
На стенде витрину с новым зерном хранят как готовый агрегат, чтобы не
|
||||
рассчитывать ее заново для каждого запроса. Причины описаны в разделе
|
||||
[«Как принято на ступени 1»](README.md#как-принято-на-ступени-1). Создайте
|
||||
пять объектов:
|
||||
`dm.top_pages_daily_rep` / `_dist`, донор `_stage_rep` / `_stage_dist` и
|
||||
публичное представление `dm.top_pages_daily_v`. Ключ партиции — день, он же
|
||||
единица пересборки.
|
||||
Сохраните рассчитанные показатели в таблице, чтобы отчеты читали готовые
|
||||
итоги. Создайте пять объектов:
|
||||
|
||||
- основные таблицы `dm.top_pages_daily_rep` и `dm.top_pages_daily_dist`;
|
||||
- промежуточные таблицы `dm.top_pages_daily_stage_rep` и `dm.top_pages_daily_stage_dist`;
|
||||
- представление для чтения `dm.top_pages_daily_v`.
|
||||
|
||||
Их назначение и порядок обновления разобраны во вводной:
|
||||
[«Создание и обновление витрин»](README.md#создание-и-обновление-витрин).
|
||||
Разбейте данные на партиции по дням, чтобы можно было заменить результат
|
||||
одного дня целиком.
|
||||
|
||||
### Состав колонок
|
||||
|
||||
@@ -50,25 +55,26 @@
|
||||
адрес разделил бы показатели одной страницы на сотни строк. Учитывайте все
|
||||
три типа событий, а их счетчики вычисляйте через `countIf`.
|
||||
|
||||
### Ритм пересборки
|
||||
### Дни для пересчета
|
||||
|
||||
События прожитого дня больше не меняются. Определяйте объем прогона по
|
||||
состоянию витрины: от первого несобранного прожитого дня до последнего
|
||||
прожитого. Отсчет от последнего собранного дня оставит пропуски после
|
||||
частичного сбоя.
|
||||
События завершившегося модельного дня больше не меняются. Найдите первый
|
||||
такой день, которого еще нет в витрине, и рассчитайте все дни от него до
|
||||
последнего завершившегося. Если начать после последнего дня, который уже
|
||||
есть в витрине, можно пропустить более ранний день, не загруженный из-за сбоя.
|
||||
|
||||
Как у `revenue_daily`, заполните донор за весь диапазон одной вставкой,
|
||||
затем замените дневные партиции по одной.
|
||||
Как у `revenue_daily`, запишите расчет за все выбранные дни в промежуточную
|
||||
таблицу одной вставкой. Затем замените дневные партиции основной таблицы по
|
||||
одной. Добавьте запуск этих запросов в `dm_transform`.
|
||||
|
||||
### Ключ шардирования
|
||||
|
||||
Выберите ключ самостоятельно. У `revenue_daily` кроме дня одно измерение с
|
||||
шестью близкими по объему значениями. Здесь страниц много, и число событий
|
||||
на них сильно различается.
|
||||
Выберите ключ самостоятельно. У `revenue_daily` строки сгруппированы по дню
|
||||
и категории товара; категорий всего шесть. Здесь страниц много, и число
|
||||
событий на них сильно различается.
|
||||
|
||||
У цели и донора используйте одинаковый ключ: иначе при замене партиции
|
||||
строка потеряется на одном шарде и удвоится на другом. О том, [как устроен
|
||||
донор](../architecture/storage.md), читайте в разделе «Раскладка по шардам».
|
||||
У основной и промежуточной таблиц используйте одинаковый ключ шардирования.
|
||||
Замена партиции не перераспределяет строки между шардами. Подробнее об этом
|
||||
в разделе [«Раскладка по шардам»](../architecture/storage.md#раскладка-по-шардам).
|
||||
|
||||
## Типичная ошибка
|
||||
|
||||
@@ -82,13 +88,13 @@
|
||||
Выбирайте проверку под возможную ошибку. Этот подход описан в
|
||||
[справочнике проверок](../architecture/testing.md).
|
||||
|
||||
## Как проверить себя
|
||||
## Проверка результата
|
||||
|
||||
Деловой ключ — `page_path` внутри дня. Получите прямой агрегат из
|
||||
`dds.event_v` за день и соедините его с `dm.top_pages_daily_v` полным
|
||||
внешним соединением по ключу. Посчитайте строки, где ключ отсутствует с
|
||||
одной из сторон или различается любой из четырех счетчиков. Расхождений
|
||||
должно быть ноль.
|
||||
Рассчитайте те же показатели по `dds.event_v` за выбранный день.
|
||||
Сопоставьте результат со строками `dm.top_pages_daily_v` за этот день
|
||||
через `FULL OUTER JOIN` по `page_path`. Найдите страницы, которые есть
|
||||
только с одной стороны, и строки, где различается хотя бы один из четырех
|
||||
счетчиков. Расхождений должно быть ноль.
|
||||
|
||||
Перед полным сравнением проверьте дневные суммы:
|
||||
|
||||
@@ -98,10 +104,10 @@
|
||||
Сумма `visitors` по страницам повторно учитывает посетителя, открывшего
|
||||
несколько страниц. Число посетителей за день берите из `dm.daily_traffic_v`.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] у вас есть пять объектов витрины; донор объявлен через `AS` и
|
||||
использует тот же ключ шардирования, что и целевая таблица;
|
||||
- [ ] у вас есть пять объектов витрины; промежуточная таблица объявлена через
|
||||
`AS` и использует тот же ключ шардирования, что и основная;
|
||||
- [ ] в `dm.top_pages_daily_v` явно перечислены колонки, включая
|
||||
`_load_id` и `_load_ts`;
|
||||
- [ ] ваш запрос сам определяет дни пересборки по состоянию витрины;
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
# Задание 3: `dm.session_overview`
|
||||
# Задание 3: визиты по устройствам, `dm.session_overview`
|
||||
|
||||
**Чему учит:** хранить слагаемые для расчета средних и долей и понимать,
|
||||
Вы соберете дневные показатели визитов по устройствам. В таблице будут
|
||||
храниться суммы и счетчики для расчета средних и долей. Заодно проверите,
|
||||
когда сложение числа посетителей по устройствам завышает аудиторию.
|
||||
|
||||
## Сначала прочитать
|
||||
## Подготовка
|
||||
|
||||
- [«Хранилище и его слои»](../handbook/storage-layers.md);
|
||||
- [«Потоки Airflow и даги модельного времени»](../handbook/airflow-dags.md).
|
||||
- [«Генерация и обработка данных в Airflow»](../handbook/airflow-dags.md).
|
||||
|
||||
## Для чего нужна витрина
|
||||
## Показатели визитов
|
||||
|
||||
Витрина нужна, чтобы по дням сравнивать визиты с телефона и компьютера: их
|
||||
число, длительность, просмотры страниц и частоту покупок.
|
||||
@@ -17,13 +18,19 @@
|
||||
устройствам. Почему такие итоги хранят в отдельной таблице, объясняет
|
||||
[справочник DM](../architecture/dm.md).
|
||||
|
||||
## Что сделать
|
||||
## Создание витрины
|
||||
|
||||
Источник — `dds.session_v`. Зерно — **день сессии × категория устройства**.
|
||||
Зерно новое, поэтому витрина хранится готовым агрегатом — см. [«Как принято
|
||||
на ступени 1»](README.md#как-принято-на-ступени-1). Создайте пять объектов:
|
||||
`dm.session_overview_rep` / `_dist`, донор `_stage_rep` / `_stage_dist` и
|
||||
публичное представление `dm.session_overview_v`. Ключ партиции — день.
|
||||
Читайте готовые сессии из `dds.session_v`. В результате нужна одна строка
|
||||
на категорию устройства за день: например, итоги всех визитов с телефонов
|
||||
за один день.
|
||||
|
||||
Создайте пять объектов по [общим правилам ступени 1](README.md#создание-и-обновление-витрин):
|
||||
|
||||
- основные таблицы `dm.session_overview_rep` и `dm.session_overview_dist`;
|
||||
- промежуточные таблицы `dm.session_overview_stage_rep` и `dm.session_overview_stage_dist`;
|
||||
- представление для чтения `dm.session_overview_v`.
|
||||
|
||||
Разбейте данные на партиции по дням.
|
||||
|
||||
### Состав колонок
|
||||
|
||||
@@ -47,19 +54,20 @@
|
||||
категорий встречаются `desktop`, `mobile` и `tablet`. Строку для `tv`
|
||||
создавайте только при появлении таких визитов.
|
||||
|
||||
Вместо `avg_duration_seconds` храните слагаемые. Среднее из средних по трем
|
||||
устройствам даст неверный ответ за день: число сессий в категориях
|
||||
различается, поэтому средние нужно взвешивать. Потребитель делит сумму
|
||||
`duration_seconds_total` на сумму `sessions`. Долю отказов считают так же:
|
||||
сумму `bounce_sessions` делят на сумму `sessions`, а не усредняют готовые
|
||||
доли.
|
||||
Для расчета средней длительности храните общую длительность и число сессий.
|
||||
Готовую среднюю `avg_duration_seconds` сохранять не нужно. Число сессий
|
||||
в категориях различается, поэтому среднее из трех средних даст неверный
|
||||
ответ за день. Чтобы получить его правильно, разделите сумму
|
||||
`duration_seconds_total` на сумму `sessions`. Для доли отказов разделите
|
||||
сумму `bounce_sessions` на сумму `sessions`. Усреднение готовых долей тоже
|
||||
даст неверный результат.
|
||||
|
||||
### Ритм пересборки
|
||||
### Дни для пересчета
|
||||
|
||||
Сессии прожитого дня больше не меняются. Как в задании 2, определяйте
|
||||
диапазон от первого несобранного прожитого дня до последнего прожитого.
|
||||
Соберите его в донор и замените дневные партиции. Добавьте задачу в
|
||||
`dm_transform`.
|
||||
Сессии завершившегося модельного дня больше не меняются. Как в задании 2,
|
||||
найдите первый завершившийся день, которого еще нет в витрине. Рассчитайте
|
||||
все дни от него до последнего завершившегося, запишите их в промежуточную
|
||||
таблицу и замените дневные партиции основной. Добавьте задачу в `dm_transform`.
|
||||
|
||||
### Ключ шардирования
|
||||
|
||||
@@ -68,18 +76,18 @@
|
||||
`device_category`, сначала посчитайте строки на каждом шарде. Затем
|
||||
объясните, почему такое распределение вас устраивает.
|
||||
|
||||
Ключ донора — тот же, что у цели (см. [«Как принято на ступени
|
||||
1»](README.md#как-принято-на-ступени-1) и раздел «Раскладка по шардам»
|
||||
[справочника хранилища](../architecture/storage.md)).
|
||||
У основной и промежуточной таблиц должен быть одинаковый ключ шардирования:
|
||||
см. [общие правила ступени 1](README.md#создание-и-обновление-витрин).
|
||||
|
||||
## Почему сумма `visitors` сходится с `dm.daily_traffic_v`
|
||||
## Число посетителей за день
|
||||
|
||||
Сравните сумму `visitors` по устройствам за день с `dm.daily_traffic_v`. На
|
||||
стенде значения совпадут: устройство приписано куке на всю жизнь, поэтому за
|
||||
день она учитывается только в одной строке. Это свойство мира стенда; см.
|
||||
стенде значения совпадут: одному `client_id` всегда соответствует одно
|
||||
устройство, поэтому за день он учитывается только в одной категории.
|
||||
Это свойство мира стенда; см.
|
||||
раздел «Паспорт куки» в [словаре понятий](../../CONTEXT.md).
|
||||
|
||||
Если появятся визиты одной куки с двух устройств, сумма начнет завышать
|
||||
Если появятся визиты одного `client_id` с двух устройств, сумма начнет завышать
|
||||
аудиторию. Проверки счетчиков по категориям этого не обнаружат. Число
|
||||
посетителей дня берите из `dm.daily_traffic_v`, где оно рассчитано для дня
|
||||
целиком.
|
||||
@@ -91,12 +99,13 @@
|
||||
событий совпало с источником, а число сессий завышено, вероятная причина —
|
||||
сборка витрины из событий вместо сессий.
|
||||
|
||||
## Как проверить себя
|
||||
## Проверка результата
|
||||
|
||||
Деловой ключ — `device_category` внутри дня. Получите прямой агрегат из
|
||||
`dds.session_v` за день. Соедините его с `dm.session_overview_v` полным
|
||||
внешним соединением по ключу. Посчитайте строки, где ключ отсутствует с
|
||||
одной из сторон или различается любой счетчик. Расхождений должно быть ноль.
|
||||
Рассчитайте те же показатели по `dds.session_v` за выбранный день.
|
||||
Сопоставьте результат со строками `dm.session_overview_v` за этот день
|
||||
через `FULL OUTER JOIN` по `device_category`. Найдите категории, которые
|
||||
есть только с одной стороны, и строки, где различается хотя бы один
|
||||
счетчик. Расхождений должно быть ноль.
|
||||
|
||||
Еще две проверки дневных итогов:
|
||||
|
||||
@@ -104,12 +113,12 @@
|
||||
повторного учета визитов;
|
||||
- сумма `events_total` равна сумме `events_total` по сессиям того же дня.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] у вас есть пять объектов витрины; донор объявлен через `AS` и
|
||||
использует ключ шардирования целевой таблицы;
|
||||
- [ ] у вас есть пять объектов витрины; промежуточная таблица объявлена через
|
||||
`AS` и использует ключ шардирования основной;
|
||||
- [ ] вы явно перечислили колонки в `dm.session_overview_v` и собрали
|
||||
показатели из готовых сессий `dds.session_v`;
|
||||
- [ ] в таблице хранятся слагаемые для средних и долей;
|
||||
- [ ] в таблице хранятся суммы и счетчики для расчета средних и долей;
|
||||
- [ ] проверка `session_overview` в `homework_check` прошла успешно;
|
||||
- [ ] витрина обновляется при запуске `dm_transform`.
|
||||
@@ -1,28 +1,42 @@
|
||||
# Задание 4: `dm.dq_errors_daily`
|
||||
# Задание 4: ошибки загрузки по дням, `dm.dq_errors_daily`
|
||||
|
||||
**Чему учит:** считать брак по дню загрузки и сохранять сводку дольше
|
||||
исходных строк.
|
||||
Вы соберете дневную сводку ошибок загрузки. Ее итоги должны сохраниться
|
||||
и после того, как исходные сообщения будут удалены из хранилища.
|
||||
|
||||
## Сначала прочитать
|
||||
## Подготовка
|
||||
|
||||
- [«Хранилище и его слои»](../handbook/storage-layers.md);
|
||||
- [«Потоки Airflow и даги модельного времени»](../handbook/airflow-dags.md).
|
||||
- [«Генерация и обработка данных в Airflow»](../handbook/airflow-dags.md).
|
||||
|
||||
## Для чего нужна витрина
|
||||
## Ошибки загрузки и их источники
|
||||
|
||||
Сводку открывают при росте брака в потоке: смотрят, каких ошибок стало
|
||||
больше и сколько строк забраковано. Причину конкретной ошибки исследуют
|
||||
в `ods.event_errors_dist` и `ods.order_errors_dist`, где есть сырой текст и
|
||||
координаты доставки.
|
||||
Если сообщение не удалось разобрать, стенд записывает его в таблицу ошибок.
|
||||
Такие строки дальше называются браком. Вы соберете сводку, по которой можно
|
||||
увидеть, каких ошибок стало больше и сколько строк забраковано.
|
||||
|
||||
## Что сделать
|
||||
Источники сводки:
|
||||
|
||||
Зерно — **день загрузки × источник × класс брака**. В источнике зерно —
|
||||
строка брака. Зерно новое, поэтому витрина хранится готовым агрегатом — см.
|
||||
[«Как принято на ступени 1»](README.md#как-принято-на-ступени-1). Создайте
|
||||
пять объектов: `dm.dq_errors_daily_rep` / `_dist`, донор `_stage_rep` /
|
||||
`_stage_dist` и публичное представление `dm.dq_errors_daily_v`. Ключ
|
||||
партиции — день загрузки.
|
||||
- `ods.event_errors_dist`: ошибки разбора событий;
|
||||
- `ods.order_errors_dist`: ошибки разбора выгрузки заказов.
|
||||
|
||||
В этих таблицах есть исходный текст сообщения и сведения о его доставке:
|
||||
они пригодятся при поиске причины ошибки. Представлений `_v` у таблиц ошибок
|
||||
нет, создавать их для задания не нужно. Чтение локальной таблицы `_rep`
|
||||
даст строки только одного шарда из двух, поэтому используйте `_dist`.
|
||||
|
||||
## Создание сводки
|
||||
|
||||
В результате нужна одна строка на день загрузки, источник и класс ошибки.
|
||||
Например, ошибки `field_invalid` в заказах за один день загрузки попадут
|
||||
в одну строку с их количеством.
|
||||
|
||||
Создайте пять объектов по [общим правилам ступени 1](README.md#создание-и-обновление-витрин):
|
||||
|
||||
- основные таблицы `dm.dq_errors_daily_rep` и `dm.dq_errors_daily_dist`;
|
||||
- промежуточные таблицы `dm.dq_errors_daily_stage_rep` и `dm.dq_errors_daily_stage_dist`;
|
||||
- представление для чтения `dm.dq_errors_daily_v`.
|
||||
|
||||
Разбейте данные на партиции по дням загрузки.
|
||||
|
||||
### Состав колонок
|
||||
|
||||
@@ -41,16 +55,17 @@
|
||||
Служебная `_load_ts` витрины описывает сборку сводки; ее дата может
|
||||
отличаться от `load_date`.
|
||||
|
||||
У событий и заказов свои классы брака. `source` входит в зерно, чтобы
|
||||
одинаковые имена классов с разным смыслом не объединялись в одну строку.
|
||||
У событий и заказов свои классы брака. Группируйте строки также по `source`,
|
||||
чтобы одноименные классы разных источников не объединялись.
|
||||
|
||||
### Ритм пересборки
|
||||
### Пересчет и хранение итогов
|
||||
|
||||
Брак хранится месяц по `TTL`, сырые данные — трое суток. Сводка хранится
|
||||
бессрочно: итоги нужны и после удаления исходных строк.
|
||||
Правило `TTL` удаляет строки брака через месяц, сырые данные через трое суток.
|
||||
Сводку храните бессрочно: итоги нужны и после удаления исходных строк.
|
||||
|
||||
Получите список дней загрузки, еще имеющихся в источниках. Соберите их в
|
||||
донор и замените только эти партиции, сохранив остальные.
|
||||
Получите список дней загрузки, еще имеющихся в источниках. Рассчитайте их
|
||||
итоги в промежуточной таблице и замените только эти партиции основной таблицы.
|
||||
Более старые итоги сохраните.
|
||||
|
||||
В течение текущих суток ошибки еще поступают: пересчитайте этот день на
|
||||
следующем прогоне.
|
||||
@@ -60,36 +75,23 @@
|
||||
Выберите ключ самостоятельно. Классов брака у событий и заказов вместе
|
||||
меньше десятка. Объясните, насколько существенен перекос при таком объеме.
|
||||
|
||||
Ключ донора — тот же, что у цели (см. [«Как принято на ступени
|
||||
1»](README.md#как-принято-на-ступени-1) и раздел «Раскладка по шардам»
|
||||
[справочника хранилища](../architecture/storage.md)).
|
||||
|
||||
## Источники: таблицы `_dist`
|
||||
|
||||
Только это задание читает данные не через `_v`: брак нужен для диагностики
|
||||
приема и не входит в бизнес-модель. Публичных представлений у таблиц ошибок
|
||||
нет, создавать их для задания не нужно. Источники:
|
||||
|
||||
- `ods.event_errors_dist` — ошибки разбора событий;
|
||||
- `ods.order_errors_dist` — ошибки разбора слепка заказов.
|
||||
|
||||
Чтение локальной `_rep` даст строки одного шарда из двух: часть брака не
|
||||
попадет в сводку, хотя запрос выполнится без ошибки.
|
||||
У основной и промежуточной таблиц должен быть одинаковый ключ шардирования:
|
||||
см. [общие правила ступени 1](README.md#создание-и-обновление-витрин).
|
||||
|
||||
## Типичная ошибка
|
||||
|
||||
Если заменить всю таблицу данными из нового донора, исчезнут итоги дней,
|
||||
истекших по `TTL` источника. Восстановить их неоткуда.
|
||||
Если заменить всю сводку новым расчетом, исчезнут итоги дней, за которые
|
||||
исходные строки уже удалены по `TTL`. Восстановить эти итоги неоткуда.
|
||||
|
||||
## Как проверить себя
|
||||
## Проверка результата
|
||||
|
||||
Деловой ключ — `source` и `error_class` внутри `load_date`. Проверяйте дни,
|
||||
оставшиеся хотя бы в одной таблице ошибок. Если дня за пределами `TTL` уже
|
||||
нет в источнике, это не ошибка: сверять его уже не с чем.
|
||||
Сопоставляйте строки по `load_date`, `source` и `error_class`. Проверяйте дни,
|
||||
оставшиеся хотя бы в одной таблице ошибок. Старые итоги, для которых исходные
|
||||
строки уже удалены по `TTL`, сверять не с чем.
|
||||
|
||||
Посчитайте строки прямо в обеих таблицах ошибок и соедините результат с
|
||||
`dm.dq_errors_daily_v` полным внешним соединением по ключу. Затем посчитайте
|
||||
строки, где ключ отсутствует с одной из сторон или различается `error_rows`.
|
||||
`dm.dq_errors_daily_v` через `FULL OUTER JOIN` по этим трем полям. Посчитайте
|
||||
строки, которые есть только с одной стороны или у которых различается `error_rows`.
|
||||
Расхождений должно быть ноль.
|
||||
|
||||
Сумма `error_rows` по проверяемым дням равна общему числу строк в
|
||||
@@ -99,10 +101,10 @@
|
||||
Если в источниках нет брака, витрина должна быть пустой. В этом случае
|
||||
расхождений тоже ноль и проверка успешна.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] у вас есть пять объектов витрины; донор объявлен через `AS` и
|
||||
использует ключ шардирования целевой таблицы;
|
||||
- [ ] у вас есть пять объектов витрины; промежуточная таблица объявлена через
|
||||
`AS` и использует ключ шардирования основной;
|
||||
- [ ] сводка включает ошибки событий и заказов из таблиц `_dist`;
|
||||
- [ ] ваш запрос пересобирает только дни, еще доступные в источниках,
|
||||
сохраняя более старые итоги;
|
||||
|
||||
@@ -1,30 +1,38 @@
|
||||
# Задание 5: `dm.utm_effectiveness`
|
||||
# Задание 5: показатели рекламы, `dm.utm_effectiveness`
|
||||
|
||||
**Чему учит:** различать сумму покупки со слов клиента (`declared_`) и
|
||||
серверную выручку, находить причины их расхождения.
|
||||
Вы подготовите показатели рекламных каналов по событиям сайта. Суммы покупок
|
||||
в этой витрине будут отличаться от выручки по заказам магазина. В конце
|
||||
работы вы сравните их и объясните причины расхождения.
|
||||
|
||||
## Сначала прочитать
|
||||
## Подготовка
|
||||
|
||||
- [«Хранилище и его слои»](../handbook/storage-layers.md);
|
||||
- [«Потоки Airflow и даги модельного времени»](../handbook/airflow-dags.md).
|
||||
- [«Генерация и обработка данных в Airflow»](../handbook/airflow-dags.md).
|
||||
|
||||
## Для чего нужна витрина
|
||||
## Покупки по рекламным каналам
|
||||
|
||||
Аналитик сравнивает рекламные каналы по дням: трафик, добавления в корзину,
|
||||
покупки и заявленные клиентом суммы.
|
||||
покупки и их суммы из событий сайта.
|
||||
|
||||
На стенде поведение и атрибуцию считают по трекеру, деньги — по бэкенду.
|
||||
Здесь сделано исключение: UTM-метки есть только в кликстриме, поэтому для
|
||||
оценки покупок по меткам используют клиентскую сумму.
|
||||
События сайта показывают действия посетителя и рекламные метки его переходов.
|
||||
Заказы магазина служат источником выручки. Но в заказах нет UTM-меток, поэтому
|
||||
в этой витрине вы возьмете суммы из событий покупки. Их передает браузер
|
||||
посетителя; дальше они называются клиентскими суммами. Приставка `declared_`
|
||||
в имени показателя напоминает об этом источнике.
|
||||
|
||||
## Что сделать
|
||||
## Создание витрины
|
||||
|
||||
Читайте `dds.event_v` напрямую, без соседних витрин DM. Зерно — **день
|
||||
события × `utm_source` × `utm_medium` × `utm_campaign`**. Зерно новое,
|
||||
поэтому витрина хранится готовым агрегатом — см. [«Как принято на ступени
|
||||
1»](README.md#как-принято-на-ступени-1). Создайте пять объектов:
|
||||
`dm.utm_effectiveness_rep` / `_dist`, донор `_stage_rep` / `_stage_dist` и
|
||||
публичное представление `dm.utm_effectiveness_v`. Ключ партиции — день.
|
||||
Читайте события из `dds.event_v`, без других витрин DM. В результате нужна
|
||||
одна строка на день и сочетание трех меток: `utm_source`, `utm_medium`,
|
||||
`utm_campaign`. События одного дня с одинаковыми метками попадают в одну группу.
|
||||
|
||||
Создайте пять объектов по [общим правилам ступени 1](README.md#создание-и-обновление-витрин):
|
||||
|
||||
- основные таблицы `dm.utm_effectiveness_rep` и `dm.utm_effectiveness_dist`;
|
||||
- промежуточные таблицы `dm.utm_effectiveness_stage_rep` и `dm.utm_effectiveness_stage_dist`;
|
||||
- представление для чтения `dm.utm_effectiveness_v`.
|
||||
|
||||
Разбейте данные на партиции по дням.
|
||||
|
||||
### Состав колонок
|
||||
|
||||
@@ -54,31 +62,32 @@
|
||||
Для `utm_campaign` выбран тип `String`: имен кампаний со временем становится
|
||||
много, и затраты на словарь `LowCardinality` перестают окупаться.
|
||||
|
||||
### Ритм пересборки
|
||||
### Дни для пересчета
|
||||
|
||||
События прожитого дня больше не меняются. Как в задании 2, соберите в донор
|
||||
дни от первого несобранного до последнего прожитого и замените дневные
|
||||
партиции. Добавьте задачу в `dm_transform`.
|
||||
События завершившегося модельного дня больше не меняются. Как в задании 2,
|
||||
найдите первый завершившийся день, которого еще нет в витрине. Рассчитайте
|
||||
все дни от него до последнего завершившегося, запишите их в промежуточную
|
||||
таблицу и замените дневные партиции основной. Добавьте задачу в `dm_transform`.
|
||||
|
||||
### Ключ шардирования
|
||||
|
||||
Выберите ключ самостоятельно. Если берете хеш одной из трех UTM-колонок,
|
||||
посчитайте долю строк с ее самым частым значением и обоснуйте выбор.
|
||||
|
||||
Ключ донора — тот же, что у цели (см. [«Как принято на ступени
|
||||
1»](README.md#как-принято-на-ступени-1) и раздел «Раскладка по шардам»
|
||||
[справочника хранилища](../architecture/storage.md)).
|
||||
У основной и промежуточной таблиц должен быть одинаковый ключ шардирования:
|
||||
см. [общие правила ступени 1](README.md#создание-и-обновление-витрин).
|
||||
|
||||
## Атрибуция по событию, а не по визиту
|
||||
## Рекламные метки событий
|
||||
|
||||
Группируйте события по их собственным меткам. Модели «первое касание» и
|
||||
«последнее касание» здесь не рассчитываются.
|
||||
|
||||
В `dds.session_v` записаны UTM первого события визита. Распределение по
|
||||
каналам может отличаться от этой витрины, и это не означает ошибку в
|
||||
расчете. В отчете различайте атрибуцию визитов и событий.
|
||||
расчете. Укажите в отчете, что распределяете по рекламным каналам отдельные
|
||||
события. Такое распределение называют атрибуцией.
|
||||
|
||||
## Прямой трафик остается в витрине
|
||||
## События без рекламных меток
|
||||
|
||||
Сохраните события без меток: прямой трафик обычно крупнейший канал, и без
|
||||
него доля рекламы будет завышена. Если отфильтровать такие строки, дневные
|
||||
@@ -108,13 +117,13 @@
|
||||
причины можно посмотреть в `dm.purchase_vs_orders_v`, где выделены классы
|
||||
расхождений.
|
||||
|
||||
## Как проверить себя
|
||||
## Проверка результата
|
||||
|
||||
Деловой ключ — `utm_source`, `utm_medium`, `utm_campaign` внутри дня.
|
||||
Получите прямой агрегат из `dds.event_v` за день и соедините его с
|
||||
`dm.utm_effectiveness_v` полным внешним соединением по ключу. Посчитайте
|
||||
строки, где ключ отсутствует с одной из сторон или различается любая из пяти
|
||||
величин. Расхождений должно быть ноль.
|
||||
Рассчитайте те же показатели по `dds.event_v` за выбранный день.
|
||||
Сопоставьте результат со строками `dm.utm_effectiveness_v` за этот день
|
||||
через `FULL OUTER JOIN` по `utm_source`, `utm_medium` и `utm_campaign`.
|
||||
Найдите сочетания меток, которые есть только с одной стороны, и строки,
|
||||
где различается хотя бы один из пяти показателей. Расхождений должно быть ноль.
|
||||
|
||||
Дополнительно сверьте дневные итоги:
|
||||
|
||||
@@ -126,10 +135,10 @@
|
||||
Сумма `visitors` по строкам повторно учитывает одного посетителя,
|
||||
приходящего по разным меткам. Для числа посетителей дня она не подходит.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] у вас есть пять объектов витрины; донор объявлен через `AS` и
|
||||
использует ключ шардирования целевой таблицы;
|
||||
- [ ] у вас есть пять объектов витрины; промежуточная таблица объявлена через
|
||||
`AS` и использует ключ шардирования основной;
|
||||
- [ ] строки без UTM-меток сохранены, а `declared_revenue` хранится
|
||||
в `Decimal(18, 2)`;
|
||||
- [ ] вы записали, почему сумма отличается от `dm.revenue_daily_v`;
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
# Свой график в Superset
|
||||
|
||||
**Чему учит:** увидеть, как собственный результат из хранилища становится
|
||||
отчетом в BI.
|
||||
В этой работе вы построите график по своей витрине и проверите числа
|
||||
на нем SQL-запросом.
|
||||
|
||||
## Сначала прочитать
|
||||
## Подготовка
|
||||
|
||||
- [«Немного Superset»](../handbook/superset.md);
|
||||
- [«Отчеты в Superset»](../handbook/superset.md);
|
||||
- [задание 3: `dm.session_overview`](1-3-session-overview.md).
|
||||
|
||||
## На какой вопрос ответит график
|
||||
## График отказов по устройствам
|
||||
|
||||
В задании 3 вы собрали витрину `dm.session_overview_v`. Теперь покажите, с
|
||||
каких устройств чаще уходят после первой страницы. Постройте столбиковый
|
||||
график доли отказов по `device_category` за весь период витрины. Измерение
|
||||
одно — `device_category`, метрика тоже одна.
|
||||
В задании 3 первой ступени вы собрали витрину `dm.session_overview_v`.
|
||||
Теперь покажите, с каких устройств чаще уходят после единственного события.
|
||||
Такие визиты в стенде считаются отказами. Постройте столбиковый график доли
|
||||
отказов по `device_category` за весь период витрины. Каждый столбик должен
|
||||
показывать долю для одной категории устройств.
|
||||
|
||||
## Что сделать
|
||||
## Настройка графика
|
||||
|
||||
Заведите физический датасет в подключении к `ClickHouse`: схема `dm`, объект
|
||||
`session_overview_v`. Назовите по-русски хотя бы две колонки:
|
||||
Добавьте представление как источник данных для графиков. В Superset такой
|
||||
источник называется физическим датасетом. Выберите подключение `ClickHouse`,
|
||||
схему `dm` и объект `session_overview_v`. Задайте русские подписи хотя бы двум
|
||||
колонкам:
|
||||
|
||||
- `device_category` — «Категория устройства»;
|
||||
- `report_date` — «Дата отчета».
|
||||
@@ -32,10 +35,10 @@
|
||||
| `SQL expression` | `SUM(bounce_sessions) / SUM(sessions)` |
|
||||
| `D3 format` | `.1%` |
|
||||
|
||||
Доля считается из сумм слагаемых: среднее готовых долей соврет. Поэтому
|
||||
метрике нужно это выражение. Сохраните ее в датасете, а не в графике:
|
||||
подписи и формат задаются в датасете, а график их использует. Так во всех
|
||||
будущих графиках у метрики будут одинаковые подписи. Это правило описано в
|
||||
Выражение делит общее число отказов на общее число сессий. Усреднение дневных
|
||||
долей может дать неверный результат: число сессий в разные дни различается.
|
||||
Сохраните метрику в датасете, чтобы все будущие графики использовали одно
|
||||
выражение, подпись и формат. Это правило описано в
|
||||
[справочнике Superset](../architecture/superset.md#дерево-импорта).
|
||||
|
||||
Не используйте вкладку «Custom SQL» конструктора графика. Созданная там
|
||||
@@ -56,9 +59,9 @@ FROM dm.session_overview_v;
|
||||
```
|
||||
|
||||
Стендовые объекты не меняйте. Что переживает `make up`, а что стирает
|
||||
`make clean`, описано в главе [«Немного Superset»](../handbook/superset.md#стендовые-объекты-и-ваши).
|
||||
`make clean`, описано в главе [«Отчеты в Superset»](../handbook/superset.md#готовые-и-собственные-отчеты).
|
||||
|
||||
## Как проверить себя
|
||||
## Проверка результата
|
||||
|
||||
Автоматической проверки этого задания нет. Сверьте числа на графике
|
||||
с результатом запроса в SQL Lab:
|
||||
@@ -76,7 +79,7 @@ ORDER BY device_category;
|
||||
проценты с одним знаком, как формат `.1%`, поэтому числа должны совпасть
|
||||
один в один.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] у вас есть физический датасет `dm.session_overview_v` в подключении
|
||||
`ClickHouse`;
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
# Нарежьте сессии сами
|
||||
# Сессии из событий
|
||||
|
||||
**Чему учит:** собрать сессии из событий, сравнить их число с `VisitID`
|
||||
источника и найти события, из-за которых расчеты расходятся.
|
||||
Вы самостоятельно объедините события сайта в сессии и сравните их число
|
||||
с готовыми визитами источника, обозначенными `VisitID`. Затем найдете
|
||||
события, из-за которых расчеты расходятся.
|
||||
|
||||
## Сначала прочитать
|
||||
## Подготовка
|
||||
|
||||
- [Хранилище и его слои, раздел DDS](../handbook/storage-layers.md#dds-язык-бизнеса);
|
||||
- [Хранилище и его слои, раздел DDS](../handbook/storage-layers.md#dds-события-заказы-и-сессии);
|
||||
- [формат события: Идентификаторы и время](../formats/clickstream-event.md#идентификаторы-и-время).
|
||||
|
||||
## Для чего нужны сессии
|
||||
## События и визиты
|
||||
|
||||
Визит, или сессия, объединяет действия посетителя за один заход на сайт.
|
||||
По визитам аналитик считает длительность и глубину просмотра.
|
||||
В хранилище сессии собираются из отдельных событий.
|
||||
Сверьте самостоятельный расчет с `VisitID`, чтобы увидеть, как из событий
|
||||
получается сессия.
|
||||
|
||||
## Что сделать
|
||||
## Расчет сессий
|
||||
|
||||
### Нарезка
|
||||
### Границы визитов
|
||||
|
||||
В SQL Lab или DBeaver под `analyst` напишите запрос к `ods.event_v`.
|
||||
Для своей нарезки и эталона возьмите одни и те же строки за все доступные
|
||||
Для своего расчета и эталона возьмите одни и те же строки за все доступные
|
||||
дни: для расследования понадобятся события соседних дней.
|
||||
Выберите `ClientID`, `UTCEventTime`, `EventDate`, `EventType` и `VisitID`.
|
||||
В `dds.event_v` эталонного `VisitID` нет.
|
||||
|
||||
Для начала считайте визитом последовательные события одной куки (`ClientID`);
|
||||
пауза дольше 30 минут открывает новый визит.
|
||||
`ClientID` хранится в cookie браузера и обозначает посетителя в событиях
|
||||
сайта. Для начала считайте визитом последовательные события одного
|
||||
`ClientID`: пауза дольше 30 минут открывает новый визит.
|
||||
Учитывайте события всех типов. Упорядочите их по `UTCEventTime`.
|
||||
Присвойте каждому событию номер своей сессии внутри куки.
|
||||
Присвойте каждому событию номер своей сессии внутри `ClientID`.
|
||||
Подсказка: оконные функции.
|
||||
|
||||
Днем сессии считайте `EventDate` ее первого события: дата из `UTCEventTime`
|
||||
@@ -38,7 +38,8 @@
|
||||
|
||||
### Сверка с эталоном
|
||||
|
||||
В эталоне посчитайте число визитов каждой куки за модельный день:
|
||||
Посчитайте по эталонному `VisitID` число визитов каждого `ClientID`
|
||||
за модельный день:
|
||||
|
||||
```sql
|
||||
SELECT ClientID, EventDate, uniqExact(VisitID) AS reference_sessions
|
||||
@@ -47,46 +48,48 @@ GROUP BY ClientID, EventDate
|
||||
ORDER BY EventDate, ClientID;
|
||||
```
|
||||
|
||||
Посчитайте свои сессии по куке и дню сессии.
|
||||
Соедините два готовых агрегата через `FULL OUTER JOIN` по куке и дню.
|
||||
В результате полного соединения останутся ключи обеих сторон: при расхождении
|
||||
на вашей стороне ключа может не быть совсем. Учтите такие строки при сравнении.
|
||||
Выведите сводку по дням с числом разошедшихся кук и список этих кук
|
||||
с днем и обоими значениями счета. Общая сумма за день могла бы скрыть
|
||||
взаимную компенсацию ошибок разных кук. По списку видно, у кого искать причину.
|
||||
Посчитайте свои сессии по `ClientID` и дню сессии. Соедините результаты двух
|
||||
расчетов через `FULL OUTER JOIN` по этим полям. Учтите строки, которые есть
|
||||
только с одной стороны: полное соединение сохранит их в результате.
|
||||
|
||||
Выведите сводку по дням с числом `ClientID`, для которых расчеты разошлись,
|
||||
и список этих идентификаторов с днем и обоими значениями. Общая сумма за день
|
||||
могла бы скрыть ошибки: для одного посетителя вы посчитали лишнюю сессию,
|
||||
для другого пропустили. По списку видно, чьи события нужно исследовать.
|
||||
|
||||
Расхождений будет всего несколько. Если их нет, мир прожил мало дней.
|
||||
Прогоните несколько дней через [даги модельного времени](../handbook/airflow-dags.md#даги-мира)
|
||||
Прогоните несколько дней через [даги модельного времени](../handbook/airflow-dags.md#генерация-модельных-дней)
|
||||
и повторите сверку.
|
||||
|
||||
### Расследование
|
||||
### Поиск причины расхождения
|
||||
|
||||
Выберите одну разошедшуюся куку. Выведите ее события за два соседних дня
|
||||
Выберите один `ClientID` с расхождением. Выведите его события за два соседних дня
|
||||
с `UTCEventTime`, `EventDate`, `VisitID` и номером своей сессии.
|
||||
Сопоставьте сессии через события: общего ключа у двух нарезок нет,
|
||||
Сопоставьте сессии через события: общего ключа у двух расчетов нет,
|
||||
зато у каждого события есть и `VisitID`, и присвоенный вами номер.
|
||||
Найдите два соседних события, между которыми границы визитов разошлись.
|
||||
Запишите куку, день, время обоих событий и причину одним предложением.
|
||||
Запишите `ClientID`, день, время обоих событий и причину одним предложением.
|
||||
Уточните определение визита и добейтесь нуля расхождений во всех днях.
|
||||
|
||||
## Как проверить себя
|
||||
## Проверка результата
|
||||
|
||||
Сохраните исходный и уточненный расчеты, результаты сверки и найденные строки.
|
||||
Таблицы и представления создавать не нужно. Задачи в `homework_check`
|
||||
для этого задания нет: вы проверяете расчет своим запросом.
|
||||
|
||||
В `sql/dds/session_rebuild.sql` и
|
||||
`sql/dq/sessions_vs_reference.sql` лежат готовые запросы нарезки и сверки.
|
||||
`sql/dq/sessions_vs_reference.sql` лежат готовые запросы расчета сессий и сверки.
|
||||
Откройте их после собственного решения и расследования. Сравните запросы
|
||||
со своими.
|
||||
Сопоставление со стендовой нарезкой по `session_id` остается на ступень 4.
|
||||
На ступени 4 вы сравните свой результат с готовыми сессиями стенда
|
||||
по `session_id`.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] ваш запрос собирает сессии из всех событий `ods.event_v` за все
|
||||
доступные дни;
|
||||
- [ ] у вас есть сводка расхождений по дням и список кук, полученные через
|
||||
- [ ] у вас есть сводка расхождений по дням и список `ClientID`, полученные через
|
||||
`FULL OUTER JOIN`;
|
||||
- [ ] вы объяснили причину одного расхождения и записали куку, день и два
|
||||
- [ ] вы объяснили причину одного расхождения и записали `ClientID`, день и два
|
||||
события, на которых оно видно;
|
||||
- [ ] уточненный расчет дает ноль расхождений во всех днях.
|
||||
@@ -1,16 +1,17 @@
|
||||
# Перепишите нарезку стенда
|
||||
# Расчет сессий в стенде
|
||||
|
||||
**Чему учит:** переписать расчет сессий в готовом конвейере и проверить
|
||||
На прошлой ступени вы рассчитали сессии отдельным запросом. Теперь
|
||||
перепишете расчет внутри стенда, запустите его через Airflow и проверите
|
||||
результат по исходным событиям.
|
||||
|
||||
## Сначала прочитать
|
||||
## Подготовка
|
||||
|
||||
- [задание «Нарежьте сессии сами»](3-1-sessions-by-hand.md);
|
||||
- [задание «Сессии из событий»](3-1-sessions-by-hand.md);
|
||||
- [модель сессии DDS](../architecture/dds/session.md);
|
||||
- [дневной конвейер трансформаций](../architecture/etl.md#объём-прогона);
|
||||
- [раскладка по шардам](../architecture/storage.md#раскладка-по-шардам).
|
||||
|
||||
## Что предстоит сделать
|
||||
## Файлы для изменения
|
||||
|
||||
Даг, таблицы сессий и проверка их числа уже созданы.
|
||||
Перепишите только три файла:
|
||||
@@ -19,56 +20,57 @@
|
||||
- `sql/dds/session_rebuild.sql`;
|
||||
- `sql/dds/session_replace.sql`.
|
||||
|
||||
Удалите из них готовый SQL и соберите свою реализацию. Параметры и порядок
|
||||
запросов заданы в `dds_transform`. DDL задает целевые таблицы и донор. Донор —
|
||||
служебная таблица, где день собирается перед заменой. В
|
||||
`sessions_vs_reference.sql` число сессий сравнивается с `VisitID` из ODS.
|
||||
Удалите из них готовый SQL и напишите свой. Даг `dds_transform` передаст
|
||||
параметры и выполнит запросы по порядку. Таблицы уже созданы: основные хранят
|
||||
готовые сессии, промежуточные нужны для нового расчета перед заменой данных.
|
||||
Запрос `sessions_vs_reference.sql` сравнивает число сессий с `VisitID` из ODS.
|
||||
|
||||
## Как даг запускает ваши запросы
|
||||
## Выполнение запросов в Airflow
|
||||
|
||||
Объем прогона — дни, которые соберет один запуск. Его вычисляет первый файл.
|
||||
|
||||
| Файл | Вход | Результат |
|
||||
|---|---|---|
|
||||
| `session_scope.sql` | `{position:UInt16}` — номер первого несыгранного дня | `first_day` и `last_day` |
|
||||
| `session_scope.sql` | `{position:UInt16}` — номер первого еще не прожитого модельного дня | `first_day` и `last_day` |
|
||||
| `session_rebuild.sql` | `{first_day:Date}`, `{last_day:Date}`, `{load_id:String}` | Сессии в `dds.session_stage_dist` |
|
||||
| `session_replace.sql` | День в `{{ params.day }}` | Партиция дня в `dds.session_rep` |
|
||||
|
||||
Первый прожитый день, которого еще нет в DDS, становится `first_day`.
|
||||
`last_day` — последний прожитый день. Начало шкалы нужно взять из событий ODS:
|
||||
`position` считает дни от него. Живой модельный день еще не закончен, поэтому
|
||||
сессии за него не собираются.
|
||||
`first_day` должен быть первым завершившимся модельным днем, которого еще
|
||||
нет в DDS, а `last_day` последним завершившимся днем. Начальную дату возьмите
|
||||
из событий ODS: `position` считает дни от нее. Текущий модельный день еще
|
||||
не закончен, поэтому сессии за него не рассчитываются.
|
||||
|
||||
Если в ODS нет ни одного события, запрос не возвращает строк. Если DDS уже
|
||||
догнал мир, запрос возвращает строку с `first_day = NULL`. В этом случае группа
|
||||
сессий в Airflow получает состояние `skipped`.
|
||||
Если в ODS нет ни одного события, запрос не возвращает строк. Если все
|
||||
завершившиеся дни уже есть в DDS, запрос возвращает строку с `first_day = NULL`.
|
||||
Тогда Airflow пропускает группу задач расчета сессий: ее состояние будет `skipped`.
|
||||
|
||||
`session_rebuild.sql` сначала очищает `dds.session_stage_rep` на всем кластере.
|
||||
Затем он собирает в `dds.session_stage_dist` все дни отрезка. Добавьте к
|
||||
Затем он записывает в `dds.session_stage_dist` расчет за все выбранные дни. Добавьте к
|
||||
вставке `SETTINGS distributed_foreground_insert = 1`. Так строки успеют попасть
|
||||
на шарды до замены партиций.
|
||||
|
||||
Цель `session_replace.sql` — локальная таблица `dds.session_rep`, источник —
|
||||
локальный донор `dds.session_stage_rep`. Операция выполняется на всем кластере.
|
||||
Запрос `session_replace.sql` заменяет партицию в основной `dds.session_rep`
|
||||
готовой партицией из промежуточной `dds.session_stage_rep`.
|
||||
Операция выполняется на всем кластере, на локальных таблицах каждого шарда.
|
||||
|
||||
Не ставьте точку с запятой в конце `session_scope.sql`. Драйвер добавляет формат
|
||||
ответа, и точка с запятой превратит строку в мультизапрос, который ClickHouse
|
||||
ответа. С точкой с запятой получится несколько команд, которые ClickHouse
|
||||
отклонит. День для замены подставляет шаблон Jinja в Airflow. Параметр
|
||||
ClickHouse в `ALTER ... PARTITION` здесь не работает.
|
||||
|
||||
## Нарежьте сессии
|
||||
## Правила расчета сессий
|
||||
|
||||
Источник сборки — `dds.event_v`. Одна строка результата представляет один
|
||||
визит. На третьей ступени вы уже нарезали сессии:
|
||||
визит. На третьей ступени вы уже получили правила разделения событий на сессии:
|
||||
|
||||
- сессия не пересекает модельные сутки;
|
||||
- пауза больше 30 минут открывает новую сессию;
|
||||
- в нарезке участвуют события всех типов.
|
||||
- в расчете участвуют события всех типов.
|
||||
|
||||
Пересборка добавляет требования к повторяемости. При одинаковом времени
|
||||
порядок событий задает `event_id`, поэтому полный порядок окон —
|
||||
Повторный расчет должен давать тот же результат. При одинаковом времени
|
||||
упорядочивайте события по `event_id`: порядок в оконных функциях должен быть
|
||||
`(event_time, event_id)`. Один визит должен получать одинаковый `session_id`
|
||||
при каждом запуске. Тогда его можно сравнить с сохраненным результатом.
|
||||
при каждом запуске.
|
||||
Вычислите идентификатор как `cityHash64(client_id, min(event_time))`.
|
||||
|
||||
Состав и типы строки заданы в `dds.session_rep`. Заполните начало, конец и
|
||||
@@ -76,11 +78,11 @@ ClickHouse в `ALTER ... PARTITION` здесь не работает.
|
||||
покупки. Возьмите входную и выходную страницы, UTM первого события, устройство
|
||||
и город. Заполните также служебные колонки из схемы таблицы.
|
||||
|
||||
Пишите через `dds.session_stage_dist`: место строки определит ключ
|
||||
`client_id`. При записи прямо в локальную таблицу оно зависело бы от ноды,
|
||||
которая выполнила запрос.
|
||||
Пишите через `dds.session_stage_dist`: шард для строки определяется по
|
||||
`client_id`. При записи прямо в локальную таблицу он зависел бы от узла,
|
||||
который выполнил запрос.
|
||||
|
||||
## Запустите свою реализацию
|
||||
## Запуск нового расчета
|
||||
|
||||
Готовые дни повторно не собираются. Чтобы новый SQL точно выполнился, выберите
|
||||
последний собранный день:
|
||||
@@ -90,8 +92,8 @@ SELECT max(session_date)
|
||||
FROM dds.session_v;
|
||||
```
|
||||
|
||||
Берите последний день: более ранняя дыра расширит объем до всех последующих
|
||||
прожитых дней.
|
||||
Берите последний день: если удалить более ранний, расчет придется повторить
|
||||
и за все последующие завершившиеся дни.
|
||||
|
||||
Сохраните десять строк с идентификатором, границами, длительностью и счетчиками:
|
||||
|
||||
@@ -113,7 +115,7 @@ ORDER BY client_id, started_at, session_id
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
Создайте в DBeaver подключение к первой ноде по [инструкции из
|
||||
Создайте в DBeaver подключение к первому узлу по [инструкции из
|
||||
README](../../README.md#подключиться-из-dbeaver). Укажите пользователя `etl` и
|
||||
пароль `CLICKHOUSE_ETL_PASSWORD` из `.env`. У этой учетной записи есть право
|
||||
менять таблицы DDS на кластере. Подставьте найденную дату и удалите только ее
|
||||
@@ -128,19 +130,18 @@ DROP PARTITION 'YYYY-MM-DD';
|
||||
группу `session`: в ней должны выполниться задачи `scope` и `rebuild`, а также
|
||||
`replace` для выбранного дня. Если после третьей ступени мир ушел вперед,
|
||||
в группе появятся задачи `replace` и для последующих прожитых дней. Следом
|
||||
выполнятся витрины и `dq_check`.
|
||||
обновятся витрины и выполнится `dq_check`.
|
||||
|
||||
Если прогон дошел до замены, но упал на проверке, ошибочная партиция уже
|
||||
находится в DDS. После правки снова удалите тот же день перед следующим
|
||||
запуском. Иначе день останется заполненным, а новая сборка для него не
|
||||
выполнится.
|
||||
|
||||
## Как проверить себя
|
||||
## Проверка результата
|
||||
|
||||
Сначала повторите запрос с десятью строками. `session_id`, границы,
|
||||
длительность и счетчики должны совпасть с сохраненным результатом. Так вы
|
||||
завершите отложенное с третьей ступени сопоставление со стендовой нарезкой по
|
||||
`session_id`.
|
||||
длительность и счетчики должны совпасть с сохраненным результатом.
|
||||
Это проверит, что ваш расчет восстановил те же сессии с теми же идентификаторами.
|
||||
|
||||
Задача `dq_check.sessions_vs_reference` должна стать зеленой. Та же проверка
|
||||
видна в хранилище:
|
||||
@@ -152,33 +153,34 @@ WHERE check_name = 'sessions_vs_reference'
|
||||
ORDER BY data_date;
|
||||
```
|
||||
|
||||
Эта сверка сравнивает число сессий каждой куки за день с `VisitID`. Нулевой
|
||||
`failed_rows` не подтверждает границы, страницы и UTM: две ошибки могут
|
||||
оставить тот же счет. Возьмите поштучную сверку из третьей ступени. Для одной
|
||||
куки выбранного дня сопоставьте границы `VisitID` по событиям с границами
|
||||
строк `dds.session_v`. По исходным событиям проверьте страницы, UTM, устройство
|
||||
Эта сверка сравнивает число сессий каждого `client_id` за день с `VisitID`.
|
||||
При `failed_rows = 0` число сессий совпало, но их границы, страницы и UTM
|
||||
еще могут различаться. Как на третьей ступени, проверьте отдельные сессии.
|
||||
Для одного `client_id` выбранного дня найдите начало и конец каждого
|
||||
`VisitID` по событиям и сравните с границами строк `dds.session_v`.
|
||||
По исходным событиям проверьте страницы, UTM, устройство
|
||||
и город. Страницы и UTM не входят в сохраненные десять строк: готовый SQL
|
||||
стенда выбирал их через `argMin` и `argMax` только по `event_time`. При равном
|
||||
времени ожидаемое значение определяйте по исходным событиям и полному порядку
|
||||
`(event_time, event_id)`.
|
||||
|
||||
Осталась физическая раскладка. Напишите отдельный запрос к
|
||||
`dds.session_dist`. Для каждой строки он должен:
|
||||
Проверьте также, на каком шарде лежит каждая строка. Напишите отдельный
|
||||
запрос к `dds.session_dist`. Он должен:
|
||||
|
||||
1. получить фактическую ноду через `hostName()`;
|
||||
2. пересчитать ожидаемую ноду по `cityHash64(client_id)`;
|
||||
3. посчитать строки, у которых ноды не совпали.
|
||||
1. получить фактический узел строки через `hostName()`;
|
||||
2. определить ожидаемый узел по `cityHash64(client_id)`;
|
||||
3. посчитать строки, у которых фактический и ожидаемый узлы не совпали.
|
||||
|
||||
У кластера два равных шарда: остаток 0 соответствует `clickhouse-01`, остаток
|
||||
1 — `clickhouse-02`. Сгруппируйте ответ по фактической ноде. На каждой ноде
|
||||
1 — `clickhouse-02`. Сгруппируйте ответ по фактическому узлу. На каждом узле
|
||||
число строк на чужом шарде должно быть нулевым. Проверка числа сессий читает
|
||||
Distributed-таблицу и такую ошибку не заметит.
|
||||
|
||||
После зеленого результата запустите `etl_pipeline` еще раз, не удаляя
|
||||
партицию. Расчет объема должен пропустить группу сессий: новых прожитых дней и
|
||||
дыр в слое нет.
|
||||
партицию. Группа сессий должна быть пропущена: все завершившиеся дни уже
|
||||
рассчитаны, новых дней и пропусков нет.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] вы переписали три файла `sql/dds/session_*.sql`, сохранив даг,
|
||||
таблицы и проверку стенда;
|
||||
@@ -186,6 +188,6 @@ Distributed-таблицу и такую ошибку не заметит.
|
||||
- [ ] вы сверили десять сохраненных строк: `session_id`, границы,
|
||||
длительность и счетчики совпали после пересборки;
|
||||
- [ ] вы получили ноль расхождений в `sessions_vs_reference` за все
|
||||
прожитые дни и вручную сверили границы и атрибуты сессии одной куки;
|
||||
прожитые дни и вручную сверили границы и поля сессии одного `client_id`;
|
||||
- [ ] ваш запрос с `hostName()` не нашел строк на чужом шарде;
|
||||
- [ ] повторный прогон без удаления данных пропустил группу сессий.
|
||||
@@ -1,9 +1,10 @@
|
||||
# Научите проверки замечать ошибки в заказах
|
||||
# Проверки качества заказов
|
||||
|
||||
**Чему учит:** написать проверки заказов до изменения их обработки
|
||||
и убедиться на намеренной ошибке, что проверки ее замечают.
|
||||
Перед изменением обработки заказов вы добавите две проверки качества данных.
|
||||
Затем намеренно испортите один заказ и убедитесь, что проверки обнаружили
|
||||
ошибки.
|
||||
|
||||
## Сначала прочитать
|
||||
## Подготовка
|
||||
|
||||
- [модель заказа DDS](../architecture/dds/order.md);
|
||||
- [дневной конвейер](../architecture/etl.md#объём-прогона);
|
||||
@@ -13,30 +14,29 @@
|
||||
и на ее включение в [`dq_check`](../../dags/dq_check.py). Готового ответа для
|
||||
двух проверок из этого задания в репозитории нет.
|
||||
|
||||
## Что предстоит сделать
|
||||
## Новые проверки
|
||||
|
||||
Добавьте две проверки и включите их в даг:
|
||||
|
||||
- `sql/dq/order_items_total.sql` проверяет сумму позиций заказа;
|
||||
- `sql/dq/orders_vs_ods.sql` сверяет DDS с его источником;
|
||||
- `dags/dq_check.py` запускает обе проверки, публикует их строки в
|
||||
- `dags/dq_check.py` запускает обе проверки, записывает их результаты в
|
||||
`dm.dq_summary` и создает для них отдельные задачи.
|
||||
|
||||
Разбор ODS и сборку DDS вы перепишете в следующих заданиях. Здесь сохраните
|
||||
их, даги загрузки, DDL, таблицы-доноры, Kafka и службы как есть.
|
||||
их, даги загрузки, DDL, промежуточные таблицы, Kafka и службы как есть.
|
||||
|
||||
Перед опытами остановите автоматическое движение мира: поставьте `world_live`
|
||||
на паузу и дождитесь завершения активных прогонов. До завершения опытов не
|
||||
запускайте руками `world_next_day`, `world_live_day`, `world_initialize` и
|
||||
`world_recreate`. Это условие стабильного модельного времени: одной паузы
|
||||
`world_live` для него мало.
|
||||
`world_recreate`, чтобы новые данные не меняли результат между проверками.
|
||||
|
||||
## Что будут проверять ваши запросы
|
||||
## Требования к запросам
|
||||
|
||||
Чтобы `dq_check` мог запустить проверку, ее запрос возвращает по одной строке
|
||||
на день с колонками `data_date`, `reference_rows`, `actual_rows`,
|
||||
`failed_rows`, `diagnostics`. Даг сохраняет в `dm.dq_summary` только счетчики,
|
||||
статус и координаты запуска. Примеры из `diagnostics` ищите в
|
||||
статус и сведения о запуске. Примеры из `diagnostics` ищите в
|
||||
журнале упавшей задачи Airflow. Ограничьте их тем же малым числом, что и
|
||||
существующие запросы в `sql/dq/`.
|
||||
|
||||
@@ -44,46 +44,48 @@
|
||||
|
||||
Имя проверки - `order_items_total`. Для каждого заказа из `dds.order_v`
|
||||
посчитайте сумму `item_price * item_quantity` и сравните ее с `items_total`.
|
||||
`reference_rows` считает ключи со стороны `items_total`, `actual_rows` - ключи
|
||||
со стороны вычисленной суммы, а `failed_rows` - несовпавшие значения. В
|
||||
исправном наборе первые два счетчика равны; несовпадения сумм считает
|
||||
`failed_rows`. Для диагностики достаточно `order_id`, ожидаемой суммы и
|
||||
суммы позиций.
|
||||
В `reference_rows` запишите число заказов со стороны `items_total`,
|
||||
в `actual_rows` число заказов со стороны вычисленной суммы, в `failed_rows`
|
||||
число заказов, у которых суммы не совпали. На исправных данных первые два
|
||||
счетчика равны, а `failed_rows` равен нулю. Для примеров расхождений
|
||||
достаточно `order_id`, значения `items_total` и вычисленной суммы позиций.
|
||||
|
||||
Проверка относится к типизированному разбору массива `items` на границе
|
||||
ODS -> DDS. Скидку, доставку и равенство `total` здесь проверять не нужно.
|
||||
Проверка поможет заметить ошибки разбора `items` при переносе заказа
|
||||
из ODS в DDS. Скидку, доставку и равенство `total` здесь проверять не нужно.
|
||||
|
||||
### DDS против ODS
|
||||
### Полнота заказов в DDS
|
||||
|
||||
Имя проверки - `orders_vs_ods`. Источник модели DDS здесь - текущее состояние
|
||||
`ods.order_v`. С ним вы и сравниваете DDS; обращаться к внутреннему состоянию
|
||||
генератора для этой проверки не нужно.
|
||||
Одним запросом соберите обе стороны на зерне дня заказа и сравните:
|
||||
Одним запросом рассчитайте итоги каждого дня заказа в обоих слоях и сравните:
|
||||
|
||||
- число заказов;
|
||||
- максимальный `updated_at`.
|
||||
|
||||
День ODS вычисляется в `Europe/Samara`, как `order_date` в DDS. Область
|
||||
проверки - объединение дней обеих сторон, включая день, который полностью
|
||||
пропал из одного слоя. Перед сравнением приведите времена к одному часовому
|
||||
поясу. `reference_rows` - число заказов ODS, `actual_rows` - число заказов DDS,
|
||||
а `failed_rows` равен единице для дня, где расходится хотя бы одно значение.
|
||||
День заказа в ODS вычисляйте в `Europe/Samara`, как `order_date` в DDS.
|
||||
Проверьте все дни, которые есть хотя бы в одном слое: так вы заметите день,
|
||||
целиком пропавший из другого. Перед сравнением приведите времена к одному
|
||||
часовому поясу. `reference_rows` содержит число заказов ODS, `actual_rows`
|
||||
число заказов DDS. В `failed_rows` запишите 1, если за день расходится
|
||||
хотя бы одно значение, и 0, если оба совпали.
|
||||
|
||||
Обе стороны распределены по `order_id`. Дневные агрегаты соедините через
|
||||
`GLOBAL FULL OUTER JOIN` с `join_use_nulls = 1`, как в существующих сверках.
|
||||
|
||||
Если разбор ODS и сборка DDS одинаково потеряют строку, слои останутся равны
|
||||
по этой проверке. Общая потеря при разборе проявится в отдельном балансе по
|
||||
`_load_id` из [следующего задания](5-2-order-parsing.md).
|
||||
Если строка потерялась при разборе в ODS, она не попадет и в DDS. Эта проверка
|
||||
не заметит потерю: данные слоев совпадут. В [следующем задании](5-2-order-parsing.md)
|
||||
вы отдельно сравните число исходных строк с числом разобранных заказов
|
||||
и ошибок за один запуск, обозначенный `_load_id`.
|
||||
|
||||
## Получите красный и зеленый результаты
|
||||
## Проверка на намеренных ошибках
|
||||
|
||||
Сначала запустите `dq_check` на нетронутых данных. Обе новые задачи должны
|
||||
завершиться успешно, а в `dm.dq_summary_v` у обоих имен не должно быть
|
||||
нарушенных дней.
|
||||
дней с расхождениями.
|
||||
|
||||
Выберите оплаченный заказ из последнего слепка с непустым массивом позиций и
|
||||
сохраните его `order_id` и `order_date`. Подключитесь к первой ноде из DBeaver
|
||||
сохраните его `order_id` и `order_date`. Подключитесь к первому узлу из DBeaver
|
||||
по [инструкции из README](../../README.md#подключиться-из-dbeaver) под
|
||||
пользователем `etl`. Прибавьте одну копейку к `items_total` этого заказа в
|
||||
локальной таблице `dds.order_rep` на всем кластере:
|
||||
@@ -102,7 +104,7 @@ SETTINGS mutations_sync = 2;
|
||||
|
||||
Запустите `etl_pipeline`. Текущее окно заказов будет заново собрано из ODS, и
|
||||
исходная строка вернется в DDS. Повторный `dq_check` должен дать зеленый
|
||||
результат для нового сторожа.
|
||||
результат для новой проверки.
|
||||
|
||||
Теперь удалите тот же заказ из `dds.order_rep` на всем кластере:
|
||||
|
||||
@@ -118,9 +120,9 @@ SETTINGS mutations_sync = 2;
|
||||
Если `orders_vs_ods` не обнаружил удаление, исправьте запрос до следующего
|
||||
задания.
|
||||
|
||||
## Как проверить себя
|
||||
## Проверка результата
|
||||
|
||||
В `dm.dq_summary_v` должны быть зелены обе новые и пять прежних проверок:
|
||||
В `dm.dq_summary_v` обе новые и пять прежних проверок должны быть успешны:
|
||||
|
||||
```sql
|
||||
SELECT check_name, sum(failed_rows) AS failed_rows, groupUniqArray(status)
|
||||
@@ -132,12 +134,12 @@ ORDER BY check_name;
|
||||
Проверка суммы должна охватывать каждый день `dds.order_v`. Сверка слоев
|
||||
должна охватывать объединение дней ODS и DDS, включая дни только одной стороны.
|
||||
|
||||
## Завершите опыт
|
||||
## Завершение работы
|
||||
|
||||
После восстановления заказа и успешного `dq_check` можно закончить работу или
|
||||
перейти к разбору заказов, оставив модельное время на паузе.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] две новые проверки запускаются в `dq_check`, а их дневные результаты
|
||||
появляются в `dm.dq_summary`;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# Перепишите разбор заказов в ODS
|
||||
# Разбор заказов в ODS
|
||||
|
||||
**Чему учит:** переписать разбор заказов и на пробных сообщениях проверить,
|
||||
что годные строки и брак попадают в свои таблицы без потерь.
|
||||
Вы перепишете разбор заказов в ODS. На четырех пробных сообщениях проверите,
|
||||
что правильно заполненный заказ и сообщения с ошибками попадают
|
||||
в соответствующие таблицы без потерь.
|
||||
|
||||
## Сначала прочитать
|
||||
## Подготовка
|
||||
|
||||
- [проверки заказов](5-1-order-checks.md);
|
||||
- [прием заказов в ODS](../architecture/orders/ingestion.md);
|
||||
@@ -11,24 +12,22 @@
|
||||
- [`orders_ingest`](../../dags/orders_ingest.py);
|
||||
- [`order_parse.sql`](../../sql/ods/order_parse.sql).
|
||||
|
||||
Запрос для проверки пробных строк вы напишете сами.
|
||||
|
||||
## Что предстоит сделать
|
||||
## Порядок работы
|
||||
|
||||
Сначала проведите опыт на готовом `order_parse.sql`. Затем удалите его SQL,
|
||||
соберите разбор заново и повторите опыт с другим маркером.
|
||||
|
||||
Не меняйте устройство `orders_ingest`, DDL, Kafka, таблиц и преобразований
|
||||
DDS. Оба опыта должны закончиться адресной очисткой только своих строк.
|
||||
Сохраните устройство `orders_ingest`, DDL, Kafka, таблиц и преобразований
|
||||
DDS. После каждого опыта удалите только добавленные вами строки.
|
||||
|
||||
Перед началом убедитесь, что после [предыдущего задания](5-1-order-checks.md)
|
||||
все семь DQ остались зелеными. Поставьте `world_live` на паузу, дождитесь
|
||||
все семь проверок качества прошли успешно. Поставьте `world_live` на паузу, дождитесь
|
||||
активных прогонов и до завершения опыта не запускайте `world_next_day`,
|
||||
`world_live_day`, `world_initialize` и `world_recreate`. Не запускайте
|
||||
`etl_pipeline` между
|
||||
отправкой пробной пачки и ее очисткой.
|
||||
|
||||
## Четыре строки для двух ветвей
|
||||
## Пробные сообщения
|
||||
|
||||
Сделайте четыре сообщения с общим уникальным маркером:
|
||||
|
||||
@@ -39,9 +38,9 @@ DDS. Оба опыта должны закончиться адресной оч
|
||||
`field_invalid`.
|
||||
|
||||
`not_an_object` означает, что корень JSON не объект. `keyset_mismatch` означает,
|
||||
что у объекта отличается точный набор верхнеуровневых ключей. `field_invalid`
|
||||
означает, что ключи на месте, но значение не прошло проверку типа, формата или
|
||||
вместимости.
|
||||
что у объекта есть лишние верхнеуровневые ключи или не хватает обязательных.
|
||||
`field_invalid` означает, что ключи на месте, но значение имеет неверный тип,
|
||||
формат или не помещается в заданный тип данных.
|
||||
|
||||
Соберите строки в `/tmp/<маркер>.jsonl`. Заготовка дает одну годную строку и
|
||||
по одному примеру каждого класса брака:
|
||||
@@ -79,31 +78,33 @@ docker compose exec -T kafka /opt/kafka/bin/kafka-console-producer.sh \
|
||||
--bootstrap-server kafka:9092 --topic orders < "$LAB_BATCH"
|
||||
```
|
||||
|
||||
Запустите `orders_ingest` и найдите `_load_id` по маркеру в
|
||||
`stg.orders_raw_dist`. У четырех строк должен быть один идентификатор прогона.
|
||||
Запустите `orders_ingest`. Найдите свои строки по маркеру в
|
||||
`stg.orders_raw_dist` и сохраните их `_load_id`: это идентификатор запуска,
|
||||
который принял сообщения. Он должен быть одинаковым у всех четырех строк.
|
||||
|
||||
## Зафиксируйте исходный результат
|
||||
## Проверка готового разбора
|
||||
|
||||
До любого запуска `etl_pipeline` проверьте срез по найденному `_load_id`:
|
||||
До запуска `etl_pipeline` проверьте строки с найденным `_load_id`:
|
||||
|
||||
- в STG четыре строки;
|
||||
- в физической `ods.order_dist` одна годная строка;
|
||||
- в таблице `ods.order_dist` одна годная строка;
|
||||
- в `ods.order_errors_dist` три строки, по одной каждого класса;
|
||||
- `STG = годные + брак`;
|
||||
- текущая строка `ods.order_v` совпадает по деловым полям с результатом
|
||||
`argMax` по `snapshot_date` из годного сырья среза.
|
||||
- поля заказа в текущей строке `ods.order_v` совпадают с полями из годного
|
||||
исходного сообщения; последнюю версию для сравнения выберите через
|
||||
`argMax` по `snapshot_date`.
|
||||
|
||||
Счет физической таблицы нужен именно по `_load_id`: представление
|
||||
`ods.order_v` оставляет текущую версию сущности и не показывает, сколько строк
|
||||
разобрал запуск. Запрос сверки с `argMax` напишите сами. Эталонные деловые поля
|
||||
стройте только из `raw`; отдельно сравните различающиеся `created_at` и
|
||||
`updated_at`.
|
||||
Считайте строки `ods.order_dist` именно по `_load_id`: представление
|
||||
`ods.order_v` оставляет только текущую версию каждого заказа и не показывает,
|
||||
сколько строк разобрал запуск. Запрос сверки с `argMax` напишите сами.
|
||||
Значения для сравнения извлеките из `raw` пробных сообщений. Отдельно
|
||||
сравните `created_at` и `updated_at`: в заготовке они намеренно различаются.
|
||||
|
||||
Проверьте раскладку годной строки через `hostName()` на `ods.order_dist`.
|
||||
Фактическая нода должна совпасть с остатком `cityHash64(order_id) % 2`:
|
||||
Узел, на котором лежит строка, должен соответствовать остатку `cityHash64(order_id) % 2`:
|
||||
0 - `clickhouse-01`, 1 - `clickhouse-02`.
|
||||
|
||||
## Уберите пробную пачку
|
||||
## Удаление пробных сообщений
|
||||
|
||||
Удалите только строки маркера из локальных таблиц:
|
||||
|
||||
@@ -135,40 +136,41 @@ SETTINGS mutations_sync = 2;
|
||||
запустите `etl_pipeline`: пересборка текущего окна удалит заказ из DDS и
|
||||
обновит витрины.
|
||||
|
||||
## Перепишите разбор
|
||||
## Собственный разбор заказов
|
||||
|
||||
Удалите готовый SQL из `sql/ods/order_parse.sql` и напишите свой. Чтобы
|
||||
`orders_ingest` мог его использовать, сохраните следующие условия:
|
||||
|
||||
- вход - строки `stg.orders_raw_dist` одного `{load_id:String}`;
|
||||
- каждая строка среза попадает ровно в один результат: годный заказ или брак;
|
||||
- произвольный `raw` не останавливает весь разбор исключением;
|
||||
- запрос читает строки `stg.orders_raw_dist` с переданным `{load_id:String}`;
|
||||
- каждая прочитанная строка попадает ровно в один результат: годный заказ или брак;
|
||||
- ошибка в `raw` одной строки не прерывает разбор остальных;
|
||||
- годные строки записываются в `ods.order_dist`, остальные - в
|
||||
`ods.order_errors_dist`;
|
||||
- классы брака и их порядок - `not_an_object`, `keyset_mismatch`,
|
||||
`field_invalid`;
|
||||
- метки доставки и `_load_id` переходят из STG без пересчета;
|
||||
- задача завершается после доставки обоих результатов через Distributed, а
|
||||
строки распределяются по ключу целевой таблицы.
|
||||
- задача завершается после записи и заказов, и ошибок на шарды через
|
||||
Distributed; строки распределяются по ключу соответствующей целевой таблицы.
|
||||
|
||||
Точный набор полей, типы и граница строгого приема описаны в
|
||||
Точный набор полей, типы и условия проверки значений описаны в
|
||||
[документе приема](../architecture/orders/ingestion.md#граница-строгого-приёма),
|
||||
а целевые колонки - в [`20-ods-tables.sql`](../../sql/ddl/20-ods-tables.sql).
|
||||
Внутренность `items` остается сырым JSON: ее разбирает DDS.
|
||||
Содержимое `items` сохраните как JSON: его разбирает DDS.
|
||||
|
||||
Повторите опыт с новым маркером. Получите тот же баланс 4 = 1 + 3, те же три
|
||||
класса, верную текущую строку по `snapshot_date`, разные времена и правильный
|
||||
шард. Затем очистите второй срез теми же адресными командами и запустите
|
||||
класса, верную текущую версию заказа по `snapshot_date`, разные значения
|
||||
`created_at` и `updated_at` и правильный шард. Затем удалите строки второго
|
||||
опыта теми же командами и запустите
|
||||
`dq_check`.
|
||||
|
||||
## Завершите опыт
|
||||
## Завершение работы
|
||||
|
||||
Убедитесь, что оба маркера отсутствуют в STG, ODS, таблице брака и DDS,
|
||||
временные файлы удалены, а семь проверок успешны. Теперь можно перейти к
|
||||
сборке DDS, не снимая паузу с модельного времени. Если вы прерываетесь и до
|
||||
опыта `world_live` работал, снимите его с паузы.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] у вас сохранены результаты до переписывания: четыре строки дали
|
||||
один заказ и три класса брака; вы сверили поля текущей версии и шард;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# Перепишите сборку заказов в DDS
|
||||
# Сборка заказов в DDS
|
||||
|
||||
**Чему учит:** настроить пересборку заказов по состоянию ODS и DDS
|
||||
и проверить, что она восстанавливает пропущенный день за пределами текущего окна.
|
||||
Вы перепишете загрузку заказов из ODS в DDS и выбор дней для пересчета.
|
||||
Затем удалите данные за один из прошлых дней и проверите, что ваша
|
||||
загрузка восстановила их.
|
||||
|
||||
## Сначала прочитать
|
||||
## Подготовка
|
||||
|
||||
- [проверки заказов](5-1-order-checks.md);
|
||||
- [разбор заказов в ODS](5-2-order-parsing.md);
|
||||
@@ -11,7 +12,7 @@
|
||||
- [дневной конвейер](../architecture/etl.md#объём-прогона);
|
||||
- [раскладку по шардам](../architecture/storage.md#раскладка-по-шардам).
|
||||
|
||||
## Что предстоит сделать
|
||||
## Файлы для изменения
|
||||
|
||||
Удалите готовый SQL и перепишите только три файла:
|
||||
|
||||
@@ -19,18 +20,18 @@
|
||||
- `sql/dds/order_rebuild.sql`;
|
||||
- `sql/dds/order_replace.sql`.
|
||||
|
||||
Даг `dds_transform`, DDL, донор, ODS и обе проверки из первого задания
|
||||
Даг `dds_transform`, DDL, промежуточную таблицу, ODS и обе проверки из первого задания
|
||||
оставьте как есть. Перед началом убедитесь, что пробные строки предыдущего
|
||||
задания удалены, а семь DQ дают зеленый результат.
|
||||
задания удалены, а семь проверок качества дают зеленый результат.
|
||||
|
||||
Поставьте `world_live` на паузу и дождитесь завершения активных прогонов. До
|
||||
завершения опыта не запускайте `world_live_day`, `world_initialize` и
|
||||
`world_recreate`. `world_next_day` понадобится только в явно указанном ниже
|
||||
случае.
|
||||
|
||||
## Как даг запускает ваши запросы
|
||||
## Выполнение запросов в Airflow
|
||||
|
||||
Сохраните интерфейс `dds_transform`:
|
||||
Даг `dds_transform` ожидает от файлов следующие параметры и результаты:
|
||||
|
||||
| Файл | Вход | Результат |
|
||||
|---|---|---|
|
||||
@@ -38,39 +39,46 @@
|
||||
| `order_rebuild.sql` | `{first_day:Date}`, `{last_day:Date}`, `{load_id:String}` | выбранные дни в `dds.order_stage_dist` |
|
||||
| `order_replace.sql` | день в `{{ params.day }}` | партиция дня в `dds.order_rep` |
|
||||
|
||||
Объем состоит из текущего окна источника и хвоста от первого дня, который есть
|
||||
в ODS, но отсутствует в DDS. Текущее окно дают строки последнего
|
||||
`snapshot_date`: их минимальный и максимальный день заказа. Если источник
|
||||
пуст, `order_scope.sql` не возвращает строку. Если дыр нет, `first_day` равен
|
||||
началу окна. Если дыра старше, непрерывный отрезок начинается с нее.
|
||||
Сначала определите дни, за которые заказы еще могут измениться. Возьмите
|
||||
строки с последним `snapshot_date` и найдите минимальную и максимальную
|
||||
дату заказа. Этот диапазон далее называется текущим окном источника.
|
||||
|
||||
Затем найдите первый день, который есть в ODS, но отсутствует в DDS.
|
||||
Если он раньше начала текущего окна, начните расчет с него. Иначе начните
|
||||
с начала окна. Полученная дата станет `first_day`, а конец окна `last_day`.
|
||||
Один запуск пересчитывает все дни между ними. Если источник пуст,
|
||||
`order_scope.sql` не возвращает строку.
|
||||
|
||||
`order_rebuild.sql` сначала очищает все партиции `dds.order_stage_rep` на
|
||||
кластере, затем записывает в `dds.order_stage_dist` весь выбранный отрезок. Он
|
||||
читает текущее состояние `ods.order_v`, вычисляет `order_date`, переводит
|
||||
времена в `Europe/Samara`, разбирает `items` в три параллельных массива и
|
||||
ставит координаты запуска.
|
||||
заполняет служебные поля запуска.
|
||||
|
||||
Запись через Distributed должна завершиться до замены и использовать ключ
|
||||
целевой таблицы для раскладки строк. Способ собрать весь отрезок внутри файла
|
||||
выберите сами.
|
||||
Дождитесь записи всех строк на шарды через Distributed перед заменой партиций.
|
||||
Строки должны распределяться по ключу целевой таблицы. Способ рассчитать
|
||||
выбранные дни внутри файла выберите сами.
|
||||
|
||||
`order_replace.sql` работает с локальными `dds.order_rep` и
|
||||
`dds.order_stage_rep` на всем кластере. `REPLACE PARTITION` переносит готовую
|
||||
дневную партицию из донора и оставляет донор на месте. Структура, ключи и
|
||||
политика хранения совпадают: DDL объявляет донор через `AS dds.order_rep`.
|
||||
`dds.order_stage_rep` на всем кластере. `REPLACE PARTITION` заменяет дневную
|
||||
партицию основной таблицы готовой партицией из промежуточной. Промежуточная
|
||||
партиция при этом сохраняется. Структура, ключи и политика хранения таблиц
|
||||
совпадают: DDL создает промежуточную через `AS dds.order_rep`.
|
||||
|
||||
## Восстановите ближайший закрытый день
|
||||
## Восстановление пропущенного дня
|
||||
|
||||
Найдите последний по дате день DDS, который лежит левее начала текущего окна
|
||||
ODS. Это ближайший к окну закрытый день: его удаление показывает расширение
|
||||
объема на дыру без пересборки всей истории.
|
||||
Найдите самый поздний день DDS перед началом текущего окна ODS. Заказы за этот
|
||||
день уже не меняются, поэтому он считается закрытым. Удалив его, вы проверите,
|
||||
что ваш запрос заметил пропуск и включил этот день в расчет.
|
||||
|
||||
Если такого дня еще нет, один раз продвиньте мир через `world_next_day`,
|
||||
дождитесь `orders_ingest`, затем запустите `etl_pipeline`. После этого снова
|
||||
зафиксируйте модельное время до конца опыта и выберите ближайший закрытый день.
|
||||
оставьте модельное время неизменным до конца опыта и выберите этот день.
|
||||
|
||||
Сохраните для него число заказов и контрольную сумму деловых полей без
|
||||
`_load_id` и `_load_ts`. Удалите только его партицию:
|
||||
Сохраните для него число заказов и контрольную сумму их полей, исключив
|
||||
служебные `_load_id` и `_load_ts`. Контрольная сумма позволит проверить,
|
||||
что после восстановления значения остались прежними. Удалите только
|
||||
партицию выбранного дня:
|
||||
|
||||
```sql
|
||||
ALTER TABLE dds.order_rep ON CLUSTER clickstream_cluster
|
||||
@@ -90,29 +98,30 @@ DROP PARTITION 'YYYY-MM-DD';
|
||||
прогона число заказов и контрольная сумма выбранного дня должны совпасть с
|
||||
сохраненными.
|
||||
|
||||
## Проверьте раскладку и повтор
|
||||
## Распределение по шардам и повторный запуск
|
||||
|
||||
Напишите отдельный запрос к `dds.order_dist`, который получает фактическую
|
||||
ноду через `hostName()`, вычисляет ожидаемую по `cityHash64(order_id) % 2` и
|
||||
считает несовпадения по каждой ноде. Остаток 0 соответствует
|
||||
`clickhouse-01`, остаток 1 - `clickhouse-02`. На обеих нодах число строк на
|
||||
чужом шарде должно быть нулевым. Счетные DQ читают Distributed-таблицы и такую
|
||||
ошибку раскладки не обнаруживают.
|
||||
Напишите отдельный запрос к `dds.order_dist`, который получает фактический
|
||||
узел каждой строки через `hostName()`, вычисляет ожидаемый по
|
||||
`cityHash64(order_id) % 2` и считает несовпадения по каждому узлу.
|
||||
Остаток 0 соответствует `clickhouse-01`, остаток 1 - `clickhouse-02`.
|
||||
На обоих узлах число строк на чужом шарде должно быть нулевым.
|
||||
Проверки количества строк читают данные со всех шардов через Distributed
|
||||
и такую ошибку не обнаруживают.
|
||||
|
||||
Запустите `etl_pipeline` еще раз при неизменном модельном времени. Группа
|
||||
заказов снова должна пересобрать текущее изменяемое окно. Контрольные суммы
|
||||
деловых полей остаются прежними, а все семь проверок дают зеленый результат.
|
||||
полей заказов остаются прежними, а все семь проверок дают зеленый результат.
|
||||
|
||||
## Завершите опыт
|
||||
## Завершение работы
|
||||
|
||||
После восстановления партиции и успешных проверок снимите `world_live`
|
||||
с паузы, если он работал до упражнения.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] вы переписали три файла `sql/dds/order_*.sql`, сохранив остальные
|
||||
части стенда;
|
||||
- [ ] вы удалили ближайший к окну закрытый день и увидели ошибку DQ до
|
||||
- [ ] вы удалили ближайший к окну закрытый день и увидели ошибку проверки до
|
||||
восстановления;
|
||||
- [ ] ваш расчет выбрал `first_day` с удаленного дня и заменил партиции
|
||||
всего отрезка до конца окна;
|
||||
@@ -120,4 +129,4 @@ DROP PARTITION 'YYYY-MM-DD';
|
||||
с сохраненными значениями; они совпали;
|
||||
- [ ] `order_items_total`, `orders_vs_ods` и пять прежних проверок успешны;
|
||||
- [ ] ваш запрос с `hostName()` не нашел заказов на чужом шарде;
|
||||
- [ ] повторный `etl_pipeline` сохранил деловые данные, проверки прошли.
|
||||
- [ ] после повторного `etl_pipeline` поля заказов остались прежними, проверки прошли.
|
||||
@@ -1,9 +1,9 @@
|
||||
# Разберите сырые события рядом со стендом
|
||||
# Разбор сырых событий
|
||||
|
||||
**Чему учит:** пакетно разобрать сообщения отдельно от живого приема и
|
||||
построчно проверить свой результат по нетронутому ODS.
|
||||
В этой работе вы разберете сохраненные сообщения о событиях в свою таблицу.
|
||||
Каждую полученную строку сравните с результатом обработки в стенде.
|
||||
|
||||
## Сначала прочитать
|
||||
## Подготовка
|
||||
|
||||
- [формат события](../formats/clickstream-event.md);
|
||||
- [справочник хранилища](../architecture/storage.md), разделы про имена
|
||||
@@ -15,26 +15,26 @@
|
||||
Готовый стендовый разбор находится в `sql/ddl/30-ods-views.sql`. Откройте его
|
||||
после собственной сверки: в нем лежит ответ на основную часть задания.
|
||||
|
||||
## Что предстоит сделать
|
||||
## Порядок работы
|
||||
|
||||
Создайте в базе `default` одну свою таблицу годных событий и наполните ее одним
|
||||
пакетным `INSERT SELECT` из `stg.hits_raw_dist`. Работайте с первой нодой под
|
||||
пользователем `etl`.
|
||||
Создайте в базе `default` свою таблицу для правильно заполненных событий.
|
||||
Выберите небольшой набор сообщений из `stg.hits_raw_dist`, разберите их
|
||||
и загрузите в таблицу одним `INSERT SELECT`. Работайте с первым узлом под
|
||||
пользователем `etl`. Затем сравните результат с событиями в ODS.
|
||||
|
||||
Матвью `ods.event_mv` и `ods.event_errors_mv` продолжают принимать живой
|
||||
поток. Оставьте их, STG, ODS, Kafka и даги без изменений. Пересоздание матвью
|
||||
на работающем стенде оставило бы зазор в приеме, а для пакетного опыта оно не
|
||||
нужно.
|
||||
Материализованные представления `ods.event_mv` и `ods.event_errors_mv`
|
||||
продолжают обрабатывать поступающие сообщения. Оставьте их, STG, ODS, Kafka
|
||||
и даги без изменений. Если пересоздать представления на работающем стенде,
|
||||
часть сообщений может остаться без обработки.
|
||||
|
||||
Дайте своей таблице имя, которое не пересечется с чужой работой, и запишите
|
||||
его вместе с запросами. В конце вы удалите только эту таблицу.
|
||||
его вместе с запросами.
|
||||
|
||||
## Выберите сырой срез
|
||||
## Выбор исходных сообщений
|
||||
|
||||
STG хранит строки примерно три дня по `_load_ts`. Эта колонка отмечает время
|
||||
поступления в хранилище. Модельный день события лежит в `EventDate`. Сначала
|
||||
найдите среди недавних данных завершившийся интервал со строками. Посмотрите
|
||||
счет по минутам:
|
||||
поступления в хранилище. Модельный день события лежит в `EventDate`.
|
||||
Посмотрите, сколько строк поступило за каждую из последних минут:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
@@ -48,7 +48,8 @@ ORDER BY minute DESC;
|
||||
```
|
||||
|
||||
Выберите короткий интервал `[slice_from, slice_to)`, который уже закончился и
|
||||
дает удобное для построчной проверки число строк. Если одна минута велика,
|
||||
дает удобное для построчной проверки число строк. Этот набор строк дальше
|
||||
называется срезом. Если за одну минуту пришло слишком много строк,
|
||||
сузьте ее точными границами `_load_ts`. Если недавних строк нет, откройте
|
||||
Airflow. При включенном `world_live` посмотрите его текущий запуск и дочерний
|
||||
`world_live_day`: дождитесь свежих строк от текущего или следующего планового
|
||||
@@ -64,9 +65,9 @@ Airflow. При включенном `world_live` посмотрите его т
|
||||
|
||||
Во всех дальнейших запросах читайте `stg.hits_raw_dist` только с этими
|
||||
границами. Новые строки с более поздним `_load_ts` в срез не попадут. Повторите
|
||||
опыт до очистки по TTL; после нее прежний срез уже не восстановить из ODS.
|
||||
опыт до удаления исходных строк по TTL; после него прежний срез уже не восстановить из ODS.
|
||||
|
||||
## Соберите собственную таблицу
|
||||
## Таблица для результата
|
||||
|
||||
Таблица содержит все 47 полей источника с именами и типами
|
||||
`ods.event_rep`, а также `_load_ts`. Тип `purchaseRevenue` возьмите из ODS:
|
||||
@@ -75,14 +76,15 @@ Airflow. При включенном `world_live` посмотрите его т
|
||||
Обработайте повторные доставки так же, как ODS. Возьмите
|
||||
`ReplacingMergeTree(_load_ts)`, партицию `EventDate` и полный ключ замены:
|
||||
`(CounterID, EventDate, intHash32(ClientID), WatchID)`. Достаточно одной
|
||||
локальной таблицы: загрузка и сверка в этом опыте выполняются на одной ноде.
|
||||
локальной таблицы: загрузка и сверка в этом опыте выполняются на одном узле.
|
||||
|
||||
`WatchID` — логический идентификатор события, уникальный по
|
||||
[договору генератора](../specs/2026-08-01-generator.md). Физический ключ ODS
|
||||
шире: в `ReplacingMergeTree` версиями считаются строки с одинаковым полным
|
||||
ключом сортировки. Используйте его при загрузке и сверке.
|
||||
`WatchID` обозначает событие и уникален по
|
||||
[правилам генератора](../specs/2026-08-01-generator.md). Но `ReplacingMergeTree`
|
||||
считает версиями строки, у которых совпадает весь ключ сортировки.
|
||||
Поэтому для загрузки и сверки используйте все четыре части ключа ODS,
|
||||
перечисленные выше.
|
||||
|
||||
## Разберите годные строки
|
||||
## Разбор сообщений
|
||||
|
||||
Для каждой строки среза проверьте те же условия приема, что и на стенде:
|
||||
|
||||
@@ -92,8 +94,10 @@ Airflow. При включенном `world_live` посмотрите его т
|
||||
в ожидаемые типы.
|
||||
|
||||
Напишите разбор, который не прерывает пакет из-за одной грязной строки. Сами
|
||||
выберите способ безопасного извлечения пяти опорных полей, форму общих выражений
|
||||
и место фильтра. Остальные поля извлеките по описанию формата.
|
||||
выберите, как извлечь пять перечисленных полей без ошибки запроса, какие
|
||||
выражения вынести в общую часть и где отфильтровать неподходящие строки.
|
||||
Эти пять полей дальше называются опорными. Остальные поля извлеките
|
||||
по описанию формата.
|
||||
|
||||
Перенесите `_load_ts` из STG без пересчета: это время исходной доставки и
|
||||
колонка версии. Время запуска вашего запроса сюда не входит. Вставьте в свою
|
||||
@@ -114,25 +118,25 @@ Airflow. При включенном `world_live` посмотрите его т
|
||||
условия отбора на этом срезе, но не классификатор ошибок. Подкладывать грязные
|
||||
сообщения ради задания не требуется.
|
||||
|
||||
## Приведите стороны к одному зерну
|
||||
## Подготовка к сравнению
|
||||
|
||||
### Учтите повторные доставки
|
||||
### Повторные доставки
|
||||
|
||||
В STG одна строка представляет доставку. Повтор сообщения остается второй
|
||||
строкой. В `ods.event_v` одна строка представляет актуальное событие, потому
|
||||
что представление читает физическую таблицу с `FINAL`.
|
||||
|
||||
Свою сторону тоже читайте с `FINAL`. Ожидаемый счет равен числу различных
|
||||
полных ключей среди годных доставок. Разница между числом доставок и этим
|
||||
счетом показывает повторы в исходном срезе.
|
||||
Свою таблицу тоже читайте с `FINAL`. Число строк должно совпасть с числом
|
||||
различных полных ключей среди годных исходных сообщений. Разница между числом
|
||||
годных доставок и этим числом показывает повторы в исходном срезе.
|
||||
|
||||
### Выберите события ODS для сравнения
|
||||
### События ODS для сравнения
|
||||
|
||||
Теперь нужно найти в ODS события из выбранного среза. Список стройте по
|
||||
исходным доставкам: если взять его из своей таблицы, ошибочно отвергнутое
|
||||
событие исчезнет с обеих сторон сверки и потеря останется незамеченной.
|
||||
|
||||
Снова возьмите весь исходный STG-срез. По координатам Kafka исключите
|
||||
Снова возьмите все выбранные строки STG. По координатам Kafka исключите
|
||||
доставки, которые есть в `ods.event_errors_dist`. У оставшихся извлеките
|
||||
`WatchID` без риска прервать запрос и выберите эти события из свежего
|
||||
`ods.event_v`. Свое условие отбора здесь не применяйте: решение о годности
|
||||
@@ -141,36 +145,38 @@ Airflow. При включенном `world_live` посмотрите его т
|
||||
`WatchID` связывает доставку с событием в ODS. Для удаления повторов и
|
||||
сравнения полей дальше используйте полный физический ключ из схемы таблицы.
|
||||
|
||||
## Сверьте 47 полей
|
||||
## Сверка полей
|
||||
|
||||
Соедините свою таблицу и выбранные строки `ods.event_v` через `FULL OUTER
|
||||
JOIN` по полному ключу замены. Полное соединение сохраняет строки,
|
||||
которых нет на одной из сторон. Разделите результат на три исхода:
|
||||
которых нет на одной из сторон. Найдите три вида расхождений:
|
||||
|
||||
- строка есть только в ODS — вы ее потеряли;
|
||||
- строка есть только у вас — вы приняли лишнее или иначе разобрали часть
|
||||
ключа;
|
||||
- ключ есть с обеих сторон, но различается хотя бы одно из 47 полей.
|
||||
|
||||
ClickHouse по умолчанию заполняет отсутствующую сторону значениями типов.
|
||||
ClickHouse по умолчанию заполняет отсутствующую сторону значениями по умолчанию
|
||||
для соответствующих типов, например нулем или пустой строкой.
|
||||
Включите для сверки `join_use_nulls = 1` или добавьте явный признак присутствия
|
||||
в каждую сторону. Не определяйте отсутствие по значениям полей события.
|
||||
|
||||
Сравните все поля явно, включая массивы, `ecommerce` и точное десятичное
|
||||
`purchaseRevenue`. Удобный вид диагностики выберите сами: важно, чтобы по
|
||||
результату можно было назвать отличающиеся колонки одной строки. Выведите число
|
||||
строк каждого из трех исходов. В верном разборе все три числа равны нулю.
|
||||
`purchaseRevenue`. Выберите, как показать расхождения, чтобы было видно,
|
||||
какие колонки различаются в конкретной строке. Выведите число строк каждого
|
||||
из трех видов расхождений. В верном разборе все три числа равны нулю.
|
||||
|
||||
`_load_ts` не входит в 47 полей источника и не проверяет JSON-разбор.
|
||||
Проверьте его отдельно. Свяжите каждую строку своей таблицы с меткой одной из
|
||||
доставок того же полного ключа в исходном срезе. После `FINAL` остается самая
|
||||
поздняя метка этого ключа внутри среза. С ODS ее не сравнивайте: там может
|
||||
Проверьте его отдельно. Для каждой строки своей таблицы найдите самую позднюю
|
||||
метку доставки того же полного ключа в исходном срезе. Она должна совпасть
|
||||
с `_load_ts`, который вы читаете после `FINAL`. С ODS эту метку не сравнивайте: там может
|
||||
храниться более поздняя доставка того же события из-за границ среза.
|
||||
|
||||
## Расследуйте расхождение
|
||||
## Поиск ошибок разбора
|
||||
|
||||
Если сверка не пуста, возьмите одну строку. Вернитесь к независимому списку
|
||||
ключей и исходному `raw`, выведите рядом координаты Kafka и `_load_ts`.
|
||||
Если нашлись расхождения, выберите одну строку. Найдите ее ключ в списке,
|
||||
который вы получили из STG, и исходное сообщение `raw`. Выведите рядом
|
||||
координаты Kafka и `_load_ts`.
|
||||
|
||||
Для измененной строки назовите поля, которые расходятся, и сравните каждое с
|
||||
сырой строкой. Для пропавшей или лишней строки проверьте пять опорных полей и
|
||||
@@ -178,28 +184,28 @@ ClickHouse по умолчанию заполняет отсутствующую
|
||||
`ods.event_errors_dist`, посмотрите `error_class`.
|
||||
|
||||
Исправьте разбор, удалите и заново создайте только собственную таблицу, затем
|
||||
повторите вставку того же среза. Остановитесь, когда счет исходных доставок по
|
||||
прежним границам не изменился, число годных и отвергнутых строк в сумме дает
|
||||
число исходных доставок, а построчная сверка пуста.
|
||||
повторите вставку того же среза. Остановитесь, когда число исходных доставок по
|
||||
прежним границам не изменилось, число годных и отвергнутых строк в сумме дает
|
||||
число исходных доставок, а построчная сверка не находит расхождений.
|
||||
|
||||
## Чего не подтверждает сравнение с ODS
|
||||
## Ограничения проверки
|
||||
|
||||
`ods.event_v` — образец для сравнения. Оба разбора могли повторить одну
|
||||
ошибку. Для пяти опорных полей у вас есть проверка преобразования в нужный тип
|
||||
и исходный JSON. Остальные 42 поля можно вручную сопоставить с `raw`, однако
|
||||
второго независимого источника для всех значений на стенде нет.
|
||||
|
||||
## Уберите свой объект
|
||||
## Сохранение результата и удаление таблицы
|
||||
|
||||
Сохраните DDL таблицы, запрос разбора, границы среза, число исходных, годных
|
||||
и отвергнутых строк и итоговую сверку без расхождений. Запишите также, чего
|
||||
не подтверждает сравнение с ODS и как TTL ограничивает время для повторения
|
||||
опыта. Затем удалите свою таблицу командой `DROP TABLE` с ее точным именем.
|
||||
Убедитесь через `system.tables`, что объекта больше нет.
|
||||
Прикладные строки стенда вы не меняли, поэтому удалять данные из STG, ODS или
|
||||
Данные стенда вы не меняли, поэтому удалять строки из STG, ODS или
|
||||
Kafka после опыта не нужно.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] у вас сохранены границы выбранного среза, число строк и координаты
|
||||
доставки по партициям Kafka;
|
||||
|
||||
+32
-28
@@ -1,9 +1,10 @@
|
||||
# Мониторинг: куда смотреть
|
||||
|
||||
**Чему учит:** начать расследование с подходящего экрана, найти источник
|
||||
нагрузки и объяснить свежесть данных через путь доставки.
|
||||
Вы познакомитесь с дашбордами мониторинга и проведете два расследования:
|
||||
найдете запросы, которые создают нагрузку, и выясните, почему события сайта
|
||||
и заказы обновляются с разной частотой.
|
||||
|
||||
## Перед началом
|
||||
## Знакомство с дашбордами
|
||||
|
||||
Откройте Grafana и Airflow по адресам из раздела [«Адреса и
|
||||
пароли»](../../README.md#адреса-и-пароли). Для первого знакомства в Grafana
|
||||
@@ -11,7 +12,7 @@
|
||||
|
||||
- «Данные» — путь сообщений от источника до хранилища;
|
||||
- «Кластер» — процессор, память, диски и внутренняя работа ClickHouse;
|
||||
- «Запросы» — выполненные запросы, их стоимость и ошибки.
|
||||
- «Запросы» — выполненные запросы, затраченные на них ресурсы и ошибки.
|
||||
|
||||
Откройте все три дашборда и запишите по одному вопросу, на который каждый из
|
||||
них помогает ответить. «Данные» оставьте открытым в соседней вкладке.
|
||||
@@ -19,7 +20,7 @@
|
||||
Проведите два расследования на работающем стенде. Службы останавливать и данные
|
||||
портить не понадобится.
|
||||
|
||||
## Расследование 1. Кто читает больше всех
|
||||
## Расследование 1. Какие запросы читают больше всего данных
|
||||
|
||||
Начните в спокойный момент: не запускайте одновременно свои тяжелые запросы.
|
||||
Откройте дашборд «Запросы». На панели «Тяжёлые запросы: что прочитало больше
|
||||
@@ -27,8 +28,8 @@
|
||||
|
||||
Сохраните:
|
||||
|
||||
- границы окна и время запроса;
|
||||
- место запроса в списке, пользователя и ноду;
|
||||
- начало и конец выбранного периода, время запроса;
|
||||
- место запроса в списке, пользователя и узел;
|
||||
- текст запроса;
|
||||
- длительность, память, число прочитанных и отданных строк.
|
||||
|
||||
@@ -44,22 +45,25 @@
|
||||
В меню панели «Кто спрашивает: сколько запросов и какой у них аппетит»
|
||||
откройте Inspect, затем Query. Ответьте на два вопроса:
|
||||
|
||||
1. Почему журнал читают сразу с обеих нод?
|
||||
2. Почему из счета исключены строки начала запроса и работа шардов?
|
||||
1. Почему журнал читают сразу с обоих узлов?
|
||||
2. Почему при подсчете запросов исключают записи о начале выполнения и
|
||||
записи о выполнении частей запроса на шардах?
|
||||
|
||||
Проверьте второе объяснение по панели ошибок: ее фильтр не исключает записи
|
||||
шардов. Объясните, почему различаются вопросы панелей, а не только их SQL.
|
||||
шардов. Объясните, на какие разные вопросы отвечают эти две панели
|
||||
и зачем им разные условия отбора.
|
||||
|
||||
## Расследование 2. Как меняется свежесть заказов
|
||||
|
||||
Откройте дашборд «Данные». Панель «Свежесть: сколько прошло с последней
|
||||
доехавшей строки» показывает текущее число, а не историю. До наблюдения
|
||||
нарисуйте ожидаемую форму свежести событий и заказов в течение одного живого
|
||||
дня. Отдельно отметьте момент доставки слепка заказов.
|
||||
доехавшей строки» показывает, сколько времени прошло с последней загрузки.
|
||||
Истории значений на ней нет. До наблюдения нарисуйте, как, по вашему ожиданию,
|
||||
будут меняться эти числа для событий и заказов в течение одного запуска
|
||||
`world_live_day`. Отдельно отметьте ожидаемый момент доставки слепка заказов.
|
||||
|
||||
Если `world_live` снят с паузы, наблюдайте его текущий или следующий
|
||||
`world_live_day`: новый ручной запуск встанет в очередь. Если выключатель на
|
||||
паузе и активного живого дня нет, запустите один `world_live_day` вручную.
|
||||
`world_live_day`: новый ручной запуск встанет в очередь. Если `world_live` на
|
||||
паузе и активного `world_live_day` нет, запустите один `world_live_day` вручную.
|
||||
Модельные сутки занимают около 24 реальных минут.
|
||||
|
||||
Запишите несколько наблюдений до конца дня и сразу после него:
|
||||
@@ -69,29 +73,29 @@
|
||||
| | | | |
|
||||
|
||||
Дашборд обновляется раз в минуту, поэтому точного совпадения момента
|
||||
доставки со строкой таблицы ждать не нужно. Рост и сброс свежести заказов
|
||||
перенесите на свой набросок. Объясните форму линии: какую метку времени
|
||||
доставки с записью наблюдения ждать не нужно. Перенесите рост и сброс
|
||||
показателя заказов на свой рисунок. Объясните форму линии: какую метку времени
|
||||
вычитает панель из текущего времени, почему число растет и какое событие его
|
||||
сбрасывает. Модельную дату сыгранного дня подпишите отдельно от реального
|
||||
сбрасывает. Модельную дату наблюдаемого дня подпишите отдельно от реального
|
||||
времени наблюдения.
|
||||
|
||||
Подтвердите сброс двумя следами:
|
||||
Подтвердите, что сброс совпал с загрузкой заказов:
|
||||
|
||||
- в Airflow завершился прием слепка `orders_ingest`;
|
||||
- на панели «Дневной слепок заказов: какой доехал, когда и после какой паузы»
|
||||
появилась новая верхняя строка.
|
||||
|
||||
Если вы начали после доставки, дождитесь следующего дня либо честно запишите,
|
||||
какую часть формы увидели. Одно текущее значение не доказывает ни рост, ни
|
||||
Если вы начали после доставки, дождитесь следующего дня либо запишите,
|
||||
какую часть изменений успели увидеть. Одно текущее значение не доказывает ни рост, ни
|
||||
сброс.
|
||||
|
||||
## Объясните путь данных
|
||||
## Источники показателей
|
||||
|
||||
Не открывая справочник, объясните своими словами:
|
||||
|
||||
- почему свежесть считают в ODS, а поток — в STG;
|
||||
- почему время с последней загрузки считают по ODS, а число поступивших сообщений по STG;
|
||||
- почему знаменатель доли брака берут из STG;
|
||||
- почему у числителя и знаменателя доли брака одно окно.
|
||||
- почему числитель и знаменатель доли брака берут за один период.
|
||||
|
||||
Свяжите ответы с тем, что вы видели на панелях. Затем сверьте объяснение с
|
||||
[главой о мониторинге](../handbook/monitoring.md) и справочниками дашбордов
|
||||
@@ -99,20 +103,20 @@
|
||||
[«Данные»](../architecture/monitoring/data.md). Устройство живого дня и
|
||||
доставки слепка описано в [главе об Airflow](../handbook/airflow-dags.md).
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] для каждого из трех дашбордов записан вопрос, на который он отвечает;
|
||||
- [ ] у вас сохранены окно, текст и показатели верхнего запроса;
|
||||
- [ ] у вас сохранены выбранный период, текст и показатели верхнего запроса;
|
||||
вы выяснили, кто его отправил и зачем;
|
||||
- [ ] вы нашли запрос открытых дашбордов, который прочитал больше всего
|
||||
строк, и панель, которая его отправила;
|
||||
- [ ] вы можете объяснить, зачем журнал собирают с двух нод и какие записи
|
||||
- [ ] вы можете объяснить, зачем журнал собирают с двух узлов и какие записи
|
||||
учитывают при подсчете запросов;
|
||||
- [ ] у вас есть прогноз свежести, сделанный до наблюдения, и несколько
|
||||
замеров; рост и сброс на рисунке объяснены через время последней доставки;
|
||||
- [ ] вы подтвердили доставку заказов в Airflow и на панели слепков,
|
||||
подписали модельную дату отдельно от реального времени наблюдений;
|
||||
- [ ] вы можете объяснить выбор слоев для свежести, потока и доли брака
|
||||
и почему числитель и знаменатель берут за одно окно;
|
||||
и почему числитель и знаменатель берут за один период;
|
||||
- [ ] после наблюдений службы продолжают работать, данные и стендовые
|
||||
дашборды остались без изменений.
|
||||
+124
-98
@@ -1,84 +1,121 @@
|
||||
# Учебные задания
|
||||
|
||||
Программа состоит из двух частей: основная включает семь ступеней, которые вы
|
||||
проходите по порядку, а факультативы выбираете по желанию. Причины такого
|
||||
порядка и формы заданий описаны в [программе учебных лаб](../specs/2026-09-01-labs-program.md#2-устройство-программы-основная-программа-и-факультативы).
|
||||
Вы будете достраивать хранилище небольшого интернет-магазина. Стенд уже
|
||||
загружает события сайта и заказы, обрабатывает их и строит несколько отчетов.
|
||||
Сначала вы подготовите данные для новых отчетов, затем разберетесь, как
|
||||
работают расчеты и загрузка, и перепишете часть из них.
|
||||
|
||||
## Основная программа
|
||||
Основная программа состоит из семи ступеней. Проходите их по порядку,
|
||||
факультативы выбирайте по желанию. Перед первым заданием прочитайте главу
|
||||
[«Хранилище и его слои»](../handbook/storage-layers.md) и выполните запросы
|
||||
из нее: они покажут данные, с которыми предстоит работать.
|
||||
|
||||
| Ступень | Задание | Чему учит |
|
||||
## Порядок заданий
|
||||
|
||||
| Ступень | Задания | Содержание |
|
||||
|---|---|---|
|
||||
| 1 | Пять витрин слоя DM | построить результат для потребителя хранилища; уроки каждой витрины — в таблице ниже |
|
||||
| 2 | [Свой график в Superset](2-1-superset-chart.md) | превратить свой результат из хранилища в отчет в BI |
|
||||
| 3 | [Нарежьте сессии сами](3-1-sessions-by-hand.md) | сверить число сессий в своей нарезке с эталоном и найти события, из-за которых оно расходится |
|
||||
| 4 | [Перепишите нарезку стенда](4-1-session-pipeline.md) | переписать расчет сессий в готовом конвейере и проверить по исходным событиям |
|
||||
| 5 | [Научите проверки замечать ошибки в заказах](5-1-order-checks.md)<br>[Перепишите разбор заказов в ODS](5-2-order-parsing.md)<br>[Перепишите сборку заказов в DDS](5-3-order-pipeline.md) | написать проверки качества до рискованного изменения, затем переписать разбор и сборку заказов |
|
||||
| 6 | [Разберите сырые события рядом со стендом](6-1-event-parsing.md) | изучить разбор потока отдельно от живого приема и сверить его со стендовым результатом |
|
||||
| 7 | [Мониторинг: куда смотреть](7-1-monitoring.md) | выбрать, куда и на что смотреть в живой системе |
|
||||
| 1 | Пять витрин для отчетов | подготовить данные о событиях, страницах, визитах, ошибках загрузки и рекламе; задания перечислены ниже |
|
||||
| 2 | [Свой график в Superset](2-1-superset-chart.md) | построить график по своей витрине и проверить числа на нем |
|
||||
| 3 | [Сессии из событий](3-1-sessions-by-hand.md) | объединить действия посетителя в визиты и сравнить их с визитами источника |
|
||||
| 4 | [Расчет сессий в стенде](4-1-session-pipeline.md) | включить свой расчет в готовую обработку данных и проверить результат |
|
||||
| 5 | [Проверки качества заказов](5-1-order-checks.md)<br>[Разбор заказов в ODS](5-2-order-parsing.md)<br>[Сборка заказов в DDS](5-3-order-pipeline.md) | сначала написать проверки качества, затем с их помощью проверить свою обработку заказов |
|
||||
| 6 | [Разбор сырых событий](6-1-event-parsing.md) | извлечь поля из сообщений и сравнить результат с готовым разбором стенда |
|
||||
| 7 | [Мониторинг: куда смотреть](7-1-monitoring.md) | найти тяжелые запросы и разобраться, почему данные обновляются с разной частотой |
|
||||
|
||||
## Ступень 1: пять витрин слоя DM
|
||||
## Ступень 1: витрины для отчетов
|
||||
|
||||
Слой DM в стенде построен наполовину: витрины трех сюжетов дашборда и контроль
|
||||
качества уже построены, остальные пять витрин строите вы. Каждая из них —
|
||||
самостоятельное задание: в нем есть состав витрины, зерно, источники и способ
|
||||
проверки.
|
||||
Витрина готовит данные для конкретного отчета. В стенде они находятся в базе
|
||||
`dm`: это слой DM. Витрины выручки, трафика, сверки покупок с заказами и
|
||||
сводка качества уже готовы. Вы добавите еще пять.
|
||||
|
||||
| № | Результат | Чему учит |
|
||||
| № | Витрина | Содержание задания |
|
||||
|---|---|---|
|
||||
| 1 | [`events_enriched_v`](1-1-events-enriched.md) | подготовить удобный для анализа набор полей, сохранив одну строку на событие |
|
||||
| 2 | [`top_pages_daily`](1-2-top-pages-daily.md) | создавать материализованную витрину с дневной пересборкой в конвейере |
|
||||
| 3 | [`session_overview`](1-3-session-overview.md) | хранить слагаемые для расчета средних и долей |
|
||||
| 4 | [`dq_errors_daily`](1-4-dq-errors-daily.md) | считать брак по дню загрузки и хранить итоги дольше источника |
|
||||
| 5 | [`utm_effectiveness`](1-5-utm-effectiveness.md) | читать `declared_` как предупреждение о сумме со слов клиента |
|
||||
| 1 | [События для анализа: `events_enriched_v`](1-1-events-enriched.md) | выбрать нужные поля и добавить вычисляемые, сохранив одну строку на событие |
|
||||
| 2 | [Показатели страниц по дням: `top_pages_daily`](1-2-top-pages-daily.md) | сохранить итоги в таблице и настроить их обновление через Airflow |
|
||||
| 3 | [Визиты по устройствам: `session_overview`](1-3-session-overview.md) | хранить суммы и счетчики для расчета средних и долей |
|
||||
| 4 | [Ошибки загрузки по дням: `dq_errors_daily`](1-4-dq-errors-daily.md) | сохранить число ошибок даже после удаления исходных строк |
|
||||
| 5 | [Показатели рекламы: `utm_effectiveness`](1-5-utm-effectiveness.md) | различать суммы покупок из событий сайта и выручку по заказам магазина |
|
||||
|
||||
Общее устройство слоя — [справочник DM](../architecture/dm.md). Он отвечает на
|
||||
вопросы «почему так», задание отвечает на вопрос «что сделать».
|
||||
Начните с [первого задания](1-1-events-enriched.md): для него достаточно
|
||||
одного представления. В заданиях 2–5 понадобятся таблицы и их обновление
|
||||
по дням. К общим правилам ниже возвращайтесь по мере работы.
|
||||
|
||||
## Как принято на ступени 1
|
||||
## Создание и обновление витрин
|
||||
|
||||
**Читать — через `_v`.** Физические `_rep` и `_dist` служат загрузке и
|
||||
обслуживанию, а публичный договор DDS — `dds.event_v` или `dds.session_v` —
|
||||
остается прежним даже при изменении физических объектов. Чтение DDS
|
||||
напрямую делает задания независимыми: соседняя витрина может быть еще не
|
||||
построена.
|
||||
Исключение — задание 4: у таблиц брака публичного договора нет.
|
||||
### Источники данных
|
||||
|
||||
**Писать — в `_dist`.** Так строки распределяются по ключу шардирования. При
|
||||
записи в локальную таблицу распределение зависит от узла, выполнившего
|
||||
запрос, и при следующем запуске может измениться. Подробности — раздел
|
||||
«Раскладка по шардам» [справочника хранилища](../architecture/storage.md).
|
||||
События читайте из `dds.event_v`, сессии из `dds.session_v`. Суффикс `_v`
|
||||
обозначает обычное представление: оно выполняет сохраненный запрос при
|
||||
чтении. Такие представления сохраняют привычные имена и состав колонок,
|
||||
когда внутреннее устройство таблиц меняется.
|
||||
|
||||
**Форма — по зерну.** На стенде агрегат с новым зерном принято хранить
|
||||
готовым: потребитель читает его много раз без повторного расчета по
|
||||
источнику. Создайте `dm.<имя>_rep` / `_dist`, донор `dm.<имя>_stage_rep` /
|
||||
`_stage_dist` и публичное `dm.<имя>_v` с явным списком колонок. При прежнем
|
||||
зерне достаточно представления: таблица дублировала бы данные. Доводы —
|
||||
раздел «Таблица или представление» [справочника
|
||||
DM](../architecture/dm.md).
|
||||
Каждая учебная витрина читает готовый источник и не зависит от других
|
||||
учебных витрин. В задании 4 источниками служат таблицы ошибок ODS:
|
||||
представлений для них нет.
|
||||
|
||||
**Ключ шардирования — ваше решение.** Объясните, почему выбранный ключ не
|
||||
дает существенного перекоса и почему более сложный ничего не добавит.
|
||||
Объявите донор через `AS <цель>_rep` с ключом цели: иначе замена партиции
|
||||
потеряет строку на одном шарде и удвоит на другом. Детали — раздел
|
||||
«Раскладка по шардам» [справочника хранилища](../architecture/storage.md).
|
||||
Пример обоснования — `dm.revenue_daily_dist` в
|
||||
### Таблицы и представления
|
||||
|
||||
В заданиях 2–5 вы группируете исходные строки. Например, в `top_pages_daily`
|
||||
одна строка содержит итоги для одной страницы за день. То, чему соответствует
|
||||
одна строка, называют **зерном**. На стенде итоги с новым зерном сохраняют
|
||||
в таблицах. Отчеты читают готовые числа.
|
||||
|
||||
Новый расчет сначала записывают в промежуточную таблицу. Пока он готовится,
|
||||
отчеты читают прежние данные из основной. Затем готовыми разделами
|
||||
промежуточной таблицы заменяют разделы основной. Раздел называется
|
||||
**партицией**; в этих заданиях он содержит данные одного дня.
|
||||
Промежуточную таблицу также называют **донором**.
|
||||
|
||||
На примере `top_pages_daily` пять объектов выглядят так:
|
||||
|
||||
| Объект | Для чего нужен |
|
||||
|---|---|
|
||||
| `dm.top_pages_daily_rep` | Хранит рассчитанные строки на каждом шарде |
|
||||
| `dm.top_pages_daily_dist` | Читает строки со всех шардов и распределяет запись между ними |
|
||||
| `dm.top_pages_daily_stage_rep` | Хранит новый расчет до замены данных в основной таблице |
|
||||
| `dm.top_pages_daily_stage_dist` | Распределяет запись нового расчета между шардами |
|
||||
| `dm.top_pages_daily_v` | Дает отчетам доступ к готовой витрине; явно перечисляет ее колонки |
|
||||
|
||||
### Распределение по шардам
|
||||
|
||||
Каждый из двух шардов стенда хранит свою часть данных. Записывайте строки
|
||||
через таблицы `_dist`: они выбирают шард по ключу шардирования. Прямая запись
|
||||
в `_rep` оставит строки на том узле, где выполнен запрос.
|
||||
|
||||
Ключ для своей витрины выберите сами. Проверьте распределение строк между
|
||||
шардами и объясните, почему выбранного ключа достаточно.
|
||||
Пример обоснования для `dm.revenue_daily_dist` есть в
|
||||
[`sql/ddl/55-dm-tables.sql`](../../sql/ddl/55-dm-tables.sql).
|
||||
|
||||
**Где лежит код.** DDL заданий — в `sql/ddl/70-labs.sql`.
|
||||
При создании мира он применяется после стендовых файлов.
|
||||
В уже работающем стенде сохранение файла и повторный `make up` новый DDL
|
||||
не применят — выполните его сами, как описано ниже. В
|
||||
`sql/dm/<витрина>_scope.sql` определяются дни прогона, в `_rebuild.sql` они
|
||||
собираются в донор, в `_replace.sql` заменяется партиция. Задачу сборки
|
||||
добавьте в [`dags/dm_transform.py`](../../dags/dm_transform.py): без нее
|
||||
конвейер не обновит витрину. Представлению достаточно `CREATE VIEW`.
|
||||
Создайте промежуточную таблицу через `AS <цель>_rep`, сохранив структуру и
|
||||
ключи основной. У обеих таблиц `_dist` должен быть одинаковый ключ
|
||||
шардирования: замена партиции выполняется внутри каждого шарда и не
|
||||
перераспределяет строки. Подробности есть в разделе
|
||||
[«Раскладка по шардам»](../architecture/storage.md#раскладка-по-шардам).
|
||||
|
||||
**Образец — витрина `revenue_daily`.** У нее есть пять объектов, донор,
|
||||
сборка, замена дневных партиций и задача в конвейере. Используйте ее как
|
||||
образец всего порядка пересборки. Состав колонок берите из своего ТЗ, иначе
|
||||
получите показатели другой витрины.
|
||||
### SQL-файлы и запуск расчета
|
||||
|
||||
## Как применить свой DDL
|
||||
Команды создания таблиц и представлений (DDL) сохраняйте в
|
||||
`sql/ddl/70-labs.sql`. При создании мира этот файл выполняется после
|
||||
стендовых файлов. В работающем стенде примените команды сами по инструкции
|
||||
ниже: сохранение файла и повторный `make up` их не выполнят.
|
||||
|
||||
Для витрины с таблицами нужны три файла в `sql/dm/`:
|
||||
|
||||
- `<витрина>_scope.sql` определяет, какие дни пересчитать;
|
||||
- `<витрина>_rebuild.sql` рассчитывает их и записывает в промежуточную таблицу;
|
||||
- `<витрина>_replace.sql` заменяет партицию одного дня в основной таблице.
|
||||
|
||||
Добавьте запуск расчета в [`dags/dm_transform.py`](../../dags/dm_transform.py),
|
||||
чтобы витрина обновлялась вместе с остальными. Для представления из задания 1
|
||||
достаточно выполнить `CREATE VIEW`.
|
||||
|
||||
Образец этого порядка работы есть у готовой витрины `revenue_daily`.
|
||||
Ее таблицы, SQL-файлы и задача Airflow разобраны в
|
||||
[справочнике DM](../architecture/dm.md#dmrevenue_daily_v).
|
||||
Состав колонок и выбор дней для пересчета берите из своего задания.
|
||||
|
||||
## Выполнение SQL в DBeaver
|
||||
|
||||
В DBeaver создайте подключение по [инструкции в README](../../README.md#подключиться-из-dbeaver),
|
||||
но укажите пользователя `etl` и пароль из `CLICKHOUSE_ETL_PASSWORD` в `.env`.
|
||||
@@ -88,37 +125,33 @@ DM](../architecture/dm.md).
|
||||
Создайте файл `sql/ddl/70-labs.sql` и сохраняйте в нем DDL своих витрин.
|
||||
Откройте его в SQL-редакторе этого подключения и выполните DDL текущего
|
||||
задания. Создавайте объекты с
|
||||
`ON CLUSTER clickstream_cluster`, чтобы они появились на обеих нодах;
|
||||
`ON CLUSTER clickstream_cluster`, чтобы они появились на обоих узлах;
|
||||
образец для представления — [`sql/ddl/60-dm-views.sql`](../../sql/ddl/60-dm-views.sql).
|
||||
Сохраненный файл позволит воссоздать ваши объекты при пересборке мира.
|
||||
|
||||
## Самопроверка ступени 1
|
||||
## Проверка витрин
|
||||
|
||||
Даг `homework_check` запускается руками, расписания у него нет. Задача
|
||||
витрины красная, пока витрины нет или счет не сходится, и зеленая, когда
|
||||
собрано верно. В сводку качества конвейера `dm.dq_summary` он не пишет: это
|
||||
инструмент лабы, а не история качества стенда.
|
||||
В Airflow есть даг `homework_check` с отдельной задачей для каждой учебной
|
||||
витрины. Запускайте его вручную: расписания у него нет. Задача завершится
|
||||
ошибкой, если витрина еще не создана или ее данные расходятся с ожидаемыми.
|
||||
Причину и примеры расхождений ищите в журнале задачи. Проверки еще не
|
||||
выполненных заданий тоже будут красными.
|
||||
|
||||
Для каждой витрины проверка независимо рассчитывает ожидаемый результат
|
||||
прямо по ее источнику. У четырех заданий источник
|
||||
эталона тот же, что у витрины, `dds.session_v` или `dds.event_v`; у задания
|
||||
4 это таблицы брака ODS, потому что в DDS нет сущности брака. Обе стороны
|
||||
соединяются по деловому ключу через `FULL OUTER JOIN`, и считается число
|
||||
строк, где стороны разошлись или одной стороны нет. Ноль — зеленый. Полное
|
||||
соединение сохраняет ключи обеих сторон: при внутреннем пропавший день или
|
||||
ключ не попал бы в результат, и потеря осталась бы незамеченной.
|
||||
Проверка заново рассчитывает показатели по источнику и сравнивает их с вашей
|
||||
витриной. Строки сопоставляются по полям, которые их определяют: например,
|
||||
по дню и странице. `FULL OUTER JOIN` позволяет найти и разные значения, и
|
||||
строки, которых нет с одной стороны. `INNER JOIN` пропустил бы отсутствующие
|
||||
строки. Когда расхождений нет, задача становится зеленой.
|
||||
|
||||
Раздел «Как проверить себя» каждого задания по витрине называет деловой ключ и
|
||||
сравниваемые величины. Тот же прием в стендовом исполнении — [даг
|
||||
`dq_check`](../../dags/dq_check.py) и запросы в [`sql/dq/`](../../sql/dq).
|
||||
`homework_check` не сохраняет результаты в сводку качества `dm.dq_summary`.
|
||||
|
||||
Запросы проверок лежат в `sql/dq/`. Откройте их после собственного решения:
|
||||
Запросы проверок лежат в [`sql/dq/`](../../sql/dq/). Откройте их после собственного решения:
|
||||
они заново рассчитывают показатели витрины, поэтому содержат ответ на
|
||||
существенную часть задания. Сравните свой способ расчета с готовым.
|
||||
|
||||
## Факультативы
|
||||
## Задания по желанию
|
||||
|
||||
Эти задания вы берете по желанию. Полный список сюжетов — в разделе
|
||||
Полный список сюжетов — в разделе
|
||||
[«Факультативы — пять разделов»](../specs/2026-09-01-labs-program.md#4-факультативы--пять-разделов)
|
||||
программы.
|
||||
|
||||
@@ -129,16 +162,17 @@ DM](../architecture/dm.md).
|
||||
- [Куда пропали покупки](optional/missing-purchases.md): найти заказы без
|
||||
события, выбрать источник витрины и условие проверки полноты.
|
||||
- [Почему повтор покупки завышает выручку](optional/duplicate-purchases.md):
|
||||
выбрать зерно и ключ расчета, проверить его на повторных событиях.
|
||||
рассчитать выручку с одной строкой на заказ и проверить результат на
|
||||
повторных событиях.
|
||||
- [Какая позиция объясняет разницу в сумме](optional/order-amount-difference.md):
|
||||
разобрать позиции без потери точности и локализовать расхождение источников.
|
||||
разобрать товары заказа без потери точности и найти причину разницы в суммах.
|
||||
- [Почему вчерашняя выручка меняется сегодня](optional/revenue-over-time.md):
|
||||
обосновать окно пересчета витрины и требования к истории заказов.
|
||||
определить, за какие дни пересчитывать выручку и какую историю заказов сохранять.
|
||||
|
||||
Первые три работы используют готовые данные. Четвертая продвигает мир через
|
||||
Airflow; для нее вы сохраняете состояния заказов после каждого модельного дня.
|
||||
|
||||
### Тонкий анализ данных
|
||||
### Данные о посетителях
|
||||
|
||||
Открывается после ступени 1.
|
||||
|
||||
@@ -146,9 +180,7 @@ Airflow; для нее вы сохраняете состояния заказо
|
||||
сохранить историю связей, сравнить расчеты на прежних сессиях и определить,
|
||||
какие сведения нужны для воспроизведения результата.
|
||||
|
||||
Обогащение отчета своим словарем регионов пока описано только в программе.
|
||||
|
||||
### Superset и BI
|
||||
### Отчеты в Superset
|
||||
|
||||
Открывается после ступени 2. Два самостоятельных задания:
|
||||
|
||||
@@ -157,21 +189,15 @@ Airflow; для нее вы сохраняете состояния заказо
|
||||
- [В DBeaver работает, в Superset нет](optional/superset-missing-object.md):
|
||||
выяснить, почему клиенты по-разному видят один объект хранилища.
|
||||
|
||||
### Инженерия стенда
|
||||
### Загрузка и обновление данных
|
||||
|
||||
Открывается после ступени 1. Прием и хранение данных, раскладка по шардам,
|
||||
повторная обработка дня и обновление витрин.
|
||||
Открывается после ступени 1.
|
||||
|
||||
- [Как обновлять витрину без полной пересборки](optional/incremental-daily-traffic.md):
|
||||
продолжить работу о связях пользователей, пересчитать затронутые дни
|
||||
и проверить повтор незавершенной загрузки. Повышенная сложность.
|
||||
|
||||
### Что делать, когда стенд сломался
|
||||
|
||||
Открывается после ступени 7. Поломки стенда и инструкции по восстановлению
|
||||
по итогам расследований; весь раздел — повышенная сложность.
|
||||
|
||||
## Уборка
|
||||
## Удаление временных объектов
|
||||
|
||||
После опытов удаляйте свои временные объекты. Если удалили учебную витрину,
|
||||
ее задача в `homework_check` должна снова завершиться ошибкой: отсутствие
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
# Почему повтор покупки завышает выручку
|
||||
|
||||
**Чему учит:** выбрать зерно и ключ расчета, чтобы повторные события
|
||||
не умножали денежную сумму.
|
||||
Несколько событий покупки могут относиться к одному заказу. Вы проверите,
|
||||
как они завышают сумму, и составите расчет, который учитывает каждый заказ
|
||||
один раз.
|
||||
|
||||
Факультатив доступен после ступени 1. Работайте в DBeaver пользователем
|
||||
`analyst` по [инструкции](../../../README.md#подключиться-из-dbeaver).
|
||||
Дождитесь завершения `etl_pipeline`; оставьте `world_live` на паузе и
|
||||
дождитесь уже начатых прогонов, чтобы данные не менялись между запросами.
|
||||
|
||||
## Проверьте расчет по событиям
|
||||
## Расчет по событиям покупки
|
||||
|
||||
В ODS уже устранены повторные доставки событий. Достаточно ли этого,
|
||||
чтобы сложить суммы всех `purchase` для витрины заказов? Проверьте на данных.
|
||||
|
||||
Возьмите один закрытый день: его дата должна быть не позднее
|
||||
Возьмите день, за который уже получены все слепки заказов: его дата должна быть не позднее
|
||||
`max(snapshot_date) - 6` из `ods.order_v`. Во всех запросах к `dds.event_v`
|
||||
отбирайте этот `event_date` и `event_type = 'purchase'`.
|
||||
|
||||
@@ -22,12 +23,12 @@
|
||||
|
||||
Сначала посчитайте число событий и сумму всех заявленных сумм. Затем найдите
|
||||
номера заказов, которые встречаются несколько раз. Если таких нет, выберите
|
||||
другой закрытый день.
|
||||
другой день с полученными слепками.
|
||||
|
||||
Для двух повторившихся покупок выпишите `event_id`, `client_id`, `event_time`,
|
||||
номер заказа и сумму. Сопоставьте число событий и заказов в `dds.order_v`.
|
||||
|
||||
## Соберите расчет по заказам
|
||||
## Расчет по заказам
|
||||
|
||||
Напишите запрос с одной строкой на номер заказа. Сохраните в результате
|
||||
номер, число исходных событий и одну заявленную сумму. До выбора суммы
|
||||
@@ -38,7 +39,7 @@
|
||||
разных номеров в исходных событиях. Отдельно проверьте свое решение на случае
|
||||
двух разных заказов с одинаковой суммой: сколько денег оно учтет?
|
||||
|
||||
## Найдите заказы, из-за которых сумма завышена
|
||||
## Заказы с повторными событиями
|
||||
|
||||
Возьмите один повторившийся номер заказа. Сколько денег он добавил в первый
|
||||
расчет и сколько — во второй? Подтвердите разность его событиями.
|
||||
@@ -58,22 +59,23 @@
|
||||
его статус и [порядок присвоения классов](../../architecture/dm.md#dmpurchase_vs_orders_v).
|
||||
Класс витрины не заменяет поиск всех повторов в событиях.
|
||||
|
||||
## Определите границы расчета
|
||||
## Сравнение с выручкой магазина
|
||||
|
||||
Сложите `revenue` из `dm.revenue_daily_v` за тот же `report_date` по всем
|
||||
категориям. Эта витрина считает стоимость позиций оплаченных заказов,
|
||||
без скидки и доставки.
|
||||
|
||||
Сравните результат с исправленной клиентской суммой. Совпадение не требуется:
|
||||
Сравните результат с суммой по событиям после устранения повторов. Совпадение не требуется:
|
||||
удаление повторов устраняет одну причину расхождения. Найдите хотя бы один
|
||||
заказ, который объясняет, почему деньги трекера и магазина все еще могут
|
||||
различаться. Для этого сопоставьте события, заказ и готовую сверку.
|
||||
|
||||
Рядом со своим запросом укажите его зерно и ключ. Обоснуйте, где нужна одна
|
||||
строка на событие, а где — одна на заказ: следует ли ради этого расчета
|
||||
Рядом с запросом укажите, чему соответствует одна строка результата
|
||||
и какие поля ее определяют. Обоснуйте, где нужна одна строка на событие,
|
||||
а где одна на заказ: следует ли ради этого расчета
|
||||
удалять повторные покупки из `dds.event_v`? Используйте найденные `event_id`.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] сохранен запрос с одной строкой на заказ, числом событий и суммой;
|
||||
- [ ] уникальность ключа и полнота набора номеров проверены по источнику;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Как новые заказы меняют отчет о посетителях
|
||||
|
||||
**Чему учит:** сохранять историю связей посетителей с покупателями
|
||||
и использовать ее для воспроизведения прежнего расчета.
|
||||
Вы сохраните историю связей посетителей сайта с покупателями магазина.
|
||||
На ее основе проверите, как новые связи меняют старые отчеты и что нужно,
|
||||
чтобы повторить прежний расчет.
|
||||
|
||||
Факультатив доступен после ступени 1. Понадобятся Airflow и DBeaver с
|
||||
подключением `student` по [инструкции](../../../README.md#подключиться-из-dbeaver).
|
||||
@@ -9,13 +10,14 @@
|
||||
ваши таблицы будут доступны только на узле этого подключения.
|
||||
Если предложенное имя таблицы занято, выберите свое.
|
||||
|
||||
В событиях сайта посетитель обозначен кукой `client_id`, а в заказах
|
||||
покупатель — `user_id`. Сопоставив событие покупки с заказом, можно связать
|
||||
эти идентификаторы. Тогда и к прежним визитам этой куки можно добавить
|
||||
В событиях сайта посетитель обозначен `client_id` из cookie браузера,
|
||||
а в заказах покупатель обозначен `user_id`. Сопоставив событие покупки
|
||||
с заказом, можно связать эти идентификаторы. Тогда и к прежним визитам
|
||||
этого `client_id` можно добавить
|
||||
пользователя магазина. Вы проверите, как появление такой связи меняет число
|
||||
известных пользователей в отчете за прошедший день.
|
||||
|
||||
## Сопоставьте посетителей с покупателями
|
||||
## Связи посетителей с покупателями
|
||||
|
||||
Поставьте `world_live` на паузу, дождитесь завершения начатых прогонов
|
||||
генерации и обработки. Запустите `etl_pipeline` и дождитесь успеха.
|
||||
@@ -32,7 +34,8 @@
|
||||
найдите самое раннее время события покупки, которое удалось сопоставить
|
||||
с заказом. Назовите эту колонку `first_purchase_at`.
|
||||
В событии `purchase` массив `purchase_id` содержит один номер заказа.
|
||||
Статус заказа не ограничивайте: отмена покупки не меняет владельца куки.
|
||||
Статус заказа не ограничивайте: отмена покупки не меняет найденную связь
|
||||
посетителя с покупателем.
|
||||
|
||||
Источники распределены по разным ключам. Для соединения используйте
|
||||
`GLOBAL INNER JOIN`, заключив выборки событий и заказов в подзапросы.
|
||||
@@ -43,7 +46,7 @@
|
||||
Для одной пары покажите номера заказов и события, по которым получили
|
||||
`first_purchase_at`.
|
||||
|
||||
## Сохраните список связей после каждого модельного дня
|
||||
## История связей
|
||||
|
||||
Создайте таблицу:
|
||||
|
||||
@@ -84,7 +87,7 @@ SELECT max(event_date) AS observation_day FROM dds.event_v;
|
||||
не устраняет повторы вставки. Сохраните и проверьте список перед тем,
|
||||
как запускать следующий модельный день.
|
||||
|
||||
## Сравните дату покупки с датой появления связи
|
||||
## Дата покупки и дата появления связи
|
||||
|
||||
Сравните списки за соседние даты: H0 с H1, затем H1 с H2. Найдите пары,
|
||||
которых не было в прежнем списке. Для нескольких таких пар покажите
|
||||
@@ -101,7 +104,7 @@ SELECT max(event_date) AS observation_day FROM dds.event_v;
|
||||
у вас нет более ранних копий списка. Для новой пары можно установить
|
||||
интервал между двумя сохранениями, но не точное время внутри него.
|
||||
|
||||
## Пересчитайте отчет за прежние дни
|
||||
## Пересчет прежних отчетов
|
||||
|
||||
Возьмите один и тот же набор сессий из `dds.session_v` за даты до H0
|
||||
включительно. Постройте два дневных расчета:
|
||||
@@ -128,7 +131,7 @@ SELECT max(event_date) AS observation_day FROM dds.event_v;
|
||||
На примере найденных различий объясните, какие данные нужно сохранять,
|
||||
чтобы повторить прежний расчет.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
У вас есть запрос сопоставления посетителей с покупателями, три сохраненных
|
||||
списка связей и сравнение двух расчетов на одном наборе сессий. На конкретных
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Как обновлять витрину без полной пересборки
|
||||
|
||||
**Чему учит:** выбирать дни для пересчета по новым данным, проверять результат
|
||||
и повторять незавершенную загрузку.
|
||||
Вы настроите обновление витрины только за дни, затронутые новыми данными.
|
||||
Затем проверите, можно ли продолжить загрузку, которая успела записать
|
||||
лишь часть результатов.
|
||||
|
||||
Факультатив повышенной сложности продолжает работу
|
||||
[«Как новые заказы меняют отчет о посетителях»](identity-over-time.md).
|
||||
@@ -18,7 +19,7 @@
|
||||
Удаление данных, исправления старых сессий и пересоздание мира потребовали бы
|
||||
другого способа обнаружения изменений.
|
||||
|
||||
## Создайте витрину и журнал успешных загрузок
|
||||
## Витрина и журнал загрузок
|
||||
|
||||
Создайте две локальные таблицы. Если имена заняты, выберите свои.
|
||||
|
||||
@@ -52,7 +53,7 @@ ORDER BY observation_day;
|
||||
и успешной сверки. Максимальная дата в журнале укажет, какой список
|
||||
использовался в последней завершенной загрузке.
|
||||
|
||||
## Выполните первую загрузку целиком
|
||||
## Первая загрузка
|
||||
|
||||
Убедитесь, что `world_live` стоит на паузе и нет активных прогонов генерации
|
||||
и обработки. Запустите `etl_pipeline` и дождитесь успеха. Получите текущую
|
||||
@@ -79,7 +80,7 @@ ORDER BY observation_day;
|
||||
и различия обоих показателей. Учебную витрину читайте с `FINAL`.
|
||||
После совпадения запишите дату наблюдения в журнал один раз.
|
||||
|
||||
## Найдите дни, которые нужно пересчитать
|
||||
## Выбор дней для пересчета
|
||||
|
||||
Продвиньте мир на один день: `world_next_day` с `days = 1`, затем
|
||||
`etl_pipeline`; после каждого запуска дождитесь успеха. Сохраните новый
|
||||
@@ -90,7 +91,7 @@ ORDER BY observation_day;
|
||||
даты для пересчета без повторов. Он должен учесть два изменения:
|
||||
|
||||
- в сессиях появились дни, которых еще нет в вашей витрине;
|
||||
- в списке на H появились пары, которых не было в списке на P; у этих кук
|
||||
- в списке на H появились пары, которых не было в списке на P; у этих `client_id`
|
||||
есть сессии, в том числе за прежние дни.
|
||||
|
||||
Сначала выведите даты и выберите один прежний день: какая новая пара
|
||||
@@ -103,7 +104,7 @@ ORDER BY observation_day;
|
||||
найденными сравнением списков? Покажите пару, которую ограничение по дате
|
||||
покупки пропускает.
|
||||
|
||||
## Обновите выбранные дни
|
||||
## Обновление витрины
|
||||
|
||||
Напишите второй `INSERT INTO ... SELECT`. Для выбранных дат он должен
|
||||
рассчитать все сессии с полным списком связей на H. Прибавление числа новых
|
||||
@@ -117,7 +118,7 @@ ORDER BY observation_day;
|
||||
Запрос должен вернуть пустой результат, а витрина — остаться прежней.
|
||||
Повторно записывать H в журнал не требуется.
|
||||
|
||||
## Проверьте повторный запуск после частичной загрузки
|
||||
## Повтор незавершенной загрузки
|
||||
|
||||
Снова запустите `world_next_day` с `days = 1`, затем `etl_pipeline`,
|
||||
дожидаясь успеха каждого. Сохраните новый список связей с его датой H.
|
||||
@@ -133,7 +134,7 @@ ORDER BY observation_day;
|
||||
не означает, что витрина обновлена. Что изменится, если записать H в журнал
|
||||
до обновления витрины?
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
Сохраните SQL первой загрузки, выбора дат, обновления и сверки.
|
||||
После двух новых модельных дней ваша витрина совпадает со стендовой,
|
||||
@@ -143,8 +144,9 @@ ORDER BY observation_day;
|
||||
Сравните число пересчитанных дней с полной загрузкой и перечислите,
|
||||
какие дополнительные данные пришлось хранить. Так можно оценить объем работы.
|
||||
На небольшом стенде полный пересчет может быть дешевле.
|
||||
В вашей реализации поиск связей пока читает всю историю покупок —
|
||||
инкрементальным стал именно пересчет дневной витрины.
|
||||
В вашей реализации поиск связей пока читает всю историю покупок.
|
||||
По новым данным обновляется только дневная витрина; такое обновление
|
||||
называют инкрементальным.
|
||||
|
||||
После работы удалите свои `lab_daily_traffic`, `lab_traffic_runs` и
|
||||
`lab_identity_history` через `DROP TABLE`, если они больше не нужны.
|
||||
@@ -1,21 +1,24 @@
|
||||
# Куда пропали покупки
|
||||
|
||||
**Чему учит:** проверить полноту данных по независимому источнику и выбрать
|
||||
источник для витрины заказов.
|
||||
Вы сравните покупки из событий сайта с заказами магазина. Найденные
|
||||
расхождения помогут выбрать источник для полной витрины заказов.
|
||||
|
||||
Факультатив доступен после ступени 1. В DBeaver подключитесь пользователем
|
||||
`analyst` по [инструкции](../../../README.md#подключиться-из-dbeaver).
|
||||
Дождитесь завершения `etl_pipeline`; на время расследования оставьте
|
||||
`world_live` на паузе и дождитесь уже начатых прогонов.
|
||||
|
||||
## Выберите день
|
||||
## Выбор дня
|
||||
|
||||
Можно ли построить полную витрину заказов по событиям `purchase`?
|
||||
Проверьте это на одном дне: сначала сопоставьте события сайта, затем сверьте
|
||||
номера заказов с выгрузкой магазина.
|
||||
|
||||
Возьмите день, для которого уже получен последний слепок семидневного окна.
|
||||
Запрос показывает самую позднюю подходящую дату:
|
||||
Слепок заказов содержит их состояние на конец модельного дня. Состояние
|
||||
заказа может меняться семь дней, поэтому для окончательного сравнения нужны
|
||||
все слепки за этот период. Возьмите день, для которого уже получен последний
|
||||
из них. Такой день называется закрытым. Запрос показывает самую позднюю
|
||||
подходящую дату:
|
||||
|
||||
```sql
|
||||
SELECT max(snapshot_date) - 6 AS last_closed_day
|
||||
@@ -27,7 +30,7 @@ FROM ods.order_v;
|
||||
Сравнивайте все заказы дня, включая отмененные: сейчас вопрос в том,
|
||||
зарегистрировал ли магазин заказ, а не в том, был ли он оплачен.
|
||||
|
||||
## Найдите разрыв в событиях
|
||||
## События оформления заказа
|
||||
|
||||
При оформлении заказа посетитель видит страницу подтверждения, а трекер
|
||||
отправляет событие покупки. Просмотр страницы и покупка — отдельные события.
|
||||
@@ -42,7 +45,7 @@ FROM ods.order_v;
|
||||
заказа пришло ровно по одному событию каждого вида? Что можно предположить
|
||||
по вашему результату и чего эти два числа еще не доказывают?
|
||||
|
||||
## Сверьте с магазином
|
||||
## Сверка с заказами магазина
|
||||
|
||||
В событии номер заказа находится в `purchase_id[1]`, в заказах — в `order_id`.
|
||||
Соберите список заказов выбранного дня, для которых нет события покупки.
|
||||
@@ -72,7 +75,7 @@ FROM ods.order_v;
|
||||
|
||||
Если в выбранном дне повторов нет, возьмите другой закрытый день.
|
||||
|
||||
## Сопоставьте вывод с готовой сверкой
|
||||
## Сравнение с готовой витриной
|
||||
|
||||
Найдите свои заказы без события в `dm.purchase_vs_orders_v` по `order_id`.
|
||||
Сравните их `status` и `mismatch_class`. Витрина присваивает заказу один класс,
|
||||
@@ -83,7 +86,7 @@ FROM ods.order_v;
|
||||
Возьмите один заказ из своего списка и сохраните два запроса: первый находит
|
||||
его в магазине, второй показывает отсутствие покупки в трекере.
|
||||
|
||||
## Выберите источник и условие проверки
|
||||
## Выбор источника для витрины
|
||||
|
||||
По результатам сверки укажите источник для витрины, в которой должен быть
|
||||
каждый зарегистрированный заказ. Обоснуйте выбор найденными номерами.
|
||||
@@ -93,7 +96,7 @@ FROM ods.order_v;
|
||||
Позволяет ли отсутствие события в `dds.event_v` установить место потери —
|
||||
до приема в хранилище или при обработке? Какие данные нужны, чтобы это выяснить?
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] сохранен запрос сверки по дате, возвращающий заказы без события;
|
||||
- [ ] оба равенства сходятся, влияние повторного просмотра объяснено;
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
# Какая позиция объясняет разницу в сумме
|
||||
|
||||
**Чему учит:** разобрать вложенные данные без потери точности и локализовать
|
||||
расхождение двух источников до товарной позиции.
|
||||
Вы сравните товары в событии покупки и заказе магазина, чтобы найти причину
|
||||
разницы в суммах. При разборе вложенных данных понадобится сохранить точность
|
||||
цен до копейки.
|
||||
|
||||
Факультатив доступен после ступени 1. В DBeaver используйте пользователя
|
||||
`analyst` из [инструкции подключения](../../../README.md#подключиться-из-dbeaver).
|
||||
Дождитесь завершения `etl_pipeline`; оставьте `world_live` на паузе и
|
||||
дождитесь уже начатых прогонов.
|
||||
|
||||
## Найдите заказ
|
||||
## Выбор заказа
|
||||
|
||||
Суммы покупки в трекере и заказа в магазине различаются. Разберите их позиции
|
||||
и проверьте отдельно точность своего запроса и различия между событием и заказом.
|
||||
@@ -24,12 +25,12 @@
|
||||
`declared_revenue`, если мы ищем расхождение именно в составе корзины?
|
||||
Проверьте свой выбор после разбора позиций.
|
||||
|
||||
## Разберите позиции с двух сторон
|
||||
## Товарные позиции события и заказа
|
||||
|
||||
Найдите событие покупки в `ods.event_v`: `purchaseID[1]` совпадает с номером
|
||||
заказа, `EventDate` — с выбранным днем, `EventType = 'purchase'`.
|
||||
Откройте поле `ecommerce`. Внутри JSON массив `purchase.products` содержит
|
||||
позиции клиента с полями `id`, `price` и `quantity`.
|
||||
товары из события покупки с полями `id`, `price` и `quantity`.
|
||||
|
||||
Разверните этот массив в строки и получите артикул, точную цену, количество
|
||||
и стоимость каждой позиции. Для разбора пригодятся `JSONExtractArrayRaw`
|
||||
@@ -47,7 +48,7 @@
|
||||
Сохраните запрос сверки для выбранных даты и номера заказа; в результате
|
||||
оставьте артикул, значения с обеих сторон и разность стоимостей позиций.
|
||||
|
||||
## Проверьте объяснение
|
||||
## Проверка сумм
|
||||
|
||||
Подтвердите три равенства:
|
||||
|
||||
@@ -66,7 +67,7 @@
|
||||
Какие строки подтверждают найденное расхождение? Есть ли в источниках
|
||||
сведения о том, почему оно возникло? Отделите наблюдения от предположений.
|
||||
|
||||
## Выберите поля для точного расчета
|
||||
## Поля для точного расчета
|
||||
|
||||
Укажите рядом с запросом, откуда брать цену, каким типом ее читать и по какому
|
||||
ключу сопоставлять позиции. Подтвердите выбор результатами обеих сверок:
|
||||
@@ -74,7 +75,7 @@
|
||||
между собой. Какой результат укажет на ошибку вашего разбора, а какой
|
||||
потребует искать причину выше — в данных, которые получил запрос?
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] сохранен запрос сверки позиций, не теряющий товары без пары;
|
||||
- [ ] все три равенства сходятся до копейки;
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
# Почему вчерашняя выручка меняется сегодня
|
||||
|
||||
**Чему учит:** обосновать окно пересчета витрины и требования к истории
|
||||
заказов по наблюдаемым опозданиям и изменениям статусов.
|
||||
Вы проследите, как опоздавшие заказы и смена статусов меняют выручку
|
||||
за прошедший день. По этим наблюдениям определите, какие дни пересчитывать
|
||||
и какую историю заказов сохранять.
|
||||
|
||||
Факультатив доступен после ступени 1. Понадобятся Airflow и DBeaver с
|
||||
подключением `student` по [инструкции](../../../README.md#подключиться-из-dbeaver).
|
||||
Он читает данные стенда и записывает ваши наблюдения в базу `default`.
|
||||
Пользователь `student` может читать данные стенда и записывать ваши наблюдения
|
||||
в базу `default`.
|
||||
Вы будете наблюдать один день оформления заказов, продвигая мир по одному
|
||||
модельному дню. Проверьте, можно ли при загрузке выручки каждый раз считать
|
||||
только новый день. Новые данные останутся в стенде.
|
||||
|
||||
## Подготовьте первое наблюдение
|
||||
## Подготовка
|
||||
|
||||
Поставьте `world_live` на паузу. Дождитесь завершения начатых прогонов
|
||||
генерации и обработки, затем запустите `etl_pipeline` и дождитесь успеха.
|
||||
@@ -28,14 +30,15 @@ SELECT
|
||||
Дата слепка, по которому сделано очередное наблюдение, будет **S**.
|
||||
Это модельные даты магазина. Календарная дата запуска Airflow день D не задает.
|
||||
|
||||
Слепок снимается к концу суток и доставляется следующим прогоном. Поэтому
|
||||
покупки последнего сыгранного дня уже видны в событиях, а их заказов в
|
||||
Слепок содержит состояние заказов на конец модельных суток и доставляется
|
||||
при следующем запуске генератора. Поэтому покупки последнего завершившегося
|
||||
дня уже видны в событиях, а их заказов в
|
||||
хранилище еще нет. Проверьте это для D в `dds.order_v` и
|
||||
`dm.purchase_vs_orders_v`.
|
||||
|
||||
## Подготовьте место для наблюдений
|
||||
## Таблицы для истории заказов
|
||||
|
||||
Публичное представление заказа показывает текущее состояние, а сырые слепки
|
||||
Представление `dds.order_v` показывает текущее состояние заказов, а сырые слепки
|
||||
хранятся ограниченное время. Сохраните историю в двух своих таблицах: потом
|
||||
вы сможете сравнивать наблюдения обычными SQL-запросами в том же DBeaver.
|
||||
|
||||
@@ -66,7 +69,7 @@ ENGINE = MergeTree
|
||||
ORDER BY (order_date, snapshot_date);
|
||||
```
|
||||
|
||||
## Сохраните первое наблюдение
|
||||
## Первое наблюдение
|
||||
|
||||
Напишите два `INSERT INTO ... SELECT` с явным списком колонок назначения:
|
||||
|
||||
@@ -93,7 +96,7 @@ ORDER BY (order_date, snapshot_date);
|
||||
статусам и сумму `items_total` только у `paid`. Эта сумма должна сходиться
|
||||
с сохраненной выручкой, сложенной по всем категориям.
|
||||
|
||||
## Продвигайте мир по одному дню
|
||||
## Наблюдения за следующие дни
|
||||
|
||||
Для каждого следующего наблюдения:
|
||||
|
||||
@@ -102,16 +105,16 @@ ORDER BY (order_date, snapshot_date);
|
||||
не обновляет.
|
||||
3. Повторите два запроса сохранения с новой датой S и проверьте записанные строки.
|
||||
|
||||
Во всех запросах остается прежний день D. За один запуск проигрывайте один
|
||||
Во всех запросах остается прежний день D. За один запуск добавляйте один
|
||||
день, чтобы сохранить промежуточные состояния. Пакетный `world_next_day`
|
||||
отправляет события без пауз; ждать 24 минуты живого дня здесь не нужно.
|
||||
|
||||
Продолжайте до слепка **D + 7** включительно. У заказов дня D окно состоит
|
||||
из семи дней, от D до D + 6. Последний шаг нужен для сравнения состояния
|
||||
в конце окна и за его пределами.
|
||||
Продолжайте до слепка **D + 7** включительно. Заказы дня D могут меняться
|
||||
семь дней, от D до D + 6. Этот период называется окном изменяемости.
|
||||
Последний шаг нужен для сравнения состояния в конце окна и после него.
|
||||
От исходного наблюдения потребуется восемь однодневных запусков.
|
||||
|
||||
## Найдите изменения в уже загруженном дне
|
||||
## Изменения заказов и выручки
|
||||
|
||||
Для сравнения прочитайте `default.lab_order_history` дважды: в одном
|
||||
подзапросе отберите прежнюю S, в другом — новую; день D остается общим.
|
||||
@@ -138,7 +141,7 @@ ORDER BY (order_date, snapshot_date);
|
||||
Достаточно ли добавлять в старый день только опоздавшие заказы? Проверьте
|
||||
свой ответ на найденных изменениях статусов.
|
||||
|
||||
## Определите, какую историю нужно хранить
|
||||
## Требования к истории заказов
|
||||
|
||||
Возьмите отмененный заказ, у которого в вашей истории был статус `paid`.
|
||||
Укажите два наблюдения, доказывающих, что он был оплачен, а затем отменен.
|
||||
@@ -153,7 +156,7 @@ ORDER BY (order_date, snapshot_date);
|
||||
Какие сведения для каждой задачи должен передавать источник и сохранять
|
||||
хранилище? Проверьте, каких нужных сведений нет в ваших таблицах наблюдений.
|
||||
|
||||
## Задайте правило пересчета
|
||||
## Правило пересчета витрины
|
||||
|
||||
Сравните сохраненные состояния при S = D + 6 и S = D + 7: состав заказов,
|
||||
их статусы, суммы и выручку. Какой слепок последним мог изменить заказы дня D
|
||||
@@ -171,13 +174,13 @@ ORDER BY (order_date, snapshot_date);
|
||||
[слепка и его доставки](../../architecture/orders/snapshot.md#слепок-и-его-доставка)
|
||||
и [границ истории отмен](../../architecture/orders/fate.md#судьба-заказа).
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] сохранена последовательность наблюдений одного дня D с датами слепков S;
|
||||
- [ ] на опоздавших заказах и изменениях статусов показаны ошибки загрузки
|
||||
только новых дней или только новых заказов;
|
||||
- [ ] изменение выручки между наблюдениями сходится с изменением вкладов заказов;
|
||||
- [ ] оплата одной из последующих отмен подтверждена двумя наблюдениями;
|
||||
- [ ] для одного отмененного заказа найдены два наблюдения: с оплатой и с отменой;
|
||||
- [ ] сформулированы требования к истории расчетов и истории оплаты;
|
||||
- [ ] правило пересчета и момент закрытия дня обоснованы наблюдениями
|
||||
и правилом доставки слепков.
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# Свой дашборд в Superset
|
||||
|
||||
**Чему учит:** собрать из своих витрин отчет о поведении посетителей,
|
||||
подобрать графики под вопросы и разместить их на одном понятном экране.
|
||||
Из своих витрин вы соберете отчет о поведении посетителей: подберете графики
|
||||
и разместите их на одном экране так, чтобы отчет можно было понять
|
||||
без ваших пояснений.
|
||||
|
||||
Это факультатив раздела «Superset и BI». Его можно пройти после ступени 2,
|
||||
Это задание раздела «Отчеты в Superset». Его можно пройти после ступени 2,
|
||||
когда готовы витрины ступени 1.
|
||||
|
||||
## На какие вопросы ответит дашборд
|
||||
## Отчет о поведении посетителей
|
||||
|
||||
Соберите обзор трафика и поведения посетителей. Он должен отвечать на три
|
||||
вопроса: что происходило в магазине, какие страницы, устройства и рекламные
|
||||
@@ -17,25 +18,23 @@
|
||||
стендового дашборда не нужно: выберите форму, в которой ответы читаются без
|
||||
пояснений рядом с экраном.
|
||||
|
||||
## Свои объекты — отдельно
|
||||
## Отдельный дашборд для задания
|
||||
|
||||
Не добавляйте графики в стендовый дашборд и не правьте его датасеты. При
|
||||
инициализации Superset импорт с `--overwrite` заменит ручные правки стендовых
|
||||
объектов файлами из репозитория. `make up` свой дашборд не стирает; срок жизни
|
||||
таких объектов описан в [доке Superset](../../architecture/superset.md), «Импорт
|
||||
таких объектов описан в [справочнике Superset](../../architecture/superset.md), «Импорт
|
||||
при каждой инициализации».
|
||||
|
||||
Используйте свои датасеты и графики, соберите из них свой дашборд.
|
||||
Назовите его, например, «Мои витрины DM». Это то же правило, по которому DDL
|
||||
домашек лежит в `sql/ddl/70-labs.sql`: стендовые объекты уже есть, свои
|
||||
создаете вы.
|
||||
Назовите его, например, «Мои витрины DM».
|
||||
|
||||
## Датасеты
|
||||
|
||||
Датасет `session_overview_v`, его метрика доли отказов и график уже готовы
|
||||
после [ступени 2](../2-1-superset-chart.md). Используйте их здесь. Добавьте
|
||||
недостающие датасеты, чтобы в подключении `ClickHouse` были представлены
|
||||
пять публичных представлений схемы `dm`:
|
||||
пять представлений ваших витрин из схемы `dm`:
|
||||
|
||||
- `events_enriched_v`;
|
||||
- `top_pages_daily_v`;
|
||||
@@ -56,7 +55,7 @@
|
||||
| `dq_errors_daily_v` | число ошибок по дню загрузки, источнику и классу |
|
||||
| `utm_effectiveness_v` | трафик, покупки и заявленную выручку по рекламным каналам |
|
||||
|
||||
Для долей используйте слагаемые из витрин. Например, доля отказов считается
|
||||
Для долей используйте суммы счетчиков из витрин. Например, доля отказов считается
|
||||
как сумма `bounce_sessions`, деленная на сумму `sessions`. Среднее по готовым
|
||||
долям для такого графика не подходит.
|
||||
|
||||
@@ -73,7 +72,7 @@
|
||||
по модельному времени. Для панели ошибок выберите период загрузки отдельно
|
||||
и укажите его в подписи. Общий фильтр времени для всего дашборда не требуется.
|
||||
|
||||
## Как проверить себя
|
||||
## Проверка результата
|
||||
|
||||
Автоматической проверки этого задания нет. Числа вы сверите запросами,
|
||||
а понятность графиков проверите по подписям.
|
||||
@@ -103,9 +102,9 @@
|
||||
Проверьте, можно ли найти эти ответы по одним подписям графиков. Если есть
|
||||
возможность, попросите другого человека прочитать экран без ваших пояснений.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] у вас есть пять датасетов поверх публичных представлений витрин заданий 1–5;
|
||||
- [ ] у вас есть пять датасетов по представлениям витрин заданий 1–5;
|
||||
- [ ] вы собрали свой дашборд, сохранив стендовые объекты без изменений;
|
||||
- [ ] на дашборде есть хотя бы один график от каждой витрины;
|
||||
- [ ] вы проверили, что графики и подписи понятны по вопросам из раздела выше;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# В DBeaver работает, в Superset нет
|
||||
|
||||
**Чему учит:** найти причину, по которой один клиент читает объект
|
||||
хранилища, а другой его не видит.
|
||||
Вы создадите представление, которое читается из DBeaver, но недоступно
|
||||
в Superset. Затем найдете причину и исправите ее.
|
||||
|
||||
Факультатив доступен после [ступени 2](../2-1-superset-chart.md).
|
||||
Свой дашборд для опыта не нужен. Используйте готовую витрину стенда
|
||||
`dm.daily_traffic_v`.
|
||||
|
||||
## Подготовьте опыт
|
||||
## Подготовка
|
||||
|
||||
В DBeaver подключитесь к первой ноде пользователем `etl`, как описано
|
||||
в разделе [«Как применить свой DDL»](../README.md#как-применить-свой-ddl).
|
||||
В DBeaver подключитесь к первому узлу пользователем `etl`, как описано
|
||||
в разделе [«Выполнение SQL в DBeaver»](../README.md#выполнение-sql-в-dbeaver).
|
||||
Для опыта создайте отдельное представление:
|
||||
|
||||
```sql
|
||||
@@ -28,19 +28,19 @@ SELECT count() FROM dm.bi_node_probe_v;
|
||||
Повторите запрос в SQL Lab Superset, выбрав подключение `ClickHouse`.
|
||||
Сохраните текст ошибки.
|
||||
|
||||
## Найдите причину
|
||||
## Поиск причины
|
||||
|
||||
Выясните, к каким серверам обращаются DBeaver и Superset. Адреса подключения
|
||||
показывают настройки клиентов, а имя ответившего сервера можно спросить
|
||||
запросом `SELECT hostName()`.
|
||||
|
||||
Проверьте наличие `dm.bi_node_probe_v` на обеих нодах. Для второй ноды
|
||||
Проверьте наличие `dm.bi_node_probe_v` на обоих узлах. Для второго узла
|
||||
заведите подключение DBeaver с портом из
|
||||
[README](../../../README.md#подключиться-из-dbeaver). Запишите, где объект
|
||||
есть, и объясните, почему успешного создания в первом подключении оказалось
|
||||
недостаточно.
|
||||
|
||||
Исправьте DDL так, чтобы представление появилось на обеих нодах. Подключение
|
||||
Исправьте DDL так, чтобы представление появилось на обоих узлах. Подключение
|
||||
Superset должно остаться прежним. Сохраните исправленный DDL и повторите
|
||||
исходный запрос в обоих клиентах.
|
||||
|
||||
@@ -48,23 +48,23 @@ Superset должно остаться прежним. Сохраните исп
|
||||
[справочнике Superset](../../architecture/superset.md#устройство-зоны) и
|
||||
[описание распределенного DDL ClickHouse](https://clickhouse.com/docs/reference/statements/distributed-ddl).
|
||||
|
||||
## Как проверить себя
|
||||
## Проверка результата
|
||||
|
||||
В обоих клиентах запрос к вашему представлению должен вернуть столько же
|
||||
строк, сколько `SELECT count() FROM dm.daily_traffic_v`. Сравнивайте результаты
|
||||
при завершенном прогоне конвейера, пока мир не продвигается.
|
||||
|
||||
Объясните причину ошибки через три факта: куда пришел DDL, куда пришел
|
||||
запрос Superset и на каких нодах существовало представление.
|
||||
запрос Superset и на каких узлах существовало представление.
|
||||
|
||||
После опыта удалите только `dm.bi_node_probe_v` на обеих нодах. Не включайте
|
||||
После опыта удалите только `dm.bi_node_probe_v` на обоих узлах. Не включайте
|
||||
его в постоянный DDL домашних витрин: это временный объект расследования.
|
||||
|
||||
## Что должно получиться
|
||||
## Результат работы
|
||||
|
||||
- [ ] у вас сохранены успешный запрос из DBeaver и ошибка из SQL Lab;
|
||||
- [ ] вы можете объяснить, к каким серверам подключены клиенты, где было
|
||||
создано представление и почему Superset его не видел;
|
||||
- [ ] у вас есть исправленный DDL; оба клиента возвращают столько же строк,
|
||||
сколько исходная витрина;
|
||||
- [ ] после опыта вы удалили учебное представление с обеих нод.
|
||||
- [ ] после опыта вы удалили учебное представление с обоих узлов.
|
||||
@@ -45,12 +45,16 @@
|
||||
Пишите для учащегося: объясняйте, чему он научится, зачем нужны действия
|
||||
и как понять результат. Историю разработки оставляйте за пределами задания.
|
||||
|
||||
«Как проверить себя» описывает действия для проверки и поиска ошибок.
|
||||
«Что должно получиться» — результат работы и то, что учащийся теперь может
|
||||
Начинайте задание коротким описанием предстоящей работы. Заголовки должны
|
||||
называть содержание разделов: «Подготовка», «Создание витрины», «Проверка
|
||||
результата». Шаблонные вопросы «Чему учит» и «Что сделать» не нужны.
|
||||
|
||||
Раздел проверки описывает действия для проверки и поиска ошибок.
|
||||
Раздел результата перечисляет итог работы и то, что учащийся теперь может
|
||||
объяснить. Если проверка уже встроена в шаги упражнения, повторять ее
|
||||
отдельным разделом не нужно.
|
||||
|
||||
Остальные заголовки и структуру выбирайте по содержанию. Образцы — задания
|
||||
Структуру выбирайте по содержанию. Образцы — задания
|
||||
про [дашборд Superset](../labs/optional/superset-dashboard.md) и
|
||||
[расследование подключения](../labs/optional/superset-missing-object.md).
|
||||
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@
|
||||
и через окружение; имена и примеры собраны в
|
||||
[справочнике запуска](../docs/architecture/world.md#как-запустить-генератор).
|
||||
Прямой запуск генератора не продвигает сохраненную позицию времени стенда —
|
||||
ей управляют [даги модельного времени](../docs/handbook/airflow-dags.md#даги-мира).
|
||||
ей управляют [даги модельного времени](../docs/handbook/airflow-dags.md#генерация-модельных-дней).
|
||||
|
||||
Из каталога `generator/` доступны:
|
||||
|
||||
|
||||
Reference in new issue
Block a user