- Зачем:
- в docs/labs/ будет много заданий; номер ступени и номер задания в имени
делают папку оглавлением программы и избавляют от поиска по README (#230).
- Что:
- шесть ТЗ переименованы через git mv: 1-1…1-5 для ступени 1,
2-1-superset-chart.md для ступени 2; факультатив superset-dashboard.md
остался без номера.
- обновлены ссылки в docs/labs/README.md, 2-1-superset-chart.md,
docs/architecture/dds/session.md и пути в dags/homework_check.py.
- правило имен для docs/labs/ в AGENTS.md переписано под S-N-slug.
- Проверка:
- make lint; скрипт обхода относительных ссылок в docs/ и AGENTS.md —
битых нет.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
14 KiB
Модель сессии в DDS
Учебный результат: менти строит расчётную сущность, которой нет в потоке, —
режет события на сессии по правилу визита, чеканит идентификатор и сверяет
свою нарезку с эталоном. Стенд здесь играет роль сессионизатора: в бою её
играет Метрика, которая режет визиты у себя и с октября 2025 отдаёт готовый
VisitID прямо в хитах. Поэтому стенд считает сессии сам, а VisitID
генератора использует только как эталон самопроверки — иначе сборка сессий
выродилась бы в GROUP BY по чужому полю и центральный урок
слоя пропал бы. Без этого абзаца действия стенда выглядят странно: «зачем
считать то, что уже дано колонкой».
Зерно
Одна строка — один визит (CONTEXT.md): подряд идущие
события одной куки без пауз длиннее 30 минут, не пересекающие границу суток.
В нарезку входят все три типа событий — торговые садятся на свои страницы
и держат сессию наравне с просмотрами. События внутри сессии
упорядочиваются по метке времени события (UTCEventTime — опорная колонка
приёма).
Граница суток проходит по модельному дню счётчика, и он у события уже есть:
EventDate — свойство содержимого. Раз сессия сутки не пересекает, у всех
её событий EventDate один; он и становится днём сессии — колонкой
партиции, без пересчёта.
Идентификатор
Идентификатор сессии в природе не существует: сессия — расчётная сущность,
и id ей чеканит тот, кто сессионизирует. VisitID — такой же суррогат,
отчеканенный генератором в роли Метрики; наш — session_id,
детерминированный хеш от пары «кука + начало сессии» (cityHash64).
Детерминированность обязательна: партиция дня пересобирается заменой, и
повторный прогон обязан отчеканить те же id — счётчик или UUID это правило
ломают.
session_id — не ключ сортировки и не ключ шардирования, а колонка-ручка:
в соединениях он не участвует — события его не несут, мост к заказам живёт
на событиях. Читатели ручки — лаба сессий (сопоставить свою нарезку со
стендовой и назвать расхождение одним числом) и разбор DQ-расхождений.
Состав строки
Боевой образец — таблица визитов Метрики, парная к нашему формату хита; по ней состав и собран:
- ключи и времена: кука,
session_id, начало и конец сессии, длительность, день сессии. Времена — в поясе счётчика по правилу слоя; - счётчики: событий всего, просмотров, корзин, покупок;
- страницы: входная и выходная — как
StartURL/EndURLвизита; полного списка страниц нет и у Метрики, топы считаются по событиям; - атрибуция: UTM-метки первого события сессии — так визит атрибутирует Метрика. В v1 витрина брала последнее касание — заменено осознанно;
- паспорт куки: категория устройства (расшифрованная, язык бизнес-модели) и город — у куки они постоянны, берутся с первого события;
- служебные:
_load_idи_load_tsсобравшего запуска — правило слоя (модель заказа, «Служебные колонки»).
Осознанные расхождения с образцом: денег в сессии нет — визит Метрики несёт клиентскую выручку, у нас деньги считает только бэкенд (спека, раздел 7), а клиентская сумма живёт в сверке; целей нет — их место заняли счётчики торговых событий.
Движок и наполнение
dds.session_rep — ReplicatedMergeTree без колонки версии, по образцу
заказа: партиция — день сессии, ключ сортировки — кука и начало сессии.
dds.session_dist шардирует по cityHash64(client_id), тем же выражением,
что события, — это ко-локация с dds.identity_map_rep (дока
хранилища, «Раскладка по шардам»).
Совпадение ключа с событиями не случайно: все события куки лежат на одном
шарде, поэтому сессионизация локальна по природе — капкан смены ключа при
вставке, который дока хранилища обещает сборкам DDS, у сессий не стреляет.
Сборка — дневной батч: хранилище выше ODS по смыслу батчевое, раз второй
источник приходит раз в сутки. Собирается день, который мир уже прожил, и
собирается один раз и целиком; какой день прожит, слой спрашивает у позиции
мира (дневной конвейер, «Объём прогона»). Идемпотентность держит
замена партиции. В отличие от заказов, где батч пересобирает до K «дышащих»
дней, у сессии дышащего окна нет: прожитый день самодостаточен (правило
спеки, раздел 6). Второй раз он соберётся только
вынужденно — когда упавшая замена оставила позади дыру и хвост от неё накрыл
соседние дни. Дней в одном прогоне при этом бывает несколько — столько,
сколько мир успел прожить без слоя. Внутри живого дня
модель сессии про сегодня честно молчит — «сегодня» смотрят через
dds.event_v; сессия и по смыслу не финальна, пока день не прожит.
Публичная поверхность — dds.session_v: обычное представление с явным
списком деловых и собственных служебных колонок над dds.session_dist.
Роли объектов и доступ следуют общей конвенции, «Имена
объектов».
Источник сборки — dds.event_v (контракт события): сессии
нужны от него ключи нарезки, тип события, страница, UTM и паспорт куки.
Механика запуска — где собирается партиция и как подменяется на кластере —
не входит: это устройство дневного конвейера.
Сверка с эталоном
Счётная DQ-проверка с зерном «кука × день»: число сессий нашей нарезки
равно uniqExact(VisitID) по тем же событиям. Счёт по Distributed ловит и
склеенную, и разрезанную сессию — мимо него проходит лишь взаимная
компенсация ошибок внутри одной куки за день; эталон в модель не тащим —
строка сессии остаётся нашей, а не генераторной. Поштучное сопоставление
границ — материал лабы, где менти разбирает найденное расхождение руками. Дом проверки —
контур DQ этапа 4. Сверке нужен VisitID, а сборке сессий — нет; контракт
dds.event_v его скрывает, и за эталоном проверка идёт в ods.event_v
(контракт события).
Читатели
- Витрина обзора сессий
session_overview_v: дневные итоги визитов в срезе устройств; читаетdds.session_vи поднимает зерно до дня — состав и довод в ее ТЗ (#135). - DQ-сверка с эталоном (см. выше).
- Лаба сессий: менти собирает нарезку сам до знакомства с кодом стенда, потом сравнивает — с эталоном и со стендовой.
Не входит
- Механика запуска трансформации и замены партиций на кластере — дневной конвейер.
- Контур проверок качества (даг DQ,
dm.dq_summary) — отдельный разговор этапа 4. - Форма витрин над сессиями — #135.
Отклонённые варианты
- Готовый
VisitIDвместо своей нарезки. Сборка выродилась бы вGROUP BYпо чужому полю: центральный урок слоя съеден, сверка с эталоном осталась бы без предмета. Боевой довод «Метрика теперь отдаётVisitIDв хитах» честно рассказывается в лабе — после того как менти собрал сам. - Гибрид: сборка по
VisitID, DQ пересчитывает нарезку. Сторож на нарезку, которой сборка не делает; та же работа, что в варианте «считать сам», но урок спрятан в проверку. - Одно динамическое представление вместо физической таблицы. Оконная
сессионизация на каждое чтение;
урок «слой — контракт, а не копия» уже закреплён за
dds.event_v— доводы модели заказа, здесь они только сильнее. Публичноеdds.session_vповерх готовой физической модели ничего заново не считает. - Счётчик или UUID вместо детерминированного хеша. Пересборка партиции выдала бы другие id — замена перестала бы быть идемпотентной.
VisitIDколонкой в строке сессии. Чужой чекан в своей модели: наша сессия обязана уметь расходиться с эталоном, на этом стоит сверка; поштучное сопоставление — лаба, не таблица.- Массив посещённых страниц. Вес без читателя: вход и выход — как у визита Метрики, топы страниц считает витрина по событиям.
- UTM последнего касания (как в v1). Визит атрибутируется своим первым хитом; последнее касание — атрибуция другого уровня, не сессионного.
- Деньги в сессии. Выручка — только по бэкенду; клиентская сумма живёт в сверке (спека, разделы 4 и 7).
Связанные решения
- Модель заказа — прецедент замены партиций в DDS и правило служебных колонок слоя.
- Дока хранилища — раскладка по шардам, конвенции имён, часовые пояса.
- Мастер-спека, разделы 1.2 и 7 —
VisitIDкак эталон самопроверки (принятое отступление приёмки), карта таблиц. - CONTEXT.md — «Визит», «Граница суток», «День активности».