День-функция: трафик, сессии, pageview #39
Notifications
Due Date
No due date set.
Blocks
Depends on
#40 Торговые события
ddmitry/clickstream-data-platform
#38 План состава мира: зерно, приток, конфигурация
ddmitry/clickstream-data-platform
Reference: ddmitry/clickstream-data-platform#39
Reference in New Issue
Block a user
Part of #4.
Цель
День-функция, часть первая: (зерно, D) → упорядоченный поток pageview.
Суточные волны, сессии, поля страницы, атрибуции, браузера, устройства и
гео — трафиковая половина широкого события.
Что войдёт
среднего), различие будней и выходных.
VisitIDот генератора, резка по границемодельных суток; правила документируются — лаба сессий сверяет себя
по ним.
браузер/ОС/устройство/экран, IP и регионы. Справочники — таблицы-литералы
или посточный фейкер, но только для справочников, не в горячем цикле;
выбор библиотеки — здесь (спека генератора, раздел 6).
сессий; целочисленная случайность.
Критерии приёмки
пустая строка или 0, а не отсутствие ключа. Торговые поля пустые
по смыслу — их заполнит следующий тикет.
события; день N+1 не меняет события дней 1…N (тесты).
отличаются от выходных (тест на форму, без хрупких порогов).
здесь проверяется порядок величины: итоговое число сложится после
торговых событий (#40) и ляжет в манифест.
WatchID,VisitID,ClientID) — UInt64 ниже 2^53.Границы
add_to_cart,purchase) — следующий тикет.Сначала прочитать
Проверка
make testРешения при исполнении (2026-08-02)
Приняты владельцем до реализации; в спеку генератора (раздел 9) уезжают тем же PR.
1. Каталог товаров переезжает сюда из #40. У карточки товара есть
Title— это имятовара, а имена живут в каталоге: карточка без каталога невозможна. Без карточек же #39
сделал бы примерно половину трафика (критерий «итоговое число сложится после торговых
событий» предполагает добавку, а не удвоение), а подпоток
TRAFFICотдал бы содержаниетрафика торговому подпотоку. Файл
data/catalog/products.csv(sku, name, category, brand,price) заводится здесь; #40 получает готовый — его пункт и критерий про каталог снимаются.
2. Ассортимент — непродовольственная розница: товары для дома, текстиль, посуда, мелкая
бытовая техника, детское, одежда и обувь; ~150–200 sku. Довод не вкусовой: числа мира уже
приняты под этот профиль — «цикл повторной покупки магазина — месяцы», 75% одноразовых кук,
3–4 возврата, конверсия ~2% на сессию. Продуктовая сеть требовала бы других чисел (возврат
раз в неделю, корзина в двадцать позиций), то есть переоткрытия #38.
3. География — один регион присутствия: город-миллионник, города своего региона и тонкий
хвост остальной страны, а не «топ городов России». Рабочий выбор — Поволжье, центр Самара
(пояс UTC+4).
4. Модельные сутки считаются в часовом поясе счётчика, как в выгрузке Метрики:
EventDate— дата в поясе счётчика,UTCEventTime— абсолютная метка. Тогда шов сутоквсегда приходится на местную полночь и не режет утренние сессии, а регион мира выбирается
свободно. Суточная волна задаётся в местном времени посетителя: у большинства это пояс сети,
у хвоста свой — пик слегка размазывается, как в жизни. Следствие для соседних этапов:
toDate(UTCEventTime) ≠ EventDate. Это записывается в описание выгрузки и комментарием вDDL, иначе сторона хранилища выведет дату сама и разойдётся на несколько часов данных.
5. Паспорт куки — постоянная часть мира, а не поведение дня. Браузер, ОС, устройство,
экран и город у куки одни и те же во всех её визитах: кука — это браузер на устройстве.
Держит их план состава: когорта бросает куке паспорт (номер профиля устройства и номер
города), день-функция разворачивает его в поля события. Довод против вывода паспорта из
ClientIDарифметикой: про двухкуковые пары знает только план, а два города у одногочеловека — ложь в данных; пара получает один город и разные устройства («телефон и
ноутбук», мастер-спека, раздел 5). Плата:
CohortиDayAudienceиз #38 прирастаютмассивами. Броски приписываются последними, поэтому измеренные числа канонического мира не
двигаются — проверяется прогоном счётчиков до и после, а не на глазок. Ленивая форма плана
не страдает: паспорт — константа куки, истории не требует.
6. Справочники — таблицы-литералы рядом с конфигурацией мира. Faker и mimesis не
подключаются: нам нужны веса долей и связки (город → id региона → часовой пояс; телефон →
Safari → iOS → размер экрана), а их фейкер не даёт — таблицу мы напишем всё равно. Новая
зависимость при этом молча меняла бы мир при обновлении словарей.
7. Гео-id — настоящие идентификаторы Яндекса, если подтвердятся первоисточником
(проверка идёт отдельно); иначе свои с прямым комментарием «id наши, не яндексовы».
Выдуманные по памяти числа не годятся ни в каком случае.
8. IP-адреса — нероутируемые диапазоны: документационные (RFC 5737) и benchmark
(198.18/15) по ломтю на город, мобильным — CGNAT 100.64/10, как у настоящих операторов.
Правдоподобные публичные адреса принадлежат живым организациям, и в учебных данных им не
место; условность объясняется комментарием.
9. Словарь: «визит» остаётся за сессией и полем
VisitID. В плане состава одноимённоепонятие переименовывается в «день активности» — правка докстрингов
plan.py, имён двухполей
Cohortи статьи CONTEXT.md тем же PR.Хвост для #42: паспорт мира в манифесте (зерно и версия генератора) файл каталога не
накрывает — правка цены в CSV меняет мир молча. Манифесту нужен хеш каталога.
Сделано и проверено (2026-08-02)
Реализация закрыта, ветка
feat/39-day-function, коммит в ней.Проверка:
make lint,make typecheck,make test— 353 passed (было 297). Счётчики плана не сдвинулись: приток 3827,64/день, дневная аудитория 6235–7124, 68 119 посетителей за 14 дней, 170 двухкуковых пар. День 0 — 45 810 событий за 0,6 с, снимок 14 дней — 5,9 с.Ревью: две слепые линии — по коду и по задаче с вкусом. Десять находок (1 MAJOR, 8 MINOR, 1 NIT), все закрыты и перепроверены авторами находок. MAJOR: сторож детерминизма не замечал ни порядка колонок, ни швов
page/productдля #40 — теперь замечает, проверено мутацией. Ещё одна находка того же класса: тест правил нарезки доказывал только склейку визитов, но не разрез, — теперь доказывает обе стороны.Решения владельца при приёмке:
day.py(около 24 визитов в день из ~9,5 тыс.);ParsedParamsKey1остаётся пустым; наполнение вариантом A/B-теста вынесено в тикет #47;Каталог
data/catalog/products.csvзаведён здесь (решение 1): 180 позиций, шесть категорий. Из #40 пункт про каталог снят.