docs(ingestion): упрощены объяснения источников и приема данных
Зачем: помочь начинающему инженеру проследить событие и версию заказа от генератора до хранилища, включая ошибки и повторные поступления. Что: обновлены вводная генератора, справочники приема и модели заказа, разделы хранилища и комментарии SQL и дага. Сохранены технические условия; исполняемый код, схемы и поведение стенда не изменены. Проверка: make lint, git diff --check, 142 локальные ссылки и справка CLI. SQL без комментариев и Python AST без строк документации совпали с исходными. Генерация данных и сброс состояния не выполнялись. Refs #250
This commit is contained in:
1 parent
83434102d5
commit
75f28fe99f
13 files changed
+663
-974
No files matched your search
+78
-81
@@ -1,98 +1,95 @@
|
||||
# Генератор кликстрима
|
||||
# Генератор событий и заказов
|
||||
|
||||
Клиентская сторона стенда: отсюда берётся поток событий — широкое событие по
|
||||
образцу облачной выгрузки Яндекс Метрики. Устройство и принятые решения —
|
||||
спека [«Генератор (этап 2)»](../docs/specs/2026-08-01-generator.md).
|
||||
Генератор создает данные двух источников интернет-магазина. Счетчик сайта
|
||||
отправляет в топик Kafka `hits` события: просмотр страницы, добавление товара
|
||||
в корзину и покупку. Серверная часть магазина выгружает в `orders` дневной
|
||||
слепок — состояние заказов, оформленных за последние семь модельных дней.
|
||||
Оба источника работают; хранилище принимает события непрерывно, заказы —
|
||||
по команде Airflow.
|
||||
|
||||
События уже есть и уже уезжают: день-функция отдаёт по паре (зерно, D)
|
||||
упорядоченный поток трёх видов — просмотр страницы, корзина, покупка, —
|
||||
канонический сериализатор превращает его в JSON, а проигрыватель гонит в файл
|
||||
или в Kafka. Клиентская сторона на этом целая. Второй источник строится:
|
||||
день отдаёт и заказы бэкенда — проекцию своих покупок с деньгами магазина, —
|
||||
а судьба заказа и отправка слепка идут следующими тикетами.
|
||||
Событие описывает отдельное действие посетителя. Заказ после оформления
|
||||
может измениться, например перейти в статус `paid` или `cancelled`.
|
||||
Поэтому один `order_id` встречается в нескольких слепках. Часть заказов
|
||||
впервые появляется с задержкой в один или два дня; конец семидневного окна
|
||||
от этого не сдвигается. Правила изменений и задержек — в
|
||||
[описании судьбы заказа](../docs/architecture/orders/fate.md).
|
||||
|
||||
Слепок снимается на конец модельного дня и отправляется на следующий день.
|
||||
Команда `snapshot --day 1` отправляет слепок дня 0, а `snapshot --day 0`
|
||||
не отправляет ничего: до начала модели заказов нет. События и слепки
|
||||
отправляются отдельными запусками генератора.
|
||||
|
||||
## Как это работает
|
||||
|
||||
Корневое зерно ветвится на подпотоки, из них складываются когорты, аудитория
|
||||
дня, визиты, строки событий и заказы. План поднимает состав мира по требованию,
|
||||
а день-функция проживает один день без накопленного состояния. Код этой
|
||||
цепочки, ее схема и запуск D0 собраны в
|
||||
[путеводителе](../docs/architecture/generator/README.md).
|
||||
Зерно генерации задает случайные выборы: какие посетители придут,
|
||||
какие страницы откроют и что купят. При неизменных коде, каталоге
|
||||
и настройках одинаковые зерно и номер дня дают одинаковые данные.
|
||||
День можно рассчитать без генерации всех предыдущих дней.
|
||||
|
||||
Сначала рассчитываются посещения и покупки. Из покупки получаются событие
|
||||
`purchase` со стороны сайта и заказ со стороны магазина. Номер связывает
|
||||
их: `purchaseID` в событии соответствует `order_id` заказа. Суммы могут
|
||||
различаться: магазин учитывает скидку, доставку и удаленные позиции.
|
||||
Деньги генератор считает целыми копейками; в JSON заказа это строки
|
||||
в рублях с двумя знаками после точки, например `"1289.99"`.
|
||||
|
||||
Готовые записи превращаются в JSON и отправляются в файл или Kafka:
|
||||
одна запись занимает одну строку файла или одно сообщение. Событие содержит
|
||||
47 полей по [описанию выгрузки](../docs/formats/clickstream-event.md),
|
||||
запись заказа — поля [формата слепка](../docs/architecture/orders/snapshot.md#запись-на-проводе).
|
||||
Подробное устройство генератора разобрано в
|
||||
[техническом справочнике](../docs/architecture/generator/README.md).
|
||||
|
||||
## Что где лежит
|
||||
|
||||
- `src/clickstream_generator/world.py` — конфигурация мира: все его числа
|
||||
одним местом. Правка любого — смена мира; крутить их и предлагается.
|
||||
- `src/clickstream_generator/seeds.py` — иерархия зёрен: кто из какого
|
||||
подпотока берёт случайность. На ней держится весь детерминизм.
|
||||
- `src/clickstream_generator/plan.py` — план состава: кто есть в мире в
|
||||
день D. Когорты, приток, двухкуковые пары, паспорта кук, человек за кукой
|
||||
и счётчики — до генерации событий.
|
||||
- `src/clickstream_generator/weights.py` — выбор по целым весам: один приём
|
||||
на весь генератор, чтобы дисциплина целочисленной случайности не жила
|
||||
копиями.
|
||||
- `src/clickstream_generator/reference.py` — справочники: устройства,
|
||||
города, источники трафика, карта сайта. Таблицы-литералы: доля живёт в
|
||||
строке, которой принадлежит.
|
||||
- `src/clickstream_generator/catalog.py` — каталог товаров из
|
||||
`data/catalog/products.csv`, общего у генератора и словаря ClickHouse.
|
||||
- `src/clickstream_generator/day.py` — день-функция: визиты, страницы,
|
||||
атрибуция, устройство и гео. Там же правила резки визитов и шов, на
|
||||
который садятся торговые события; прожитый день отдаёт обе половины —
|
||||
поток событий и заказы.
|
||||
- `src/clickstream_generator/commerce.py` — торговые события: что легло в
|
||||
корзину, что из этого куплено, деньги, номера заказов и сырой `ecommerce`.
|
||||
Своя случайность, поэтому правка торговли трафик не двигает.
|
||||
- `src/clickstream_generator/orders.py` — заказы бэкенда: вторая проекция
|
||||
покупки. Личность покупателя и деньги магазина — скидка по промокоду,
|
||||
доставка, итог; своя случайность, ветвящаяся по дню рождения заказа.
|
||||
- `src/clickstream_generator/ids.py` — номера событий: неповторяющиеся и
|
||||
ниже 2^53. Обещание одно на обе половины дня, поэтому и живёт отдельно.
|
||||
- `src/clickstream_generator/serialize.py` — канонический сериализатор:
|
||||
единственное место, где событие целиком превращается в JSON. Порядок ключей,
|
||||
все 47 колонок всегда и форма на проводе — ISO-8601.
|
||||
- `src/clickstream_generator/sinks.py` — приёмники: файл (одно событие — одна
|
||||
строка) и Kafka (одно событие — одно сообщение). Про содержимое они не
|
||||
знают; там же довод, почему у сообщения нет ключа.
|
||||
- `src/clickstream_generator/player.py` — проигрыватель: гонит дни в приёмник
|
||||
пачкой или с темпом живого дня. Состояния не хранит.
|
||||
- `src/clickstream_generator/cli.py` — интерфейс запуска: параметры
|
||||
аргументами или окружением, логи в стандартный вывод, итог кодом возврата.
|
||||
- `src/clickstream_generator/schema.py` — контракт схемы: чистые данные о
|
||||
колонках выгрузки. Собственность генератора; из него выводятся сам
|
||||
генератор, его валидация и описание выгрузки в доках.
|
||||
- `src/clickstream_generator/schema_doc.py` — сборка «описания выгрузки»
|
||||
([`docs/formats/clickstream-event.md`](../docs/formats/clickstream-event.md))
|
||||
из контракта. Документ руками не правят — пересобирают.
|
||||
- `src/clickstream_generator/inventory.py` — сборка паспорта мира
|
||||
([`data/world-inventory.json`](../data/world-inventory.json)): шапка паспорта
|
||||
и хеши восьми дней, которыми наполняется стенд, плюс счётчики классов
|
||||
расхождений по заказам для дней с закрытым окном. Руками не правят —
|
||||
пересобирают целью `make inventory`.
|
||||
- `tests/` — инварианты контракта, свежесть описания и обещания мира:
|
||||
чистота от зерна, приток, гарантия двухкуковых пар, форма суточной волны
|
||||
и сборка визитов по задокументированным правилам. Там же побайтовое
|
||||
обещание, доведённое до диска: два прогона дня в файл дают тот же файл, а
|
||||
строк в нём ровно столько, сколько событий.
|
||||
Все перечисленные модули находятся в `src/clickstream_generator/`.
|
||||
|
||||
| Модули | Назначение |
|
||||
|---|---|
|
||||
| `world.py`, `seeds.py`, `plan.py` | Настройки мира, случайные выборы и план посещений |
|
||||
| `reference.py`, `catalog.py` | Устройства, города, источники трафика и каталог товаров |
|
||||
| `day.py`, `commerce.py` | Визиты, события сайта и покупки |
|
||||
| `orders.py` | Состояние заказов на дату слепка, изменения и задержки |
|
||||
| `serialize.py`, `player.py`, `sinks.py` | Запись JSON и отправка в файл или Kafka |
|
||||
| `cli.py` | Команды и параметры запуска |
|
||||
| `schema.py`, `schema_doc.py` | Описания колонок события и сборка документа выгрузки |
|
||||
| `inventory.py` | Сборка паспорта мира с контрольными суммами и счетчиками |
|
||||
|
||||
Тесты в `tests/` проверяют формат, правила модели и повторяемость данных.
|
||||
Описание выгрузки и [паспорт мира](../data/world-inventory.json) собираются
|
||||
из кода; вручную их не редактируют.
|
||||
|
||||
## Команды
|
||||
|
||||
Проиграть день — раздел [«Как запустить
|
||||
генератор»](../docs/architecture/world.md#как-запустить-генератор)
|
||||
справочника мира и `--help`.
|
||||
Для запуска из каталога `generator/` используйте
|
||||
`uv run python -m clickstream_generator <команда>`. Справку можно открыть
|
||||
через `--help`, в том числе после имени команды.
|
||||
|
||||
Из этого каталога — цели генератора живут здесь, а не в корне:
|
||||
| Команда | Что отправляет | Особые параметры |
|
||||
|---|---|---|
|
||||
| `batch` | События дня без пауз | `--limit N` ограничивает число событий за весь запуск |
|
||||
| `live` | События постепенно, с ускорением | `--speed X`, по умолчанию 60: сутки занимают около 24 минут |
|
||||
| `snapshot` | Слепок заказов предыдущего дня | `--day D` задает день отправки |
|
||||
|
||||
Для всех команд нужны номер дня `--day` и приемник: `--file` либо
|
||||
`--brokers` вместе с `--topic`. Общие параметры `--seed` и `--days`
|
||||
задают зерно и число последовательных дней. Параметры можно передать
|
||||
и через окружение; имена и примеры собраны в
|
||||
[справочнике запуска](../docs/architecture/world.md#как-запустить-генератор).
|
||||
Прямой запуск генератора не продвигает сохраненную позицию времени стенда —
|
||||
ей управляют [даги модельного времени](../docs/handbook/airflow-dags.md#даги-мира).
|
||||
|
||||
Из каталога `generator/` доступны:
|
||||
|
||||
- `make test` — тесты генератора;
|
||||
- `make lint` — ruff: проверка и формат;
|
||||
- `make typecheck` — ty: проверка типов;
|
||||
- `make demo` — пройти стадии канонического D0 на одном экране;
|
||||
- `make lint` — проверка кода и форматирования через ruff;
|
||||
- `make typecheck` — проверка типов через ty;
|
||||
- `make demo` — показать стадии расчета первого дня, D0;
|
||||
- `make docs` — пересобрать описание выгрузки;
|
||||
- `make inventory` — пересобрать паспорт мира.
|
||||
|
||||
В корне репозитория `make lint` тоже есть, но он про код стенда — даги и
|
||||
Superset. Одноимённые цели за разными дверями охватывают разное:
|
||||
[карта проверок](../docs/architecture/testing.md).
|
||||
|
||||
Python и зависимости — через `uv`, версии закреплены в `uv.lock`: на этом
|
||||
держится обещание побайтовой воспроизводимости (спека, раздел 2).
|
||||
В корне `make lint` проверяет даги, код Superset и сборку карты архитектуры.
|
||||
Корневые `make generate-batch` и `make generate-live` запускают события
|
||||
через контейнер генератора. Состав проверок — в
|
||||
[карте проверок](../docs/architecture/testing.md).
|
||||
Python и зависимости запускаются через `uv`; версии закреплены в `uv.lock`.
|
||||
Reference in new issue
Block a user