docs(course): переведены уроки на стартовую историю
- Зачем: - учебный путь должен идти через генерацию и штатный пайплайн, а не через архивный сид. - Что: - обновлены уроки 00-06 и стандарт урока под startup-history/backfill. - объяснено, что data/*.jsonl остаются кладовкой значений генератора. - тест-план переведён на новый штатный запуск и HITL-приёмку. - Проверка: - rg -n \"make clean/up/ddl/data/transform|make data|kafka_load|LIMIT=|2022-11-28|26 из 50\" docs/course docs/TEST_PLAN.md. - git diff --cached --check.
This commit is contained in:
@@ -65,35 +65,35 @@
|
||||
|
||||
## 2. Руки: смотрим базовый прогон
|
||||
|
||||
Поднимаем стенд, создаём схему, заливаем **малый срез** (50 строк на топик) и запускаем
|
||||
трансформацию:
|
||||
Поднимаем стенд и создаём стартовую историю. Это штатный путь курса: готовый источник
|
||||
данных стенда пишет события в Kafka, ClickHouse читает их в STG, затем batch строит
|
||||
ODS, DDS и DM. Файлы `data/*.jsonl` пока остаются только кладовкой значений для этого
|
||||
источника, а не источником аналитического контура.
|
||||
|
||||
```bash
|
||||
make up # поднять инфраструктуру
|
||||
make ddl # создать базы и таблицы (в т.ч. слой DDS)
|
||||
LIMIT=50 make data # залить по 50 строк каждого файла в Kafka → STG
|
||||
make transform # батч STG → ODS → DDS → DM
|
||||
make generated-history-analytics
|
||||
make up
|
||||
```
|
||||
|
||||
`make transform` прогоняет всю цепочку слоёв и по дороге печатает в консоль блок **«Статистика
|
||||
DDS»** — счётчики строк по двум нашим сущностям:
|
||||
Команда прогоняет всю цепочку слоёв и по дороге печатает в консоль блок **«Статистика DDS»** —
|
||||
счётчики строк по двум нашим сущностям:
|
||||
|
||||
```
|
||||
Статистика DDS:
|
||||
┌─table─────┬─rows─┐
|
||||
│ dds.click │ 26 │
|
||||
│ dds.event │ 50 │
|
||||
│ dds.click │ ... │
|
||||
│ dds.event │ ... │
|
||||
└───────────┴──────┘
|
||||
```
|
||||
|
||||
Прочитаем эти две строки.
|
||||
|
||||
**`dds.event` — 50.** Сколько событий пришло, столько карточек и собралось: одно событие — одна
|
||||
строка. Ровно как `ods.browser_event` из прошлого урока.
|
||||
**`dds.event` — события.** Сколько событий пришло с валидным ключом, столько карточек и
|
||||
собралось: одно событие — одна строка. Ровно как `ods.browser_event` из прошлого урока.
|
||||
|
||||
**`dds.click` — 26, а не 50.** И это та же история, что мы уже разбирали в уроке 2. Карточка
|
||||
клика — одна на клик, а в срезе на 50 событий разных кликов всего 26 (на один клик приходится
|
||||
несколько событий). Поэтому 50 событий ссылаются на 26 кликов — это нормально, так и должно быть.
|
||||
**`dds.click` обычно меньше, чем `dds.event`.** И это та же история, что мы уже разбирали
|
||||
в уроке 2. Карточка клика — одна на клик, а событий на один клик может быть несколько.
|
||||
Поэтому много событий ссылаются на меньшее число кликов — это нормально, так и должно быть.
|
||||
|
||||
Теперь — главный счётчик урока. Он печатается чуть ниже, в блоке **«Сводка по качеству данных»**
|
||||
(это таблица `dm.dq_summary`, куда стенд складывает метрики по всем слоям). Найди в ней строку
|
||||
@@ -108,9 +108,9 @@ DDS»** — счётчики строк по двум нашим сущност
|
||||
В колонке `check_date` стоит `today()` из кода витрины, так что у тебя там будет сегодняшняя
|
||||
дата — не пугайся, если она не совпадёт с примером.
|
||||
|
||||
`orphan_events = 0` — ни одной сироты. Каждое из 50 событий нашло свой клик в `dds.click`. На
|
||||
чистом демо-срезе так и должно быть: данные аккуратные, ничего не потерялось. В секции 4 мы
|
||||
сироту устроим сами — и эта строка оживёт.
|
||||
`orphan_events = 0` — ни одной сироты. Каждое событие нашло свой клик в `dds.click`. На
|
||||
чистой стартовой истории так и должно быть: данные аккуратные, ничего не потерялось. В секции
|
||||
4 мы сироту устроим сами — и эта строка оживёт.
|
||||
|
||||
Проверь нолик сам, не верь на слово. Открой SQL-консоль `http://localhost:9123/play`
|
||||
(пользователь `default`, пароль `123456`) и посчитай сирот напрямую:
|
||||
@@ -158,9 +158,9 @@ SELECT click_id FROM ods.geo_by_click ...
|
||||
строим карточки: так не потеряется клик, который есть, например, в `geo`, но почему-то не доехал
|
||||
в `device`.
|
||||
|
||||
> На нашем срезе `device` и `geo` содержат один и тот же набор из 26 кликов, так что универсум
|
||||
> тоже 26. Но код написан так, чтобы пережить случай, когда наборы **разойдутся**, — и это
|
||||
> правильно: в проде они расходятся постоянно.
|
||||
> На чистой стартовой истории `device` и `geo` должны содержать один и тот же набор кликов,
|
||||
> так что универсум совпадает с обоими источниками. Но код написан так, чтобы пережить случай,
|
||||
> когда наборы **разойдутся**, — и это правильно: в проде они расходятся постоянно.
|
||||
|
||||
### `argMax`: одна строка на клик, самая свежая
|
||||
|
||||
@@ -221,7 +221,7 @@ LEFT JOIN ( ...снапшот geo... ) AS g ON g.click_id = c.click_id
|
||||
|
||||
### Сироты: событие без клика
|
||||
|
||||
Мы собрали `dds.click` (26 карточек кликов) и `dds.event` (50 карточек событий). Внутри каждого
|
||||
Мы собрали `dds.click` (карточки кликов) и `dds.event` (карточки событий). Внутри каждого
|
||||
события лежит `click_id` — ссылка на клик. И вот тут возникает вопрос целостности из секции 1:
|
||||
**а на каждую ли ссылку есть карточка клика?**
|
||||
|
||||
@@ -237,16 +237,16 @@ WHERE click_id IS NOT NULL
|
||||
Заметь разницу с предыдущим пунктом. Пустое гео — это когда у **клика** не подтянулся свой
|
||||
контекст (внутренний пропуск в карточке, но сам клик есть). А сирота — это когда у **события**
|
||||
нет вообще никакого клика (порвана связь между сущностями). Это разные дырки: первую видно по
|
||||
пустым полям внутри карточки, вторую — отдельным счётчиком. На чистом срезе сирот ноль — сейчас
|
||||
мы это изменим.
|
||||
пустым полям внутри карточки, вторую — отдельным счётчиком. На чистой стартовой истории сирот
|
||||
ноль — сейчас мы это изменим.
|
||||
|
||||
---
|
||||
|
||||
## 4. Управляемая правка: заведём сироту
|
||||
|
||||
Сирота на чистом срезе не появится сама — данные слишком аккуратные. Поэтому **создадим её
|
||||
руками**: добавим в `dds.event` одно событие, которое ссылается на клик, которого в `dds.click`
|
||||
нет. И посмотрим, как оживёт счётчик сирот и как себя поведёт `LEFT JOIN`.
|
||||
Сирота на чистой стартовой истории не появится сама — данные слишком аккуратные. Поэтому
|
||||
**создадим её руками**: добавим в `dds.event` одно событие, которое ссылается на клик, которого
|
||||
в `dds.click` нет. И посмотрим, как оживёт счётчик сирот и как себя поведёт `LEFT JOIN`.
|
||||
|
||||
Открой SQL-консоль `http://localhost:9123/play` и вставь придуманное событие:
|
||||
|
||||
@@ -309,7 +309,7 @@ make transform
|
||||
```
|
||||
|
||||
После этого `orphan_events` снова `0`, придуманное событие исчезло. А если стенд совсем «поплыл» —
|
||||
полный сброс: `make clean && make up && make ddl && LIMIT=50 make data && make transform`.
|
||||
полный чистый прогон: `make generated-history-analytics && make up`.
|
||||
|
||||
---
|
||||
|
||||
@@ -317,9 +317,9 @@ make transform
|
||||
|
||||
| Действие | Где смотреть | Что ожидать |
|
||||
|----------|--------------|-------------|
|
||||
| `make transform` (базовый прогон) | блок «Статистика DDS» | `dds.click` = 26, `dds.event` = 50 |
|
||||
| базовый прогон | блок «Статистика DDS» | `dds.click` и `dds.event` не пустые |
|
||||
| `make transform` (базовый прогон) | блок «Сводка по качеству», строка `orphan_events` | `0` |
|
||||
| почему `click` = 26, а `event` = 50 | запрос `count()` по `dds.click` и `dds.event` | 50 событий ссылаются на 26 кликов — норма |
|
||||
| почему `click` меньше `event` | запрос `count()` по `dds.click` и `dds.event` | много событий ссылаются на меньшее число кликов — норма |
|
||||
| правка из секции 4 (вставили сироту) | запрос `count()` сирот в play-консоли | `0 → 1` |
|
||||
| та же сирота через `dm.v_events_enriched` | `SELECT device_type, geo_country ...` | поля клика пустые (`NULL`) — это `LEFT JOIN` |
|
||||
|
||||
|
||||
Reference in New Issue
Block a user