docs(specs): спека «Боевой реализм v2» принята владельцем (#17)

- Зачем:
  - зафиксировать вердикт владельца по трём помеченным отступлениям и
    закрыть приёмку спеки.
- Что:
  - статус Draft -> Accepted; подтверждены порядок страховочных срезов
    и VisitID как эталон самопроверки.
  - Sign взят как колонка формата без механики (всегда 1, 47-я колонка
    события); честность — комментарий в DDL, абзац в документе о
    реализме, лекционный крючок про CollapsingMergeTree.
  - в опорные точки добавлена рамка лабы сессий: сначала собрать самому,
    потом рассказ про VisitID в хитах с октября 2025.
- Проверка:
  - вычитка разделов 1.1, 1.2, 10, 12 и шапки статуса.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-30 15:24:50 +03:00
co-authored by Claude Fable 5
parent 9d3a8053d3
commit 05e4cdea4d
+23 -11
View File
@@ -1,6 +1,8 @@
# Боевой реализм стенда (v2): широкое событие, заказы, кластер, анонимность
Статус: Draft — на приёмку владельцу.
Статус: Accepted (2026-07-30). Три помеченных отступления подтверждены
владельцем на приёмке: порядок страховочных срезов (раздел 9), `Sign` как
колонка без механики (раздел 1.1), `VisitID` как эталон самопроверки (1.2).
Дата: 2026-07-30. Тикет: #17 (сборка карты #10).
Источники: резолюции #18 (модель данных), #15 (`purchase` и заказы),
#14 (кластер), #13 (цена кластера), #16 (анонимность и склейка); исследование
@@ -64,10 +66,13 @@ JSON-поле `ecommerce`. Сессий в потоке нет — их мент
тоже не берём, но это наш выбор, а не запрет источника: исследование
запрещало только выдавать это поле за формат Яндекса, оставить разрешало.
Разбор строки user agent — не урок этого стенда.
- **Механику версий записи (`Sign`/`HitVersion`) не берём сейчас**: по #15 это
кандидат на потом, и её дом — клиентская сторона (поток визитов Метрики
Про). Это помеченное отступление от рекомендации исследования («`Sign` на
хитах полезен»): колонка без механики — мёртвый вес.
- **`Sign` берём как колонку формата, без механики** (решение владельца на
приёмке спеки): генератор всегда пишет `Sign = 1`, исправлений записей не
шлёт — движки и запросы не меняются. Сама механика версий
(CollapsingMergeTree, пара `HitVersion`) — кандидат на потом, по #15.
Честность: комментарий в DDL и абзац в документе о реализме («в бою здесь
бывают −1/+1, считают через `sum(Sign)`»); в лекции — крючок про
CollapsingMergeTree (частый вопрос на собеседованиях).
- **Не берём** `ClientEventTime` (в выгрузке Метрики нет клиентской метки;
расхождение часов — тема тумана «грязь»), `Params` (второй сырой JSON не
нужен: этот навык уже несут заказы), `LastSearchEngineRoot`, `IsPageView`,
@@ -78,7 +83,7 @@ JSON-поле `ecommerce`. Сессий в потоке нет — их мент
- **Наша честная добавка** — `EventType`: у Метрики такого поля нет
(там `isPageView` + `productEventType`), стенду таксономия нужна явно.
### 1.2 Состав полей (46 колонок)
### 1.2 Состав полей (47 колонок)
Идентификаторы и время:
@@ -92,6 +97,7 @@ JSON-поле `ecommerce`. Сессий в потоке нет — их мент
| `UTCEventTime` | DateTime | единственная метка времени, как у Метрики |
| `ClientTimeZone` | Int16 | смещение пояса клиента в минутах |
| `EventType` | LowCardinality(String) | `pageview` / `add_to_cart` / `purchase` |
| `Sign` | Int8 | всегда 1: колонка формата, механика исправлений не реализована (см. 1.1) |
Правила резки визитов в генераторе документируются и совпадают с лабной
логикой (30-минутный таймаут).
@@ -155,7 +161,7 @@ Ecommerce (заполнены только у торговых событий):
### 1.4 Схема как контракт
Одно машинное описание схемы события (python-модуль или YAML) — источник
истины: из него выводятся DDL и валидация генератора, а не наоборот. 46
истины: из него выводятся DDL и валидация генератора, а не наоборот. 47
колонок повторяются примерно в семи местах (генератор, DDL, SELECT матвью,
трансформации, витрины, манифест, доки) — без контракта они расходятся
молча. Заодно это учебный артефакт: менти видит на живом примере, что такое
@@ -295,7 +301,7 @@ CSV в репозитории (`data/catalog/products.csv`: `sku`, `name`, `cate
прогонами, куда легли данные — нет».
- **Приём строгий**: `input_format_skip_unknown_fields = 0`, обязательные
поля — без значений по умолчанию. Контракт присутствия: генератор выдаёт
**все 46 полей в каждом событии**; «пусто» — пустой массив, пустая строка
**все 47 полей в каждом событии**; «пусто» — пустой массив, пустая строка
или 0, а не отсутствие ключа в JSON. Так строгий приём уживается с
полями, пустыми по смыслу (ecommerce у `pageview`, UTM у прямого захода). Несовпадение имени поля — громкая
ошибка в `*_errors`, а не молчаливые нули: имена CamelCase регистрозависимы,
@@ -501,8 +507,8 @@ smoke-проверки, а не «дашборд зелёный». Это мин
- Реплики (2×2), HAProxy, репликационная эксплуатация — в лекцию, не в стенд.
- Полный словарь торговых событий Метрики (detail, remove, impressions),
пять уровней категорий, блоки `purchasedProduct*`/`impressions*`.
- `Sign`/CollapsingMergeTree — кандидат на потом (дом — поток визитов
Метрики Про).
- Механика `Sign`/CollapsingMergeTree — кандидат на потом (дом — поток
визитов Метрики Про); сама колонка `Sign` уже в схеме, статикой (см. 1.1).
- Событийный лог заказов, CDC/Debezium, HTTP-сервис заказов, шапка+строки,
отдельный поток возвратов — отклонены в #15/#18.
- Файловые дропы как источник — зона следующего стенда (Lakehouse), у нас
@@ -547,7 +553,8 @@ smoke-проверки, а не «дашборд зелёный». Это мин
Logs API — это скачанный TSV»); trade-off «инкремент экономнее, слепок
надёжнее» + сноска про compacted topic; identity stitching перестаёт быть
чистой теорией; «прямое чтение прод-базы — анти-приём, в бою — реплика или
выгрузка»; полный словарь торговых событий — теория.
выгрузка»; колонка `Sign` без механики — честное ограничение стенда
(в бою 1/+1 и `sum(Sign)`); полный словарь торговых событий — теория.
В v1 при заморозке — указатель на v2 в README (форму решить при рождении
v2, этап 0).
@@ -564,6 +571,11 @@ v2, этап 0).
- сцена «разные consumer groups → дубли»;
- словарь регионов из CSV той же машинерией, что каталог товаров (оживляет
`RegionCityID`);
- лаба сессий: менти сначала собирает сессии сам, и только после — рассказ,
что с октября 2025 Метрика отдаёт `VisitID` прямо в хитах; частично
синтетическая постановка — осознанный приём;
- лекция «`Sign` и CollapsingMergeTree»: почему на стенде `sum(Sign)` =
`count()`, а в бою — нет; частый вопрос на собеседованиях;
- лекция про идентичность «как в бою»: `setUserID` и first-party id,
детерминированная против вероятностной склейки, identity graph,
кросс-девайс, CDP — с рамкой «мы склеили через транзакции, потому что трекер