docs(labs): влита редактура учебных заданий
Зачем: сделать задания понятнее учащемуся и сохранить правила для будущих работ. Что: объединены три коммита ветки docs/labs-human-language с сохранением истории через merge --no-ff. Проверка: рабочее дерево чистое перед слиянием, origin/main входит в локальную main, итоговый diff прошел git diff --check. Ссылки и сохранность технических примеров проверены при редактуре.
This commit is contained in:
commit
8c9c10a875
16 files changed
+280
-273
No files matched your search
@@ -81,6 +81,9 @@ Airflow) и названия из кода. Если для понятия ес
|
||||
писал: простые слова, короткие фразы, сложную мысль поясняй при первом
|
||||
упоминании. Если понятность и буквальная точность спорят — выбирай понятность.
|
||||
|
||||
При создании или редактировании лабораторных следуй
|
||||
[правилам учебного текста](docs/specs/2026-09-01-labs-program.md#как-писать-задания).
|
||||
|
||||
Внутренние рассуждения и промежуточные пометки по ходу работы веди на
|
||||
английском — он экономнее по токенам. На русском остаётся всё, что видит и
|
||||
хранит проект: итоговые ответы, документы, комментарии в коде и SQL, сообщения
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Задание 1: `dm.events_enriched_v`
|
||||
|
||||
**Чему учит:** строить слой как договор о данных, а не как их копию.
|
||||
Оставлять витрину представлением, если зерно источника не меняется.
|
||||
**Чему учит:** подготовить удобный для анализа набор полей события
|
||||
и обойтись представлением, сохранив одну строку на событие.
|
||||
|
||||
## Сначала прочитать
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
- [Как применить свой DDL](README.md#как-применить-свой-ddl): файл для решения
|
||||
и подключение с правами на создание витрины.
|
||||
|
||||
## Роль витрины
|
||||
## Для чего нужна витрина
|
||||
|
||||
В `dds.event_v` 44 колонки, включая технические поля источника и массивы
|
||||
товаров. Для анализа событий в Superset нужны деловые поля и три готовых
|
||||
@@ -88,15 +88,10 @@ DDS читают из ODS без отдельной загрузки. Эти с
|
||||
Отдельно сверьте `event_hour`, `page_path` и `referer_host` с теми же
|
||||
выражениями, рассчитанными прямо по `dds.event_v`.
|
||||
|
||||
Если строк в витрине в разы меньше, чем в источнике, и остались только
|
||||
события корзины и покупки, это признак развернутых массивов: `ARRAY JOIN`
|
||||
исключил все события с пустым массивом товаров.
|
||||
## Что должно получиться
|
||||
|
||||
## Приемка
|
||||
|
||||
- [ ] представление `dm.events_enriched_v` создано без физических таблиц;
|
||||
- [ ] список колонок в представлении задан явно;
|
||||
- [ ] число строк и набор `event_id` совпадают с `dds.event_v` за каждый
|
||||
прожитый день;
|
||||
- [ ] задача `events_enriched` в `homework_check` завершилась успешно;
|
||||
- [ ] задачи для этой витрины в `dm_transform` нет.
|
||||
- [ ] у вас есть `dm.events_enriched_v` с явно перечисленными колонками;
|
||||
- [ ] вы сверили число строк и набор `event_id` с `dds.event_v` за каждый
|
||||
прожитый день; расхождений нет;
|
||||
- [ ] проверка `events_enriched` в `homework_check` прошла успешно;
|
||||
- [ ] витрина работает без физической таблицы и задачи в `dm_transform`.
|
||||
@@ -1,14 +1,14 @@
|
||||
# Задание 2: `dm.top_pages_daily`
|
||||
|
||||
**Чему учит:** создавать витрину с новым зерном: пять объектов, донор и
|
||||
замена дневных партиций. Включать ее пересборку в `dm_transform`.
|
||||
**Чему учит:** собрать дневные показатели страниц и настроить их
|
||||
обновление в `dm_transform` через замену партиций.
|
||||
|
||||
## Сначала прочитать
|
||||
|
||||
- [«Хранилище и его слои»](../handbook/storage-layers.md);
|
||||
- [«Потоки Airflow и даги модельного времени»](../handbook/airflow-dags.md).
|
||||
|
||||
## Роль витрины
|
||||
## Для чего нужна витрина
|
||||
|
||||
По дневным показателям страниц аналитик сравнивает просмотры, посетителей и
|
||||
действия с товарами. Добавление в корзину происходит на карточке товара,
|
||||
@@ -98,13 +98,13 @@
|
||||
Сумма `visitors` по страницам повторно учитывает посетителя, открывшего
|
||||
несколько страниц. Число посетителей за день берите из `dm.daily_traffic_v`.
|
||||
|
||||
## Приемка
|
||||
## Что должно получиться
|
||||
|
||||
- [ ] созданы пять объектов, донор объявлен через `AS` и шардируется ключом
|
||||
цели;
|
||||
- [ ] в `dm.top_pages_daily_v` явно перечислены колонки, включая `_load_id`
|
||||
и `_load_ts`;
|
||||
- [ ] объем прогона определяется по состоянию витрины без ручного задания;
|
||||
- [ ] задача `top_pages_daily` в `homework_check` завершилась успешно;
|
||||
- [ ] задача витрины добавлена в `dm_transform`, повторный прогон конвейера
|
||||
не меняет результат.
|
||||
- [ ] у вас есть пять объектов витрины; донор объявлен через `AS` и
|
||||
использует тот же ключ шардирования, что и целевая таблица;
|
||||
- [ ] в `dm.top_pages_daily_v` явно перечислены колонки, включая
|
||||
`_load_id` и `_load_ts`;
|
||||
- [ ] ваш запрос сам определяет дни пересборки по состоянию витрины;
|
||||
- [ ] проверка `top_pages_daily` в `homework_check` прошла успешно;
|
||||
- [ ] вы включили витрину в `dm_transform` и убедились, что повторный
|
||||
прогон конвейера не меняет результат.
|
||||
@@ -1,24 +1,21 @@
|
||||
# Задание 3: `dm.session_overview`
|
||||
|
||||
**Чему учит:** хранить слагаемые, а не готовые средние и доли. Понимать,
|
||||
почему сумма `visitors` по строкам сейчас сходится, но начнет завышать
|
||||
аудиторию, если у одной куки появятся визиты с двух устройств.
|
||||
**Чему учит:** хранить слагаемые для расчета средних и долей и понимать,
|
||||
когда сложение числа посетителей по устройствам завышает аудиторию.
|
||||
|
||||
## Сначала прочитать
|
||||
|
||||
- [«Хранилище и его слои»](../handbook/storage-layers.md);
|
||||
- [«Потоки Airflow и даги модельного времени»](../handbook/airflow-dags.md).
|
||||
|
||||
## Роль витрины
|
||||
## Для чего нужна витрина
|
||||
|
||||
Витрина нужна, чтобы по дням сравнивать визиты с телефона и компьютера: их
|
||||
число, длительность, просмотры страниц и частоту покупок.
|
||||
|
||||
На прошлом стенде витрина выделяла визиты сама. Теперь их рассчитывают в
|
||||
DDS, а повторять зерно сессий ради представления с переименованными
|
||||
колонками незачем. Здесь вы учитесь дневной агрегации, поэтому зерно — день
|
||||
и устройство. Выбор описан в разделе «Форма: по зерну, не по цене запроса»
|
||||
[справочника DM](../architecture/dm.md).
|
||||
Сессии уже рассчитаны в DDS. Из них вы соберете дневные показатели по
|
||||
устройствам. Почему такие итоги хранят в отдельной таблице, объясняет
|
||||
[справочник DM](../architecture/dm.md).
|
||||
|
||||
## Что сделать
|
||||
|
||||
@@ -107,12 +104,12 @@ DDS, а повторять зерно сессий ради представле
|
||||
повторного учета визитов;
|
||||
- сумма `events_total` равна сумме `events_total` по сессиям того же дня.
|
||||
|
||||
## Приемка
|
||||
## Что должно получиться
|
||||
|
||||
- [ ] созданы пять объектов, донор объявлен через `AS` и шардируется ключом
|
||||
цели;
|
||||
- [ ] в `dm.session_overview_v` колонки перечислены явно;
|
||||
- [ ] источник — `dds.session_v`, повторного выделения визитов нет;
|
||||
- [ ] физическая таблица не хранит средние и доли;
|
||||
- [ ] задача `session_overview` в `homework_check` завершилась успешно;
|
||||
- [ ] задача витрины добавлена в `dm_transform`.
|
||||
- [ ] у вас есть пять объектов витрины; донор объявлен через `AS` и
|
||||
использует ключ шардирования целевой таблицы;
|
||||
- [ ] вы явно перечислили колонки в `dm.session_overview_v` и собрали
|
||||
показатели из готовых сессий `dds.session_v`;
|
||||
- [ ] в таблице хранятся слагаемые для средних и долей;
|
||||
- [ ] проверка `session_overview` в `homework_check` прошла успешно;
|
||||
- [ ] витрина обновляется при запуске `dm_transform`.
|
||||
@@ -1,15 +1,14 @@
|
||||
# Задание 4: `dm.dq_errors_daily`
|
||||
|
||||
**Чему учит:** считать брак по дню загрузки и сохранять сводку дольше
|
||||
исходных строк. Пересборка всех партиций после удаления источника по `TTL`
|
||||
стирает историю, которую уже не восстановить.
|
||||
исходных строк.
|
||||
|
||||
## Сначала прочитать
|
||||
|
||||
- [«Хранилище и его слои»](../handbook/storage-layers.md);
|
||||
- [«Потоки Airflow и даги модельного времени»](../handbook/airflow-dags.md).
|
||||
|
||||
## Роль витрины
|
||||
## Для чего нужна витрина
|
||||
|
||||
Сводку открывают при росте брака в потоке: смотрят, каких ошибок стало
|
||||
больше и сколько строк забраковано. Причину конкретной ошибки исследуют
|
||||
@@ -97,17 +96,16 @@
|
||||
`ods.event_errors_dist` и `ods.order_errors_dist`. Если она примерно вдвое
|
||||
меньше, вероятная причина — чтение `_rep` вместо `_dist`.
|
||||
|
||||
Пустая витрина на чистом стенде — верный ответ: обе стороны пусты,
|
||||
расхождений ноль, проверка успешна. Требование хотя бы одной строки дало бы
|
||||
постоянную ошибку, которую со временем стали бы игнорировать.
|
||||
Если в источниках нет брака, витрина должна быть пустой. В этом случае
|
||||
расхождений тоже ноль и проверка успешна.
|
||||
|
||||
## Приемка
|
||||
## Что должно получиться
|
||||
|
||||
- [ ] созданы пять объектов, донор объявлен через `AS` и шардируется ключом
|
||||
цели;
|
||||
- [ ] источники — таблицы `_dist` обоих видов брака;
|
||||
- [ ] прогон заменяет партиции только дней, еще имеющихся в источниках, и
|
||||
сохраняет дни за пределами их срока хранения;
|
||||
- [ ] задача `dq_errors_daily` в `homework_check` завершилась успешно, в том
|
||||
числе на чистом стенде без брака;
|
||||
- [ ] задача витрины добавлена в `dm_transform`.
|
||||
- [ ] у вас есть пять объектов витрины; донор объявлен через `AS` и
|
||||
использует ключ шардирования целевой таблицы;
|
||||
- [ ] сводка включает ошибки событий и заказов из таблиц `_dist`;
|
||||
- [ ] ваш запрос пересобирает только дни, еще доступные в источниках,
|
||||
сохраняя более старые итоги;
|
||||
- [ ] проверка `dq_errors_daily` в `homework_check` прошла успешно;
|
||||
отсутствие брака тоже дает успешный результат;
|
||||
- [ ] витрина обновляется при запуске `dm_transform`.
|
||||
@@ -1,15 +1,14 @@
|
||||
# Задание 5: `dm.utm_effectiveness`
|
||||
|
||||
**Чему учит:** понимать предупреждение в приставке `declared_`: это сумма со
|
||||
слов клиента, а не деньги отчетности. Самостоятельно находить причины
|
||||
расхождения двух сумм выручки.
|
||||
**Чему учит:** различать сумму покупки со слов клиента (`declared_`) и
|
||||
серверную выручку, находить причины их расхождения.
|
||||
|
||||
## Сначала прочитать
|
||||
|
||||
- [«Хранилище и его слои»](../handbook/storage-layers.md);
|
||||
- [«Потоки Airflow и даги модельного времени»](../handbook/airflow-dags.md).
|
||||
|
||||
## Роль витрины
|
||||
## Для чего нужна витрина
|
||||
|
||||
Аналитик сравнивает рекламные каналы по дням: трафик, добавления в корзину,
|
||||
покупки и заявленные клиентом суммы.
|
||||
@@ -44,7 +43,7 @@
|
||||
| `_load_ts` | `DateTime64(3, 'UTC')` | Время сборки |
|
||||
|
||||
Для `declared_revenue` сложите `purchase_revenue[1]` событий `purchase`. В
|
||||
параллельных массивах `purchase*` по контракту хранятся один номер заказа и
|
||||
параллельных массивах `purchase*` хранятся один номер заказа и
|
||||
одна сумма на событие. В сверке `purchase_vs_orders` используют тот же
|
||||
индекс.
|
||||
|
||||
@@ -127,12 +126,12 @@
|
||||
Сумма `visitors` по строкам повторно учитывает одного посетителя,
|
||||
приходящего по разным меткам. Для числа посетителей дня она не подходит.
|
||||
|
||||
## Приемка
|
||||
## Что должно получиться
|
||||
|
||||
- [ ] созданы пять объектов, донор объявлен через `AS` и шардируется ключом
|
||||
цели;
|
||||
- [ ] сохранены строки без UTM-меток;
|
||||
- [ ] `declared_revenue` хранится в `Decimal(18, 2)`;
|
||||
- [ ] расхождение с `dm.revenue_daily_v` объяснено письменно;
|
||||
- [ ] задача `utm_effectiveness` в `homework_check` завершилась успешно;
|
||||
- [ ] задача витрины добавлена в `dm_transform`.
|
||||
- [ ] у вас есть пять объектов витрины; донор объявлен через `AS` и
|
||||
использует ключ шардирования целевой таблицы;
|
||||
- [ ] строки без UTM-меток сохранены, а `declared_revenue` хранится
|
||||
в `Decimal(18, 2)`;
|
||||
- [ ] вы записали, почему сумма отличается от `dm.revenue_daily_v`;
|
||||
- [ ] проверка `utm_effectiveness` в `homework_check` прошла успешно;
|
||||
- [ ] витрина обновляется при запуске `dm_transform`.
|
||||
@@ -8,7 +8,7 @@
|
||||
- [«Немного Superset»](../handbook/superset.md);
|
||||
- [задание 3: `dm.session_overview`](1-3-session-overview.md).
|
||||
|
||||
## Роль графика
|
||||
## На какой вопрос ответит график
|
||||
|
||||
В задании 3 вы собрали витрину `dm.session_overview_v`. Теперь покажите, с
|
||||
каких устройств чаще уходят после первой страницы. Постройте столбиковый
|
||||
@@ -45,10 +45,10 @@
|
||||
укажите «Категория устройства», а «Dimensions» не заполняйте. Дашборд
|
||||
создавать не нужно: сохраненного графика достаточно.
|
||||
|
||||
По умолчанию в графике выбран период «No filter». Оставьте его таким или
|
||||
задайте явные даты. Относительный период, например «Last week», даст пустой
|
||||
график: модельный календарь стенда отстает от настоящего. Последнюю дату
|
||||
витрины посмотрите в SQL Lab:
|
||||
Для этого задания оставьте период «No filter»: график и проверочный запрос
|
||||
ниже считают долю отказов за весь период витрины. Относительный период,
|
||||
например «Last week», может дать пустой график: модельный календарь стенда
|
||||
отстает от настоящего. Последнюю дату витрины посмотрите в SQL Lab:
|
||||
|
||||
```sql
|
||||
SELECT max(report_date)
|
||||
@@ -60,8 +60,8 @@ FROM dm.session_overview_v;
|
||||
|
||||
## Как проверить себя
|
||||
|
||||
График проверяется глазами, поэтому задачи для него в `homework_check` нет.
|
||||
В SQL Lab выполните запрос:
|
||||
Автоматической проверки этого задания нет. Сверьте числа на графике
|
||||
с результатом запроса в SQL Lab:
|
||||
|
||||
```sql
|
||||
SELECT device_category,
|
||||
@@ -76,15 +76,16 @@ ORDER BY device_category;
|
||||
проценты с одним знаком, как формат `.1%`, поэтому числа должны совпасть
|
||||
один в один.
|
||||
|
||||
## Приемка
|
||||
## Что должно получиться
|
||||
|
||||
- [ ] создан физический датасет `dm.session_overview_v` в подключении
|
||||
- [ ] у вас есть физический датасет `dm.session_overview_v` в подключении
|
||||
`ClickHouse`;
|
||||
- [ ] в датасете есть метрика «Доля отказов» с выражением
|
||||
`SUM(bounce_sessions) / SUM(sessions)` и форматом `.1%`;
|
||||
- [ ] для `device_category` и `report_date` заданы русские имена «Категория
|
||||
- [ ] колонки `device_category` и `report_date` подписаны «Категория
|
||||
устройства» и «Дата отчета»;
|
||||
- [ ] сохранен столбиковый график доли отказов по категориям устройств без
|
||||
относительного фильтра по времени;
|
||||
- [ ] график сверен с запросом из SQL Lab;
|
||||
- [ ] стендовые объекты не изменены, дашборд не создан.
|
||||
- [ ] сохраненный столбиковый график показывает долю отказов по категориям
|
||||
устройств за весь период витрины;
|
||||
- [ ] числа на графике совпадают с результатом запроса из SQL Lab;
|
||||
- [ ] вы сохранили график отдельно, без создания дашборда и изменения
|
||||
стендовых объектов.
|
||||
@@ -1,14 +1,14 @@
|
||||
# Нарежь сессии сам
|
||||
# Нарежьте сессии сами
|
||||
|
||||
**Чему учит:** собрать сессии из событий и сверить их число с внешним
|
||||
эталоном. Найти причину расхождения в отдельных строках.
|
||||
**Чему учит:** собрать сессии из событий, сравнить их число с `VisitID`
|
||||
источника и найти события, из-за которых расчеты расходятся.
|
||||
|
||||
## Сначала прочитать
|
||||
|
||||
- [Хранилище и его слои, раздел DDS](../handbook/storage-layers.md#dds-язык-бизнеса);
|
||||
- [формат события: Идентификаторы и время](../formats/clickstream-event.md#идентификаторы-и-время).
|
||||
|
||||
## Роль сессии
|
||||
## Для чего нужны сессии
|
||||
|
||||
Визит, или сессия, объединяет действия посетителя за один заход на сайт.
|
||||
По визитам аналитик считает длительность и глубину просмотра.
|
||||
@@ -71,19 +71,22 @@ ORDER BY EventDate, ClientID;
|
||||
|
||||
## Как проверить себя
|
||||
|
||||
Результат задания проверяется глазами, поэтому задачи в `homework_check`
|
||||
для него нет. Таблицы и представления создавать не нужно.
|
||||
Сохраните исходный и уточненный расчеты, результаты сверки и найденные строки.
|
||||
Таблицы и представления создавать не нужно. Задачи в `homework_check`
|
||||
для этого задания нет: вы проверяете расчет своим запросом.
|
||||
|
||||
**Предупреждение.** В `sql/dds/session_rebuild.sql` и
|
||||
В `sql/dds/session_rebuild.sql` и
|
||||
`sql/dq/sessions_vs_reference.sql` лежат готовые запросы нарезки и сверки.
|
||||
Откройте их после собственного решения и расследования. Сравните запросы
|
||||
со своими: копирование готового SQL пропускает расследование.
|
||||
со своими.
|
||||
Сопоставление со стендовой нарезкой по `session_id` остается на ступень 4.
|
||||
|
||||
## Приемка
|
||||
## Что должно получиться
|
||||
|
||||
- [ ] запрос нарезает все события `ods.event_v` за все доступные дни;
|
||||
- [ ] сверка через `FULL OUTER JOIN` дает сводку по дням и список кук;
|
||||
- [ ] при разборе расхождения указаны кука, день, два события и причина;
|
||||
- [ ] ваш запрос собирает сессии из всех событий `ods.event_v` за все
|
||||
доступные дни;
|
||||
- [ ] у вас есть сводка расхождений по дням и список кук, полученные через
|
||||
`FULL OUTER JOIN`;
|
||||
- [ ] вы объяснили причину одного расхождения и записали куку, день и два
|
||||
события, на которых оно видно;
|
||||
- [ ] уточненный расчет дает ноль расхождений во всех днях.
|
||||
@@ -1,16 +1,16 @@
|
||||
# Перепиши нарезку стенда
|
||||
# Перепишите нарезку стенда
|
||||
|
||||
**Чему учит:** изменить расчетную сущность внутри готового конвейера и
|
||||
проверить результат внешним эталоном.
|
||||
**Чему учит:** переписать расчет сессий в готовом конвейере и проверить
|
||||
результат по исходным событиям.
|
||||
|
||||
## Сначала прочитать
|
||||
|
||||
- [задание «Нарежь сессии сам»](3-1-sessions-by-hand.md);
|
||||
- [задание «Нарежьте сессии сами»](3-1-sessions-by-hand.md);
|
||||
- [модель сессии DDS](../architecture/dds/session.md);
|
||||
- [дневной конвейер трансформаций](../architecture/etl.md#объём-прогона);
|
||||
- [раскладка по шардам](../architecture/storage.md#раскладка-по-шардам).
|
||||
|
||||
## Граница задания
|
||||
## Что предстоит сделать
|
||||
|
||||
Даг, таблицы сессий и проверка их числа уже созданы.
|
||||
Перепишите только три файла:
|
||||
@@ -24,7 +24,7 @@
|
||||
служебная таблица, где день собирается перед заменой. В
|
||||
`sessions_vs_reference.sql` число сессий сравнивается с `VisitID` из ODS.
|
||||
|
||||
## Договор с дагом
|
||||
## Как даг запускает ваши запросы
|
||||
|
||||
Объем прогона — дни, которые соберет один запуск. Его вычисляет первый файл.
|
||||
|
||||
@@ -74,7 +74,7 @@ ClickHouse в `ALTER ... PARTITION` здесь не работает.
|
||||
Состав и типы строки заданы в `dds.session_rep`. Заполните начало, конец и
|
||||
длительность. Посчитайте все события, просмотры, добавления в корзину и
|
||||
покупки. Возьмите входную и выходную страницы, UTM первого события, устройство
|
||||
и город. Служебные колонки тоже входят в договор таблицы.
|
||||
и город. Заполните также служебные колонки из схемы таблицы.
|
||||
|
||||
Пишите через `dds.session_stage_dist`: место строки определит ключ
|
||||
`client_id`. При записи прямо в локальную таблицу оно зависело бы от ноды,
|
||||
@@ -178,15 +178,14 @@ Distributed-таблицу и такую ошибку не заметит.
|
||||
партицию. Расчет объема должен пропустить группу сессий: новых прожитых дней и
|
||||
дыр в слое нет.
|
||||
|
||||
## Приемка
|
||||
## Что должно получиться
|
||||
|
||||
- [ ] переписаны только три файла `sql/dds/session_*.sql`, инфраструктура
|
||||
упражнения не изменена;
|
||||
- [ ] запуск после удаления партиции восстановил выбранный день и завершил
|
||||
`etl_pipeline` успешно;
|
||||
- [ ] сохраненные `session_id`, границы, длительность и счетчики десяти строк
|
||||
совпали после пересборки;
|
||||
- [ ] `sessions_vs_reference` дает ноль расхождений во всех прожитых днях;
|
||||
- [ ] для одной куки вручную сверены границы и атрибуты сессии;
|
||||
- [ ] пересчет ключа по `hostName()` не нашел строк на чужом шарде;
|
||||
- [ ] повторный прогон без новой дыры пропустил группу сессий.
|
||||
- [ ] вы переписали три файла `sql/dds/session_*.sql`, сохранив даг,
|
||||
таблицы и проверку стенда;
|
||||
- [ ] ваш расчет восстановил удаленный день, `etl_pipeline` завершился успешно;
|
||||
- [ ] вы сверили десять сохраненных строк: `session_id`, границы,
|
||||
длительность и счетчики совпали после пересборки;
|
||||
- [ ] вы получили ноль расхождений в `sessions_vs_reference` за все
|
||||
прожитые дни и вручную сверили границы и атрибуты сессии одной куки;
|
||||
- [ ] ваш запрос с `hostName()` не нашел строк на чужом шарде;
|
||||
- [ ] повторный прогон без удаления данных пропустил группу сессий.
|
||||
@@ -1,7 +1,7 @@
|
||||
# Сначала сторож, потом ломай: заказы
|
||||
# Научите проверки замечать ошибки в заказах
|
||||
|
||||
**Чему учит:** до рискованного изменения задавать проверке явный договор и
|
||||
испытывать его управляемой ошибкой.
|
||||
**Чему учит:** написать проверки заказов до изменения их обработки
|
||||
и убедиться на намеренной ошибке, что проверки ее замечают.
|
||||
|
||||
## Сначала прочитать
|
||||
|
||||
@@ -13,30 +13,30 @@
|
||||
и на ее включение в [`dq_check`](../../dags/dq_check.py). Готового ответа для
|
||||
двух проверок из этого задания в репозитории нет.
|
||||
|
||||
## Граница задания
|
||||
## Что предстоит сделать
|
||||
|
||||
Добавьте два сторожа:
|
||||
Добавьте две проверки и включите их в даг:
|
||||
|
||||
- `sql/dq/order_items_total.sql` проверяет сумму позиций заказа;
|
||||
- `sql/dq/orders_vs_ods.sql` сверяет DDS с его источником;
|
||||
- `dags/dq_check.py` запускает обе проверки, публикует их строки в
|
||||
`dm.dq_summary` и создает для них отдельные задачи.
|
||||
|
||||
На этом шаге не меняйте разбор ODS и сборку DDS. Даги, DDL, таблицы-доноры,
|
||||
Kafka и службы остаются несущей частью упражнения.
|
||||
Разбор ODS и сборку DDS вы перепишете в следующих заданиях. Здесь сохраните
|
||||
их, даги загрузки, DDL, таблицы-доноры, Kafka и службы как есть.
|
||||
|
||||
Перед опытами остановите автоматическое движение мира: поставьте `world_live`
|
||||
на паузу и дождитесь завершения активных прогонов. До чистой точки остановки не
|
||||
на паузу и дождитесь завершения активных прогонов. До завершения опытов не
|
||||
запускайте руками `world_next_day`, `world_live_day`, `world_initialize` и
|
||||
`world_recreate`. Это условие стабильного модельного времени: одной паузы
|
||||
`world_live` для него мало.
|
||||
|
||||
## Два договора качества
|
||||
## Что будут проверять ваши запросы
|
||||
|
||||
Обе проверки следуют договору `dq_check`: запрос возвращает по одной строке
|
||||
Чтобы `dq_check` мог запустить проверку, ее запрос возвращает по одной строке
|
||||
на день с колонками `data_date`, `reference_rows`, `actual_rows`,
|
||||
`failed_rows`, `diagnostics`. Даг сохраняет в `dm.dq_summary` только счетчики,
|
||||
статус и координаты запуска. Ограниченные примеры из `diagnostics` ищите в
|
||||
статус и координаты запуска. Примеры из `diagnostics` ищите в
|
||||
журнале упавшей задачи Airflow. Ограничьте их тем же малым числом, что и
|
||||
существующие запросы в `sql/dq/`.
|
||||
|
||||
@@ -46,17 +46,18 @@ Kafka и службы остаются несущей частью упражн
|
||||
посчитайте сумму `item_price * item_quantity` и сравните ее с `items_total`.
|
||||
`reference_rows` считает ключи со стороны `items_total`, `actual_rows` - ключи
|
||||
со стороны вычисленной суммы, а `failed_rows` - несовпавшие значения. В
|
||||
исправном наборе первые два счетчика равны по построению; сам инвариант выражен
|
||||
через `failed_rows`. Для диагностики достаточно `order_id`, ожидаемой суммы и
|
||||
исправном наборе первые два счетчика равны; несовпадения сумм считает
|
||||
`failed_rows`. Для диагностики достаточно `order_id`, ожидаемой суммы и
|
||||
суммы позиций.
|
||||
|
||||
Проверка относится к типизированному разбору массива `items` на границе
|
||||
ODS -> DDS. Скидка, доставка и равенство `total` в этот договор не входят.
|
||||
ODS -> DDS. Скидку, доставку и равенство `total` здесь проверять не нужно.
|
||||
|
||||
### DDS против ODS
|
||||
|
||||
Имя проверки - `orders_vs_ods`. Источник модели DDS здесь - текущее состояние
|
||||
`ods.order_v`. Внутреннее состояние генератора в этот договор не входит.
|
||||
`ods.order_v`. С ним вы и сравниваете DDS; обращаться к внутреннему состоянию
|
||||
генератора для этой проверки не нужно.
|
||||
Одним запросом соберите обе стороны на зерне дня заказа и сравните:
|
||||
|
||||
- число заказов;
|
||||
@@ -131,18 +132,17 @@ ORDER BY check_name;
|
||||
Проверка суммы должна охватывать каждый день `dds.order_v`. Сверка слоев
|
||||
должна охватывать объединение дней ODS и DDS, включая дни только одной стороны.
|
||||
|
||||
## Когда можно остановиться
|
||||
## Завершите опыт
|
||||
|
||||
После двух восстановлений выбранный заказ снова совпадает с ODS. Все семь
|
||||
проверок дают зеленый результат. В таком состоянии можно закончить работу или
|
||||
После восстановления заказа и успешного `dq_check` можно закончить работу или
|
||||
перейти к разбору заказов, оставив модельное время на паузе.
|
||||
|
||||
## Приемка
|
||||
## Что должно получиться
|
||||
|
||||
- [ ] две новые проверки встроены в `dq_check` и публикуют дневные строки по
|
||||
принятому договору;
|
||||
- [ ] каждая проверка дала красный результат на своей управляемой ошибке;
|
||||
- [ ] журналы упавших задач содержали ограниченную диагностику;
|
||||
- [ ] `etl_pipeline` восстановил обе измененные строки DDS;
|
||||
- [ ] `order_items_total`, `orders_vs_ods` и пять прежних DQ зелены;
|
||||
- [ ] SQL разбора ODS и сборки DDS на этом шаге не менялся.
|
||||
- [ ] две новые проверки запускаются в `dq_check`, а их дневные результаты
|
||||
появляются в `dm.dq_summary`;
|
||||
- [ ] вы увидели ошибку каждой проверки после соответствующего изменения
|
||||
заказа, а в журнале нашли примеры заказов с расхождениями;
|
||||
- [ ] вы восстановили заказ через `etl_pipeline` после обоих опытов;
|
||||
- [ ] `order_items_total`, `orders_vs_ods` и пять прежних проверок успешны;
|
||||
- [ ] разбор ODS и сборка DDS остались прежними.
|
||||
@@ -1,7 +1,7 @@
|
||||
# Перепишите разбор заказов в ODS
|
||||
|
||||
**Чему учит:** сначала измерить обе ветви разбора на управляемом срезе, затем
|
||||
переписать переход и повторить тот же опыт.
|
||||
**Чему учит:** переписать разбор заказов и на пробных сообщениях проверить,
|
||||
что годные строки и брак попадают в свои таблицы без потерь.
|
||||
|
||||
## Сначала прочитать
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
- [`orders_ingest`](../../dags/orders_ingest.py);
|
||||
- [`order_parse.sql`](../../sql/ods/order_parse.sql).
|
||||
|
||||
Готового запроса для сверки управляемого среза в репозитории нет.
|
||||
Запрос для проверки пробных строк вы напишете сами.
|
||||
|
||||
## Граница задания
|
||||
## Что предстоит сделать
|
||||
|
||||
Сначала проведите опыт на готовом `order_parse.sql`. Затем удалите его SQL,
|
||||
соберите разбор заново и повторите опыт с другим маркером.
|
||||
@@ -72,7 +72,7 @@ jq -cn --arg marker "$LAB_MARKER" --arg day "$LAB_DAY" '
|
||||
' > "$LAB_BATCH"
|
||||
```
|
||||
|
||||
Отправьте файл в обычный топик заказов штатным производителем контейнера:
|
||||
Отправьте файл в обычный топик заказов утилитой из контейнера Kafka:
|
||||
|
||||
```bash
|
||||
docker compose exec -T kafka /opt/kafka/bin/kafka-console-producer.sh \
|
||||
@@ -123,7 +123,7 @@ SETTINGS mutations_sync = 2;
|
||||
|
||||
В ClickHouse 26.3 мутации выполняются в фоне. С `mutations_sync = 2` команда
|
||||
ждет их завершения на всех репликах. Запросами к трем Distributed-таблицам
|
||||
докажите, что строк маркера не осталось, затем удалите временный файл.
|
||||
убедитесь, что строк маркера не осталось, затем удалите временный файл.
|
||||
|
||||
Группа `clickstream_orders` уже подтвердила эти сообщения, поэтому обычный
|
||||
повтор `orders_ingest` их не прочитает. В Kafka они остаются до удаления по
|
||||
@@ -137,8 +137,8 @@ SETTINGS mutations_sync = 2;
|
||||
|
||||
## Перепишите разбор
|
||||
|
||||
Удалите готовый SQL из `sql/ods/order_parse.sql` и сохраните его договор с
|
||||
`orders_ingest`:
|
||||
Удалите готовый SQL из `sql/ods/order_parse.sql` и напишите свой. Чтобы
|
||||
`orders_ingest` мог его использовать, сохраните следующие условия:
|
||||
|
||||
- вход - строки `stg.orders_raw_dist` одного `{load_id:String}`;
|
||||
- каждая строка среза попадает ровно в один результат: годный заказ или брак;
|
||||
@@ -161,21 +161,20 @@ SETTINGS mutations_sync = 2;
|
||||
шард. Затем очистите второй срез теми же адресными командами и запустите
|
||||
`dq_check`.
|
||||
|
||||
## Когда можно остановиться
|
||||
## Завершите опыт
|
||||
|
||||
Оба маркера отсутствуют в STG, ODS, таблице брака и DDS; временные файлы
|
||||
удалены. Семь DQ дают зеленый результат. В таком состоянии можно перейти к
|
||||
Убедитесь, что оба маркера отсутствуют в STG, ODS, таблице брака и DDS,
|
||||
временные файлы удалены, а семь проверок успешны. Теперь можно перейти к
|
||||
сборке DDS, не снимая паузу с модельного времени. Если вы прерываетесь и до
|
||||
опыта `world_live` работал, снимите его с паузы.
|
||||
|
||||
## Приемка
|
||||
## Что должно получиться
|
||||
|
||||
- [ ] до переписывания зафиксированы баланс 4 = 1 + 3, три класса брака,
|
||||
деловые поля текущей версии и фактический шард;
|
||||
- [ ] переписан только `sql/ods/order_parse.sql`, несущая инфраструктура не
|
||||
изменена;
|
||||
- [ ] после переписывания новый срез дал тот же результат;
|
||||
- [ ] обе годные строки различали `created_at` и `updated_at`;
|
||||
- [ ] проверки `hostName()` не нашли строки ODS на чужом шарде;
|
||||
- [ ] строки обоих опытов удалены только по своим маркерам;
|
||||
- [ ] после очистки общий конвейер и семь DQ находятся в зеленом состоянии.
|
||||
- [ ] у вас сохранены результаты до переписывания: четыре строки дали
|
||||
один заказ и три класса брака; вы сверили поля текущей версии и шард;
|
||||
- [ ] вы переписали `sql/ods/order_parse.sql`, сохранив остальные части стенда;
|
||||
- [ ] ваш разбор дал тот же результат на новой пробной пачке;
|
||||
- [ ] вы проверили разные значения `created_at` и `updated_at` у годной
|
||||
строки в обоих опытах и подтвердили правильный шард через `hostName()`;
|
||||
- [ ] строки обоих опытов удалены по их маркерам;
|
||||
- [ ] после очистки конвейер и все семь проверок успешны.
|
||||
@@ -1,7 +1,7 @@
|
||||
# Перепишите сборку заказов в DDS
|
||||
|
||||
**Чему учит:** переписать пересборку заказов по состоянию слоев и доказать ее
|
||||
результат на пропущенном закрытом дне.
|
||||
**Чему учит:** настроить пересборку заказов по состоянию ODS и DDS
|
||||
и проверить, что она восстанавливает пропущенный день за пределами текущего окна.
|
||||
|
||||
## Сначала прочитать
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
- [дневной конвейер](../architecture/etl.md#объём-прогона);
|
||||
- [раскладку по шардам](../architecture/storage.md#раскладка-по-шардам).
|
||||
|
||||
## Граница задания
|
||||
## Что предстоит сделать
|
||||
|
||||
Удалите готовый SQL и перепишите только три файла:
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
- `sql/dds/order_rebuild.sql`;
|
||||
- `sql/dds/order_replace.sql`.
|
||||
|
||||
Даг `dds_transform`, DDL, донор, ODS и обе проверки из первого задания уже
|
||||
задают границу работы. Перед началом убедитесь, что пробные строки предыдущего
|
||||
Даг `dds_transform`, DDL, донор, ODS и обе проверки из первого задания
|
||||
оставьте как есть. Перед началом убедитесь, что пробные строки предыдущего
|
||||
задания удалены, а семь DQ дают зеленый результат.
|
||||
|
||||
Поставьте `world_live` на паузу и дождитесь завершения активных прогонов. До
|
||||
@@ -28,7 +28,7 @@
|
||||
`world_recreate`. `world_next_day` понадобится только в явно указанном ниже
|
||||
случае.
|
||||
|
||||
## Договор с дагом
|
||||
## Как даг запускает ваши запросы
|
||||
|
||||
Сохраните интерфейс `dds_transform`:
|
||||
|
||||
@@ -103,22 +103,21 @@ DROP PARTITION 'YYYY-MM-DD';
|
||||
заказов снова должна пересобрать текущее изменяемое окно. Контрольные суммы
|
||||
деловых полей остаются прежними, а все семь проверок дают зеленый результат.
|
||||
|
||||
## Когда можно остановиться
|
||||
## Завершите опыт
|
||||
|
||||
Удаленная партиция восстановлена, ее число строк и контрольная сумма совпали
|
||||
с исходными. На чужих шардах строк нет, повторный прогон не изменил деловые
|
||||
данные, семь DQ зелены. Если до упражнения `world_live` работал, теперь его
|
||||
можно снять с паузы.
|
||||
После восстановления партиции и успешных проверок снимите `world_live`
|
||||
с паузы, если он работал до упражнения.
|
||||
|
||||
## Приемка
|
||||
## Что должно получиться
|
||||
|
||||
- [ ] переписаны только три файла `sql/dds/order_*.sql`, инфраструктура
|
||||
упражнения не изменена;
|
||||
- [ ] удален ближайший к окну закрытый день, а красный DQ зафиксирован до
|
||||
- [ ] вы переписали три файла `sql/dds/order_*.sql`, сохранив остальные
|
||||
части стенда;
|
||||
- [ ] вы удалили ближайший к окну закрытый день и увидели ошибку DQ до
|
||||
восстановления;
|
||||
- [ ] `first_day` начался с удаленного дня, задачи замены покрыли непрерывный
|
||||
отрезок до конца окна;
|
||||
- [ ] число заказов и контрольная сумма восстановленного дня совпали;
|
||||
- [ ] `order_items_total`, `orders_vs_ods` и пять прежних DQ зелены;
|
||||
- [ ] проверка `hostName()` не нашла заказов DDS на чужом шарде;
|
||||
- [ ] повторный `etl_pipeline` сохранил деловые данные и зеленые проверки.
|
||||
- [ ] ваш расчет выбрал `first_day` с удаленного дня и заменил партиции
|
||||
всего отрезка до конца окна;
|
||||
- [ ] вы сверили число заказов и контрольную сумму восстановленного дня
|
||||
с сохраненными значениями; они совпали;
|
||||
- [ ] `order_items_total`, `orders_vs_ods` и пять прежних проверок успешны;
|
||||
- [ ] ваш запрос с `hostName()` не нашел заказов на чужом шарде;
|
||||
- [ ] повторный `etl_pipeline` сохранил деловые данные, проверки прошли.
|
||||
@@ -15,7 +15,7 @@
|
||||
Готовый стендовый разбор находится в `sql/ddl/30-ods-views.sql`. Откройте его
|
||||
после собственной сверки: в нем лежит ответ на основную часть задания.
|
||||
|
||||
## Граница задания
|
||||
## Что предстоит сделать
|
||||
|
||||
Создайте в базе `default` одну свою таблицу годных событий и наполните ее одним
|
||||
пакетным `INSERT SELECT` из `stg.hits_raw_dist`. Работайте с первой нодой под
|
||||
@@ -72,11 +72,10 @@ Airflow. При включенном `world_live` посмотрите его т
|
||||
`ods.event_rep`, а также `_load_ts`. Тип `purchaseRevenue` возьмите из ODS:
|
||||
`Array(Decimal(18, 2))`.
|
||||
|
||||
Схлопните повтор доставки по договору ODS. Возьмите
|
||||
Обработайте повторные доставки так же, как ODS. Возьмите
|
||||
`ReplacingMergeTree(_load_ts)`, партицию `EventDate` и полный ключ замены:
|
||||
`(CounterID, EventDate, intHash32(ClientID), WatchID)`. Достаточно одной
|
||||
локальной таблицы: отдельная Distributed-таблица не добавит урока к пакетному
|
||||
опыту на одной ноде.
|
||||
локальной таблицы: загрузка и сверка в этом опыте выполняются на одной ноде.
|
||||
|
||||
`WatchID` — логический идентификатор события, уникальный по
|
||||
[договору генератора](../specs/2026-08-01-generator.md). Физический ключ ODS
|
||||
@@ -85,8 +84,7 @@ Airflow. При включенном `world_live` посмотрите его т
|
||||
|
||||
## Разберите годные строки
|
||||
|
||||
Для каждой строки среза примите решение по тому же строгому входу, что и
|
||||
стенд:
|
||||
Для каждой строки среза проверьте те же условия приема, что и на стенде:
|
||||
|
||||
1. корень `raw` — JSON-объект;
|
||||
2. набор верхнеуровневых ключей в точности равен 47 ключам формата;
|
||||
@@ -95,29 +93,31 @@ Airflow. При включенном `world_live` посмотрите его т
|
||||
|
||||
Напишите разбор, который не прерывает пакет из-за одной грязной строки. Сами
|
||||
выберите способ безопасного извлечения пяти опорных полей, форму общих выражений
|
||||
и место фильтра. Остальные поля извлеките по договору формата.
|
||||
и место фильтра. Остальные поля извлеките по описанию формата.
|
||||
|
||||
Перенесите `_load_ts` из STG без пересчета: это время исходной доставки и
|
||||
колонка версии. Время запуска вашего запроса сюда не входит. Вставьте в свою
|
||||
таблицу только годные строки выбранного среза.
|
||||
|
||||
Считайте отвергнутые строки прямо в исходном срезе: отдельная таблица брака в
|
||||
задание не входит. Проверьте работу строгого входа одним запросом. Посчитайте:
|
||||
Считайте отвергнутые строки прямо в исходном срезе, без отдельной таблицы
|
||||
брака. Одним запросом посчитайте:
|
||||
|
||||
- все доставки;
|
||||
- строки, прошедшие ваш предикат;
|
||||
- строки, отвергнутые его отрицанием.
|
||||
- строки, прошедшие ваше условие отбора;
|
||||
- строки, для которых это условие ложно.
|
||||
|
||||
Проверьте равенство общего числа строк сумме прошедших и отвергнутых. Так каждая
|
||||
исходная строка учитывается ровно в одной группе. Если отвергнутые строки есть,
|
||||
сохраните их `raw`, `_load_ts` и координаты Kafka. Найдите те же
|
||||
координаты в `ods.event_errors_dist` и запишите стендовый `error_class`. Если
|
||||
брака нет, так и зафиксируйте: опыт проверил успешную ветвь и выполнение
|
||||
предиката на этом срезе, но не классификатор ошибок. Подкладывать грязные
|
||||
условия отбора на этом срезе, но не классификатор ошибок. Подкладывать грязные
|
||||
сообщения ради задания не требуется.
|
||||
|
||||
## Приведите стороны к одному зерну
|
||||
|
||||
### Учтите повторные доставки
|
||||
|
||||
В STG одна строка представляет доставку. Повтор сообщения остается второй
|
||||
строкой. В `ods.event_v` одна строка представляет актуальное событие, потому
|
||||
что представление читает физическую таблицу с `FINAL`.
|
||||
@@ -126,17 +126,20 @@ Airflow. При включенном `world_live` посмотрите его т
|
||||
полных ключей среди годных доставок. Разница между числом доставок и этим
|
||||
счетом показывает повторы в исходном срезе.
|
||||
|
||||
Область эталона постройте независимо от своего предиката и результата вставки.
|
||||
Снова возьмите весь исходный STG-срез. По координатам Kafka исключите доставки,
|
||||
которые есть в `ods.event_errors_dist`. У оставшихся доставок извлеките
|
||||
`WatchID` без риска прервать запрос и выберите эти события из свежего
|
||||
`ods.event_v`.
|
||||
### Выберите события ODS для сравнения
|
||||
|
||||
Свяжите сырую доставку с ODS по `WatchID`: по договору генератора это уникальный
|
||||
идентификатор события. Он задает только область эталона. Дедупликация и
|
||||
последующее соединение используют полный физический ключ. Не берите область из
|
||||
своей таблицы и не применяйте к ней свой предикат. Иначе ошибочно отвергнутая
|
||||
строка исчезнет с обеих сторон сверки.
|
||||
Теперь нужно найти в ODS события из выбранного среза. Список стройте по
|
||||
исходным доставкам: если взять его из своей таблицы, ошибочно отвергнутое
|
||||
событие исчезнет с обеих сторон сверки и потеря останется незамеченной.
|
||||
|
||||
Снова возьмите весь исходный STG-срез. По координатам Kafka исключите
|
||||
доставки, которые есть в `ods.event_errors_dist`. У оставшихся извлеките
|
||||
`WatchID` без риска прервать запрос и выберите эти события из свежего
|
||||
`ods.event_v`. Свое условие отбора здесь не применяйте: решение о годности
|
||||
доставки вы берете из результата стендового разбора.
|
||||
|
||||
`WatchID` связывает доставку с событием в ODS. Для удаления повторов и
|
||||
сравнения полей дальше используйте полный физический ключ из схемы таблицы.
|
||||
|
||||
## Сверьте 47 полей
|
||||
|
||||
@@ -171,44 +174,46 @@ ClickHouse по умолчанию заполняет отсутствующую
|
||||
|
||||
Для измененной строки назовите поля, которые расходятся, и сравните каждое с
|
||||
сырой строкой. Для пропавшей или лишней строки проверьте пять опорных полей и
|
||||
решение строгого предиката. Если координаты доставки есть в
|
||||
результат условия отбора. Если координаты доставки есть в
|
||||
`ods.event_errors_dist`, посмотрите `error_class`.
|
||||
|
||||
Исправьте разбор, удалите и заново создайте только собственную таблицу, затем
|
||||
повторите вставку того же среза. Остановитесь, когда счет исходных доставок по
|
||||
прежним границам не изменился, баланс строгого входа сошелся, а построчная
|
||||
сверка пуста.
|
||||
прежним границам не изменился, число годных и отвергнутых строк в сумме дает
|
||||
число исходных доставок, а построчная сверка пуста.
|
||||
|
||||
## Где заканчивается проверка
|
||||
## Чего не подтверждает сравнение с ODS
|
||||
|
||||
`ods.event_v` — образец для сравнения. Оба разбора могли повторить одну
|
||||
ошибку. Для пяти опорных полей у вас есть проверка разбираемости и исходный
|
||||
JSON. Остальные 42 поля можно вручную сопоставить с `raw`, однако второго
|
||||
независимого источника для всех значений на стенде нет.
|
||||
ошибку. Для пяти опорных полей у вас есть проверка преобразования в нужный тип
|
||||
и исходный JSON. Остальные 42 поля можно вручную сопоставить с `raw`, однако
|
||||
второго независимого источника для всех значений на стенде нет.
|
||||
|
||||
## Уберите свой объект
|
||||
|
||||
Сохраните DDL таблицы, запрос разбора, границы среза, баланс строгого входа и
|
||||
пустую итоговую сверку. Затем удалите свою таблицу командой `DROP TABLE` с ее
|
||||
точным именем. Убедитесь через `system.tables`, что объекта больше нет.
|
||||
Сохраните DDL таблицы, запрос разбора, границы среза, число исходных, годных
|
||||
и отвергнутых строк и итоговую сверку без расхождений. Запишите также, чего
|
||||
не подтверждает сравнение с ODS и как TTL ограничивает время для повторения
|
||||
опыта. Затем удалите свою таблицу командой `DROP TABLE` с ее точным именем.
|
||||
Убедитесь через `system.tables`, что объекта больше нет.
|
||||
Прикладные строки стенда вы не меняли, поэтому удалять данные из STG, ODS или
|
||||
Kafka после опыта не нужно.
|
||||
|
||||
## Приемка
|
||||
## Что должно получиться
|
||||
|
||||
- [ ] записаны границы малого завершенного среза, его счет и координаты
|
||||
- [ ] у вас сохранены границы выбранного среза, число строк и координаты
|
||||
доставки по партициям Kafka;
|
||||
- [ ] создана одна собственная таблица со всеми 47 полями, перенесенным
|
||||
`_load_ts` и физическим договором дедупликации ODS;
|
||||
- [ ] пакетный разбор принимает только JSON-объекты с точным набором ключей и
|
||||
пятью разбираемыми опорными полями;
|
||||
- [ ] счет исходных строк равен сумме годных и отвергнутых, а найденный брак
|
||||
рассмотрен по `raw`, координатам и стендовому классу;
|
||||
- [ ] область ODS получена из всех координат исходного среза и стендовой
|
||||
таблицы ошибок, независимо от собственного предиката и таблицы;
|
||||
- [ ] обе стороны приведены к зерну полного физического ключа;
|
||||
- [ ] `FULL OUTER JOIN` сравнивает все 47 полей, а числа пропавших, лишних и
|
||||
измененных строк равны нулю;
|
||||
- [ ] отдельно подтвержден перенос `_load_ts` и учтены повторы доставки;
|
||||
- [ ] границы стендового оракула и TTL записаны вместе с результатом;
|
||||
- [ ] собственная таблица удалена, объекты и данные стенда не изменены.
|
||||
- [ ] ваша таблица содержит все 47 полей и `_load_ts` и обрабатывает
|
||||
повторы по тем же правилам, что и ODS;
|
||||
- [ ] ваш разбор принимает только JSON-объекты с точным набором ключей и
|
||||
пятью опорными полями, которые преобразуются в нужные типы;
|
||||
- [ ] вы сверили число исходных строк с суммой годных и отвергнутых,
|
||||
а найденный брак разобрали по `raw`, координатам и классу ошибки стенда;
|
||||
- [ ] вы выбрали события ODS по исходному срезу и таблице ошибок,
|
||||
независимо от своей таблицы и условия отбора;
|
||||
- [ ] вы сравнили обе стороны по полному физическому ключу через
|
||||
`FULL OUTER JOIN`: во всех 47 полях нет расхождений,
|
||||
пропавших и лишних строк тоже нет;
|
||||
- [ ] вы отдельно проверили перенос `_load_ts` и учли повторные доставки;
|
||||
- [ ] вы записали ограничения сравнения с ODS и срок повторения опыта;
|
||||
- [ ] после опыта вы удалили свою таблицу, сохранив объекты и данные стенда.
|
||||
+15
-16
@@ -99,21 +99,20 @@
|
||||
[«Данные»](../architecture/monitoring/data.md). Устройство живого дня и
|
||||
доставки слепка описано в [главе об Airflow](../handbook/airflow-dags.md).
|
||||
|
||||
## Приемка
|
||||
## Что должно получиться
|
||||
|
||||
- [ ] для каждого из трех дашбордов записан вопрос, на который он отвечает;
|
||||
- [ ] сохранены окно, текст и показатели верхнего запроса, определены его
|
||||
источник и назначение;
|
||||
- [ ] среди запросов открытых дашбордов найден тот, который прочитал больше
|
||||
всего строк, и соответствующая панель;
|
||||
- [ ] объяснено, как журнал собирают с двух нод и какие записи учитывают для
|
||||
счета запросов;
|
||||
- [ ] до наблюдения нарисован прогноз свежести событий и заказов, а форма
|
||||
линии объяснена через метку последней доставки;
|
||||
- [ ] записаны несколько наблюдений, на наброске видны рост и сброс свежести
|
||||
заказов, а доставка подтверждена в Airflow и на панели слепков;
|
||||
- [ ] модельная дата отделена от реального времени наблюдения;
|
||||
- [ ] объяснен выбор слоев для свежести, потока и доли брака, а также общее
|
||||
окно числителя и знаменателя;
|
||||
- [ ] службы не остановлены, данные вручную не правились, стендовые дашборды
|
||||
не изменены.
|
||||
- [ ] у вас сохранены окно, текст и показатели верхнего запроса;
|
||||
вы выяснили, кто его отправил и зачем;
|
||||
- [ ] вы нашли запрос открытых дашбордов, который прочитал больше всего
|
||||
строк, и панель, которая его отправила;
|
||||
- [ ] вы можете объяснить, зачем журнал собирают с двух нод и какие записи
|
||||
учитывают при подсчете запросов;
|
||||
- [ ] у вас есть прогноз свежести, сделанный до наблюдения, и несколько
|
||||
замеров; рост и сброс на рисунке объяснены через время последней доставки;
|
||||
- [ ] вы подтвердили доставку заказов в Airflow и на панели слепков,
|
||||
подписали модельную дату отдельно от реального времени наблюдений;
|
||||
- [ ] вы можете объяснить выбор слоев для свежести, потока и доли брака
|
||||
и почему числитель и знаменатель берут за одно окно;
|
||||
- [ ] после наблюдений службы продолжают работать, данные и стендовые
|
||||
дашборды остались без изменений.
|
||||
+19
-23
@@ -10,28 +10,28 @@
|
||||
|---|---|---|
|
||||
| 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) | изучить разбор потока отдельно от живого приема и сверить его со стендовым результатом |
|
||||
| 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
|
||||
|
||||
Слой DM в стенде построен наполовину: витрины трех сюжетов дашборда и контроль
|
||||
качества уже построены, остальные пять витрин строите вы. Каждая из них —
|
||||
самостоятельное ТЗ: в нем есть состав витрины, зерно, источники и способ
|
||||
самостоятельное задание: в нем есть состав витрины, зерно, источники и способ
|
||||
проверки.
|
||||
|
||||
| № | Результат | Чему учит |
|
||||
|---|---|---|
|
||||
| 1 | [`events_enriched_v`](1-1-events-enriched.md) | строить слой как договор о данных, а не как их копию |
|
||||
| 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_` как предупреждение о сумме со слов клиента |
|
||||
|
||||
Общее устройство слоя — [дока DM](../architecture/dm.md). Она отвечает на
|
||||
Общее устройство слоя — [справочник DM](../architecture/dm.md). Он отвечает на
|
||||
вопросы «почему так», задание отвечает на вопрос «что сделать».
|
||||
|
||||
## Как принято на ступени 1
|
||||
@@ -99,9 +99,8 @@ DM](../architecture/dm.md).
|
||||
собрано верно. В сводку качества конвейера `dm.dq_summary` он не пишет: это
|
||||
инструмент лабы, а не история качества стенда.
|
||||
|
||||
Проверка пяти заданий по витринам устроена одинаково и золотых констант не
|
||||
содержит: рядом с витриной независимым запросом собирается тот же ответ —
|
||||
прямо из источника витрины, минуя ее саму. У четырех заданий источник
|
||||
Для каждой витрины проверка независимо рассчитывает ожидаемый результат
|
||||
прямо по ее источнику. У четырех заданий источник
|
||||
эталона тот же, что у витрины, `dds.session_v` или `dds.event_v`; у задания
|
||||
4 это таблицы брака ODS, потому что в DDS нет сущности брака. Обе стороны
|
||||
соединяются по деловому ключу через `FULL OUTER JOIN`, и считается число
|
||||
@@ -109,16 +108,13 @@ DM](../architecture/dm.md).
|
||||
соединение сохраняет ключи обеих сторон: при внутреннем пропавший день или
|
||||
ключ не попал бы в результат, и потеря осталась бы незамеченной.
|
||||
|
||||
Раздел «Как проверить себя» каждого ТЗ по витрине называет деловой ключ и
|
||||
Раздел «Как проверить себя» каждого задания по витрине называет деловой ключ и
|
||||
сравниваемые величины. Тот же прием в стендовом исполнении — [даг
|
||||
`dq_check`](../../dags/dq_check.py) и запросы в [`sql/dq/`](../../sql/dq).
|
||||
|
||||
**Предупреждение.** Запросы самой проверки лежат в `sql/dq/` открыто, и
|
||||
имена называют витрину и роль сверки. Проверка на инвариантах неизбежно
|
||||
близка к ответу: чтобы сверить витрину, надо ее пересчитать. Если открыть
|
||||
эти файлы до выполнения задания, можно скопировать готовый SQL, не
|
||||
разобравшись в расчете самостоятельно. Смотреть туда стоит после своей
|
||||
сборки: сравнить два решения полезно, списать одно — нет.
|
||||
Запросы проверок лежат в `sql/dq/`. Откройте их после собственного решения:
|
||||
они заново рассчитывают показатели витрины, поэтому содержат ответ на
|
||||
существенную часть задания. Сравните свой способ расчета с готовым.
|
||||
|
||||
## Факультативы
|
||||
|
||||
@@ -152,12 +148,12 @@ DM](../architecture/dm.md).
|
||||
|
||||
### Что делать, когда стенд сломался
|
||||
|
||||
Открывается после ступени 7. Поломки стенда и runbook по итогам
|
||||
расследований; весь раздел — повышенная сложность.
|
||||
Открывается после ступени 7. Поломки стенда и инструкции по восстановлению
|
||||
по итогам расследований; весь раздел — повышенная сложность.
|
||||
|
||||
## Уборка
|
||||
|
||||
Черновики и опыты за собой сносить: правило проверок стенда —
|
||||
[дока тестирования](../architecture/testing.md). Снесенная витрина обязана
|
||||
снова покраснить свою задачу в `homework_check` — если не покраснила,
|
||||
проверка ничего и не проверяла.
|
||||
После опытов удаляйте свои временные объекты. Если удалили учебную витрину,
|
||||
ее задача в `homework_check` должна снова завершиться ошибкой: отсутствие
|
||||
витрины тоже проверяется. Подробнее — в
|
||||
[справочнике проверок](../architecture/testing.md).
|
||||
@@ -40,6 +40,20 @@
|
||||
- *расследование* — данные уже лежат на стенде или стенд намеренно ломается, а
|
||||
менти ищет причину и объясняет ее.
|
||||
|
||||
### Как писать задания
|
||||
|
||||
Пишите для учащегося: объясняйте, чему он научится, зачем нужны действия
|
||||
и как понять результат. Историю разработки оставляйте за пределами задания.
|
||||
|
||||
«Как проверить себя» описывает действия для проверки и поиска ошибок.
|
||||
«Что должно получиться» — результат работы и то, что учащийся теперь может
|
||||
объяснить. Если проверка уже встроена в шаги упражнения, повторять ее
|
||||
отдельным разделом не нужно.
|
||||
|
||||
Остальные заголовки и структуру выбирайте по содержанию. Образцы — задания
|
||||
про [дашборд Superset](../labs/optional/superset-dashboard.md) и
|
||||
[расследование подключения](../labs/optional/superset-missing-object.md).
|
||||
|
||||
## 3. Основная программа — семь ступеней
|
||||
|
||||
### Ступень 1. Построй пять витрин слоя DM
|
||||
@@ -64,7 +78,7 @@
|
||||
ступени предстоит написать при реализации программы. Полный собственный дашборд
|
||||
остается факультативом в разделе «Superset и BI».
|
||||
|
||||
### Ступень 3. Нарежь сессии сам
|
||||
### Ступень 3. Нарежьте сессии сами
|
||||
|
||||
До того как открыть стендовый SQL нарезки сессий в DDS, менти самостоятельно
|
||||
делит события на визиты. Сначала он сравнивает число своих сессий с эталоном
|
||||
@@ -75,7 +89,7 @@
|
||||
Форма — построить свое по ТЗ и провести расследование. Обратная связь состоит
|
||||
из счетной сверки с `VisitID` и ручного сопоставления границ.
|
||||
|
||||
### Ступень 4. Перепиши нарезку стенда
|
||||
### Ступень 4. Перепишите нарезку стенда
|
||||
|
||||
Менти удаляет и заново пишет SQL нарезки сессий DDS: расчет объема, пересборку
|
||||
и замену. Урок — изменить расчетную сущность внутри готового конвейера и
|
||||
@@ -86,7 +100,7 @@
|
||||
Форма — вычитание. Обратную связь дают `dq_check` и поштучная сверка,
|
||||
построенная на третьей ступени.
|
||||
|
||||
### Ступень 5. Сначала сторож, потом ломай: заказы
|
||||
### Ступень 5. Научите проверки замечать ошибки в заказах
|
||||
|
||||
Сначала менти строит две недостающие DQ-проверки. Первая утверждает равенство
|
||||
«цена × количество = items_total». Вторая сверяет слой с источником по дням. Их
|
||||
|
||||
Reference in new issue
Block a user