From 5a3e3f92d96000957b14135feade0e7b9b851427 Mon Sep 17 00:00:00 2001 From: Dmitry Dementev Date: Sun, 13 Sep 2026 13:59:37 -0400 Subject: [PATCH] =?UTF-8?q?docs(context):=20=D1=83=D0=BF=D1=80=D0=BE=D1=89?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=81=D0=BB=D0=BE=D0=B2=D0=B0=D1=80=D1=8C=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=BD=D1=8F=D1=82=D0=B8=D0=B9=20=D0=B4=D0=BB=D1=8F?= =?UTF-8?q?=20=D0=BD=D0=B0=D1=87=D0=B8=D0=BD=D0=B0=D1=8E=D1=89=D0=B5=D0=B3?= =?UTF-8?q?=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Зачем: понимать термины стенда без изучения устройства генератора. Что: понятия сгруппированы по учебным темам, определения упрощены; уточнены идентификаторы, модельное время и повторный запуск стенда. Проверка: сохранены все 43 прежних термина; проверены 11 входящих и 6 внутренних ссылок, якорь «Язык» и git diff --check. --- CONTEXT.md | 448 +++++++++++++++++++++++++++++------------------------ 1 file changed, 247 insertions(+), 201 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 106c192..fd3b8ff 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -1,229 +1,275 @@ # Кликстрим-платформа (стенд v2) -Словарь понятий проекта: одни и те же слова для одних и тех же вещей — -у владельца, кода, документов и агентов. Только язык, никаких решений. +Словарь понятий, которые встречаются в учебных заданиях, документации и коде +стенда. ## Язык -**Хранилище**: -Аналитическая база стенда — кластер ClickHouse со слоями STG/ODS/DDS/DM. -Принимающая сторона границы «трекер | хранилище»: нормализует имена и стили -источников, строится по их документации. -_Избегать_: склад, склад данных +- [Люди и события](#люди-и-события) +- [Хранилище и форматы данных](#хранилище-и-форматы-данных) +- [Заказы](#заказы) +- [Модельное время и запуск стенда](#модельное-время-и-запуск-стенда) +- [Устройство генератора](#устройство-генератора) — для чтения его кода + и технических справочников. -**Сырые данные**: -Слой STG хранилища и его содержимое: сообщение топика, положенное строкой, -как пришло, без разбора. В прозе — сырые данные и сырой слой; про -конкретную таблицу или базу — STG. -_Избегать_: сырьё - -**Состав мира**: -Постоянная часть мира генератора — популяция посетителей, их привычки, -двухкуковые пары. По дням его выдаёт план состава. -_Избегать_: состояние мира - -**План состава**: -Способ спросить состав мира: функция зерна, выдающая его по дням — -когорту новых кук, их возвраты, назначенные заказы двухкуковых пар. - -**Подпоток**: -Ветвь дерева случайности генератора: своё зерно у состава мира, у каждого -дня и у каждого компонента дня. Подпоток задан позицией в дереве, а не -порядком вычислений. - -**Конфигурация мира**: -Модуль чистых данных со всеми числами мира: приток, профили возвратов, доли -покупателей, D0. Правка модуля — смена мира. Модуль-близнец контракта схемы: -там колонки, здесь числа. - -**Когорта дня**: -Люди, впервые пришедшие в мир в один день, со всеми их куками и днями -активности. Единица плана состава: когорта — функция зерна и номера дня. +### Люди и события **Человек**: -Устойчивая личность в составе мира, которой принадлежат одна или две куки. -Не поле источника: кликстрим видит куку, а заказ — пользователя магазина. +Участник модели магазина, который может заходить с одного или двух +устройств. Генератор знает, какие идентификаторы посетителя ему принадлежат, +но в событиях сайта этой связи нет. **Посетитель**: -Кука, наблюдаемая в кликстриме и обозначенная `ClientID`; единица -`uniq(ClientID)`. Один человек может быть представлен несколькими посетителями. -_Избегать_: «посетитель» про человека после склейки +Браузер на устройстве, который счетчик сайта узнает по куке — сохраненному +в браузере идентификатору. В кликстриме он обозначается `ClientID`: +`uniq(ClientID)` считает посетителей, и у одного человека их может быть несколько. +_Избегать_: «посетитель» про человека после объединения его идентификаторов. **Пользователь магазина**: -Представление человека со стороны заказов; в записи заказа обозначается -`user_id`. Через заказы связывает разные куки человека, в кликстрим напрямую -не попадает. - -**Приток**: -Появление новых кук на всём протяжении оси модельного времени; единица — -кука (`ClientID`). Из-за притока накопленная аудитория растёт с -горизонтом и не совпадает с дневной. - -**Хвост возвратов**: -Окно активности, отсчитанное от первого дня человека и общее на обе -его куки; дольше окна кука не возвращается. - -**Предыстория**: -Когорты плана с первым днём активности до D0; событий не порождают. - -**День активности**: -День, в который кука хоть раз появилась в мире: день её рождения или день -возврата. Единица плана состава: за день он у куки один, а визитов внутри -него бывает несколько. -_Избегать_: визит (визит — про сессию) +Человек, известный серверной части магазина под идентификатором `user_id`. +Он указан в заказах и позволяет связать разные `ClientID` одного человека; +в кликстриме поля `user_id` нет. Например, покупки с телефона и ноутбука +могут иметь разные `ClientID`, но один `user_id` в заказах. **Визит**: -Подряд идущие события одной куки без пауз длиннее 30 минут, не пересекающие -границу суток. Поле `VisitID` — эталон для лабы сессий. «Сессия» — то же -понятие словами аналитики: в данных и в коде оно зовётся визитом, но имена -вроде «лаба сессий» и «сборка сессий» остаются. +Последовательность событий одного посетителя без пауз длиннее 30 минут +и без перехода через полночь по часовому поясу счетчика. Сессия означает +то же самое; поле `VisitID` служит эталоном в задании по нарезке сессий. + +**День активности**: +День, в который посетитель появился на сайте хотя бы раз: впервые или +вернувшись. За один день активности у него может быть несколько визитов. +_Избегать_: визит в значении целого дня активности. **Паспорт куки**: -Устройство и город, приписанные куке на всю жизнь: кука — это браузер на -устройстве. Держит их план состава, разворачивает в поля события -день-функция; у двухкуковой пары город один на две куки, устройства разные. - -**Покупатель**: -Человек, которого план состава пометил склонным покупать. Метка значима: -помеченный доходит до заказа заметно чаще прочих, но и непомеченный иногда -покупает. Из покупателей отбираются двухкуковые пары. -_Избегать_: «покупатель» про того, кто купил в конкретный день — это визит -с заказом. - -**День-функция**: -Функция (зерно, номер дня), выдающая упорядоченный поток событий этих -модельных суток. Состояния между днями нет: день D не зависит от того, -прожиты ли дни до него. +Устройство и город, закрепленные за посетителем во все дни его активности. +Генератор записывает их в поля событий. У двух посетителей одного человека +город совпадает, а устройства различаются. **Торговое событие**: -Событие корзины или покупки — отдельная строка потока, а не просмотр -страницы. Садится на ту страницу, где случилось: корзина — на карточку -товара, покупка — на страницу подтверждения заказа. +Запись о добавлении товара в корзину или о покупке: `add_to_cart` или +`purchase`. Это отдельная строка кликстрима со страницей, где произошло +действие: карточкой товара для корзины или страницей подтверждения для покупки. + +### Хранилище и форматы данных + +**Хранилище**: +Аналитическая база стенда в ClickHouse. Данные проходят слои STG, ODS, DDS +и DM: от исходных сообщений до витрин для отчетов. Хранилище принимает +данные по документации источников и приводит их к своей модели и именам. +_Избегать_: склад, склад данных. + +**Сырые данные**: +Исходные сообщения Kafka, сохраненные в слое STG без разбора: одно сообщение +занимает одну строку таблицы. Слой STG также называют сырым слоем. +_Избегать_: сырьё. **Справочник**: -Данные, которые хранилище не производит, а получает готовыми со стороны: -содержимым владеет источник вне хранилища, слои только читают. Живут в базе -`dic`. Первый и пока единственный — каталог товаров. -_Избегать_: словарь — этим словом в проекте зовут и глоссарий понятий, и -объект `DICTIONARY` ClickHouse, одну из форм, в которой справочник доступен. +Готовые сведения из внешнего источника, которыми хранилище дополняет +свои данные. На стенде они находятся в базе `dic`; пока это только каталог +товаров. Объект `DICTIONARY` ClickHouse — одна из форм доступа к справочнику. +_Избегать_: словарь в значении справочных данных: это слово уже обозначает +`DICTIONARY` и словарь понятий. **Каталог товаров**: -`data/catalog/products.csv` — общий файл генератора и словаря -ClickHouse. Форма файла решена, длина — нет: строки дописываются. - -**Уровень спроса**: -Свойство товара в каталоге: как часто открытую карточку кладут в корзину. -Три значения — магнит, обычный, залёживается. Постоянная часть мира, а не -поведение дня: он и делает конверсию «просмотр → корзина» по товарам и -брендам различимой. - -**Ось модельного времени**: -Собственный календарь мира генератора. Дни считаются от фиксированного -первого дня D0; реальный календарь в модели не участвует. Между прогонами -живут только зерно и позиция на оси. - -**Стартовый мир**: -Первые восемь дней оси (понедельник по понедельник), которыми стенд -наполняется при каждом `make up`. Он же эталонный мир, всегда один и тот же: -на нем принимаются следующие этапы. -_Избегать_: зерновой мир - -**Паспорт мира**: -`data/world-inventory.json` — единственное, что о мире хранится в git: шапка -(зерно, версия генератора, хеш каталога), строка на каждый день событий -(число и хеш байтов) и строка на каждый отправленный слепок (хеш байтов). -Счётчик сверх этого заводится только с названным читателем. Сам мир не -хранится, а пересчитывается; паспорт отвечает на один вопрос: тот ли это мир. -_Избегать_: опись мира, манифест, мини-манифест - -**Пошаговый режим**: -Базовый способ движения по оси модельного времени: «прожить следующий -день» — явное действие. - -**Живой день**: -Проигрывание текущего модельного дня в реальном времени с ускорением; -включается по требованию — постоянным фоном идёт, только пока включён -выключатель `world_live`. - -**Пакетный режим**: -Проигрывание готового дня пачкой, без темпа: заливка снимка при старте -стенда, пересборки и проверки. - -**Даги модельного времени**: -Даги Airflow, которыми двигают ось модельного времени: шаговые даги играют -день — пачкой или в темпе, — а выключатель держит стенд живущим, пока включён. -Коротко — даги времени. -_Избегать_: пульт мира, управляющие даги - -**Шаговый даг**: -Даг модельного времени, который играет день и ставит позицию на оси: -`world_next_day` — пачкой, `world_live_day` — в темпе. Запускается руками, -расписания и паузы не имеет; автоматику поверх него даёт выключатель. -_Избегать_: работник - -**Граница суток**: -Единственный структурный шов модели: сессии режутся по ней, слепок заказов -снимается на ней, день проживается только целиком. - -**Контракт схемы**: -Python-модуль с описателями колонок события — собственность генератора. -Из него выводятся генератор, валидация и документация формата; хранилище -строится по документации, не по модулю. +Список товаров с артикулами, названиями, категориями, брендами, ценами +и уровнями спроса. Файл `data/catalog/products.csv` общий для генератора +и справочника ClickHouse; товары можно добавлять строками в этом формате. **Нормализованное имя**: -Имя колонки источника, приведённое к нашему стилю (snake_case). Живёт в -контракте схемы и в описании выгрузки. Не то же, что имя атрибута в модели -данных: слой DDS складывает модель и называет атрибуты по ней. +Имя поля источника, записанное в стиле `snake_case`: например, +`ClientID` становится `client_id`. Оно указано в описании выгрузки и +контракте схемы. Слой DDS выбирает имена по своей модели данных, поэтому +они не обязаны повторять нормализованные имена источника. **Описание выгрузки**: -Публичная документация формата события: таблица колонок, собранная из -контракта схемы. По ней пишется сторона хранилища — как в бою по документации -источника. Правится только контракт, документ пересобирается. -_Избегать_: описание схемы, документация контракта - -**Канонический сериализатор**: -Единственная граница, где запись генератора превращается в байты. Фиксированный -порядок ключей и строк — основа побайтовой воспроизводимости. - -**Проигрыватель**: -Компонент доставки готового потока дня в приёмник. Два режима: пакетный -(пачкой, без темпа) и живой день. - -**Слепок**: -Полная выгрузка заказов окна изменяемости, снятая бэкендом на границе суток: -состояние заказов на этот момент, а не поток их изменений. Опоздавший заказ -может отсутствовать в ранних слепках; после первого появления ездит до конца -своего окна. - -**Дата слепка**: -Дата завершившегося модельного дня, состояние которого снято на исходящей -границе суток. Одна для всех записей выгрузки; не дата заказа и не время -отправки сообщения. - -**Время аудита источника**: -Время создания или последнего изменения строки заказа по часам базы источника. -В записи слепка это `created_at` и `updated_at`; бизнес-время покупки живёт в -событии `purchase` отдельно. - -**Судьба заказа**: -Исход, моменты изменений, вычеркнутая позиция и опоздание заказа — всё -решается бросками при его рождении и укладывается в окно изменяемости целиком. -Слепок любого дня — чтение готовой судьбы, а не накопление состояния. - -**Окно изменяемости**: -Сколько модельных дней заказ ещё может измениться и потому продолжает ездить -в слепках. Константа мира: семь дней. За окном заказ замёрз, выручка дня -перестала «дышать». - -**Пакетный забор**: -Способ приёма топика, при котором данные тянет запрос по команде, а не -матвью непрерывно. Так стенд принимает заказы: у слепка есть начало и конец, -и забирать его уместно тогда, когда он собран целиком. Противоположность — -потоковый приём событий. -_Избегать_: путать с приёмником и пакетным режимом — те про сторону -генератора, про отправку; забор — про сторону хранилища, про чтение. +Документ, который объясняет поля и формат событий источника: +[`docs/formats/clickstream-event.md`](docs/formats/clickstream-event.md). +По нему строится прием данных в хранилище. Документ собирается из +описаний колонок генератора: правят их, затем пересобирают документ. +_Избегать_: описание схемы, документация контракта. **Техническая проверка**: -Проверка, чей оракул — внутренний эталон стенда (паспорт мира), которого у -настоящего магазина нет. За учебный урок DQ не выдаётся. -_Избегать_: DQ-проверка — та работает на наблюдаемых данных магазина. +Проверка по внутреннему эталону стенда, например сравнение числа событий +с паспортом мира. Проверка качества данных (DQ) опирается на данные +магазина и связи между ними; ей не нужен эталон генератора, которого +у настоящего магазина нет. +_Избегать_: DQ-проверка в значении проверки по паспорту мира. + +### Заказы + +**Слепок**: +Дневная выгрузка состояния заказов из серверной части магазина. +Она содержит заказы, оформленные за последние семь модельных дней, +с учетом изменений к концу дня. Опоздавший заказ может отсутствовать +в первых слепках. + +**Дата слепка**: +Дата модельного дня, на конец которого снято состояние заказов; +поле `snapshot_date` одинаково во всех строках выгрузки. Даты оформления +заказов могут различаться; выгрузка отправляется на следующий модельный день. + +**Время аудита источника**: +Время создания и последнего изменения записи заказа по часам базы +магазина: поля `created_at` и `updated_at`. Время покупки в событии +`purchase` записывает счетчик сайта, и оно может отличаться от этих полей. + +**Окно изменяемости**: +Семь модельных дней, включая день оформления, в течение которых заказ +может измениться и входит в слепки. После этого его состояние окончательно. +Например, заказ от 1 июня входит в окно слепков за 1–7 июня; опоздание +сокращает число его появлений, но не сдвигает конец окна. + +**Пакетный забор**: +Чтение сообщений Kafka отдельным запросом по команде. Так хранилище +принимает заказы после отправки всего слепка. События сайта оно принимает +непрерывно через материализованное представление. +_Избегать_: смешивать с пакетным режимом генератора: забор — чтение +на стороне хранилища, режим генератора — отправка. + +### Модельное время и запуск стенда + +**Мир**: +Модель интернет-магазина: люди, их посещения сайта, товары и заказы, +которые создает генератор. + +**Ось модельного времени**: +Календарь магазина в генераторе. Первый день, D0, — 1 июня 2026 года, +понедельник (`world.ORIGIN`); следующие дни отсчитываются от него. +Реальное время — часы компьютера, на котором работает стенд. Дата запуска +не меняет даты событий: D0 остается 1 июня, даже если стенд запущен осенью. + +**Граница суток**: +Конец одного модельного дня и начало следующего, полночь по часовому +поясу счетчика. Здесь заканчиваются визиты и снимается состояние заказов +для слепка. Даги времени продвигают мир только на целые дни. + +**Стартовый мир**: +Данные первых восьми модельных дней, с понедельника по следующий +понедельник, которыми стенд наполняется при первом `make up`. +Команда вызывает `world_initialize`: если мир уже готов и сохранен, +повторный запуск оставляет данные и позицию времени прежними. +`make rebuild-storage` пересоздает стартовый мир. При неизменных коде, +каталоге и настройках его данные повторяются и служат эталоном проверок. +_Избегать_: зерновой мир. + +**Паспорт мира**: +Файл для сверки с эталонными данными: `data/world-inventory.json`. +В нем записаны зерно генерации, версия генератора, контрольная сумма +каталога, число событий и контрольная сумма каждого начального дня, +контрольные суммы слепков и счетчики расхождений по заказам. +В git хранится паспорт; сами события и слепки пересчитываются генератором. +_Избегать_: опись мира, манифест, мини-манифест. + +**Пошаговый режим**: +Продвижение модельного времени по явной команде: запустить генерацию +следующего дня или нескольких дней. + +**Пакетный режим**: +Отправка событий готового модельного дня без пауз между ними. +Используется для начального наполнения, пересборок и проверок. + +**Живой день**: +Постепенная отправка событий по времени их появления в модельном дне, +с ускорением: по умолчанию сутки занимают около 24 реальных минут. +Чтобы следующие дни запускались автоматически, снимают с паузы `world_live`. + +**Даги модельного времени**: +Наборы связанных задач Airflow для генерации следующих дней магазина: +`world_next_day`, `world_live_day` и `world_live`. Первые два выполняют +шаг по времени, третий автоматически запускает живые дни, пока снят с паузы. +Короткое название — даги времени. +_Избегать_: пульт мира, управляющие даги. + +**Шаговый даг**: +Даг, который отправляет данные следующих модельных дней и после успеха +записывает новую позицию времени. `world_next_day` отправляет их пачкой, +`world_live_day` — один день с ускорением. У них нет расписания; +их запускают вручную или из другого дага. +_Избегать_: работник. + +### Устройство генератора + +**Зерно генерации**: +Число, которое задает повторяемую последовательность случайных выборов +генератора. Одинаковые зерно и день дают одинаковые данные при неизменных +коде, каталоге и настройках. Эталон использует `CANONICAL_SEED` из `seeds.py`. + +**Состав мира**: +Люди в модели, их идентификаторы посетителей и заранее заданные привычки: +когда они приходят, как часто возвращаются, склонны ли покупать. +У части людей две куки — это двухкуковые пары. +_Избегать_: состояние мира. + +**План состава**: +Расчет того, какие посетители придут в каждый день и какие свойства у них +будут. В `plan.py` из зерна и номера дня получаются новые посетители, +их возвраты и назначенные заказы людей с двумя куками. + +**Когорта дня**: +Группа людей, впервые пришедших в один день, со всеми их куками и днями +активности. Генератор рассчитывает ее по зерну и номеру первого дня. + +**Приток**: +Появление новых посетителей (`ClientID`) по мере продвижения модельного +времени. Поэтому число разных посетителей за весь период растет и +не совпадает с числом посетителей за отдельный день. + +**Хвост возвратов**: +Срок от первого появления человека, в пределах которого его куки могут +возвращаться на сайт. У человека с двумя куками конец срока общий. + +**Предыстория**: +Люди, впервые пришедшие до D0, и план их активности. До D0 события +не создаются; возвраты этих людей дают повторных посетителей уже в первый +день генерируемых данных. + +**Покупатель**: +Человек, которому генератор заранее назначил повышенную склонность +покупать. Он чаще доходит до заказа, но иногда покупают и остальные. +Из таких людей выбираются те, у кого будут две куки. +_Избегать_: покупатель в значении любого посетителя с покупкой за день; +для этого случая — «визит с заказом». + +**Уровень спроса**: +Свойство товара, от которого зависит, как часто после просмотра карточки +его добавляют в корзину. В колонке `demand` каталога три значения: +`магнит`, `обычный`, `залёживается`. Они постоянны по дням и задают +различия в конверсии товаров и брендов. + +**Конфигурация мира**: +Числовые настройки модели магазина в `world.py`: приток, частота возвратов, +доли покупателей, начальная дата и другие параметры. Изменение этих +настроек меняет генерируемые данные. + +**Подпоток**: +Отдельная последовательность случайных выборов для части генератора, +например для торговли в заданный день. В `seeds.py` она определяется +общим зерном, днем и компонентом; для состава мира есть отдельные +последовательности. Результат не зависит от порядка расчета частей. + +**День-функция**: +Расчет событий одних модельных суток по зерну и номеру дня, без генерации +предыдущих дней. Функция `day.stream(seed, day)` возвращает события +в порядке времени. + +**Судьба заказа**: +Заранее рассчитанные изменения заказа: итоговый статус, время оплаты +или отмены, удаленная позиция и задержка первого появления в слепке. +Они укладываются в семидневное окно заказа. Слепок показывает состояние +на выбранный день без проигрывания предыдущих изменений. + +**Контракт схемы**: +Описания колонок события в модуле генератора `schema.py`: имена, +типы и ограничения. По ним генератор создает и проверяет события, +а `schema_doc.py` собирает описание выгрузки для хранилища. + +**Канонический сериализатор**: +Код в `serialize.py`, который превращает события и записи заказов в JSON +для отправки. Порядок ключей и формат значений фиксированы, поэтому +одинаковые записи дают одинаковые байты. + +**Проигрыватель**: +Код в `player.py`, который доставляет готовые события дня в приемник, +например в Kafka или файл. В пакетном режиме он отправляет их без пауз, +в режиме живого дня — с ускорением по времени событий. -- 2.54.0