feat(dds): добавлена модель сессии и нарезка визитов окнами
Зачем: - Дать слою расчётную сущность, которой в потоке нет: стенд режет визиты сам, а VisitID генератора остаётся эталоном самопроверки. - Отделить прожитый день от живого — иначе визит, начатый в 23:50, замёрз бы обрезанным навсегда: пересобирать замёрзший день слой не станет. Что: - Добавлены dds.session_rep/_dist, двойник-донор dds.session_stage_rep/_dist и публичный договор dds.session_v. - Нарезка стоит на двух окнах: первое ставит шов там, где пауза длиннее тридцати минут, второе считает нарастающий итог швов — номер визита. Границу суток держит разбиение окна, отдельной проверки ей не нужно. - Порядок в окнах полный, по метке времени и event_id: сортировок две, и при равных метках они разошлись бы, разорвав сессию надвое. - Объём прогона считается от первой дыры в слое по последний прожитый день; прожитый день выводится из позиции мира и первого дня событий. Собирать нечего — группа задач пропускается, а не зеленеет впустую. - В dds_transform появилась группа session рядом с заказом; зависимости по данным между ними нет. - Потолок max_active_tasks: размноженная замена без него выносит планировщика за отведённые ему 640 МиБ. - parallel_distributed_insert_select у сессий не назначается вслух — ключ шардирования у источника и цели один по контракту ко-локации. - Записано, что позиция отвечает за «прожит», а не за «доехал»: сторож хвоста потока приезжает отдельным тикетом. Проверка: - make lint - make smoke (21 проверка), make check-clickhouse (10), make check-services - Стартовый мир, пересобранный с нуля: 79367 сессий за восемь дней; расхождение с uniqExact(VisitID) по паре «кука × день» — ноль; все 401185 событий ровно в одной сессии; session_id уникальны. - Ни одной паузы длиннее тридцати минут внутри сессии и ни одной пары соседних визитов куки, разделённой меньшим промежутком. - Повтор после DROP PARTITION ALL: те же session_id и то же содержимое строк. - Снесённая руками партиция 2026-06-04: следующий прогон взял хвост от неё, дни до дыры не тронуты. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c361c36e3d
commit
176cf5520d
9 files changed
+495
-60
No files matched your search
@@ -40,8 +40,8 @@
|
||||
## Объём прогона
|
||||
|
||||
Параметров у конвейера нет: объём он выводит сам из `world_position` и
|
||||
состояния слоёв. Сессии догоняют хвост — от последнего собранного дня до
|
||||
последнего прожитого; заказы пересобирают дышащее окно K и тот же хвост; карта
|
||||
состояния слоёв. Сессии догоняют хвост — от первого несобранного дня по
|
||||
последний прожитый; заказы пересобирают дышащее окно K и тот же хвост; карта
|
||||
идентичностей пересчитывается целиком; правила витрин — у
|
||||
[слоя DM](dm.md).
|
||||
Поэтому один и тот же запуск одинаково верен из триггера, с кнопки менти и
|
||||
@@ -56,6 +56,16 @@
|
||||
неудачного приёма она обгоняет хранилище. Слой обязан считать объём по тому,
|
||||
что у него есть, а не по тому, что мир о себе заявляет.
|
||||
|
||||
У сессий всё наоборот: им отвечает только позиция, потому что вопрос у них
|
||||
другой. Им нужен не день, о котором рассказал источник, а день, который мир
|
||||
**прожил**: события живой день от прожитого не отличают — и посреди дня D, и
|
||||
сразу после его конца позднейший день событий равен D. Соберись сессии посреди дня, визит, начатый в
|
||||
23:50, остался бы обрезанным навсегда: замёрзший день слой не пересобирает.
|
||||
Отсюда правило — последний прожитый день равен первому дню событий плюс позиция
|
||||
минус один ([`sql/dds/session_scope.sql`](../../sql/dds/session_scope.sql)).
|
||||
Оговорка про «доехал» остаётся в силе и здесь: за хвост потока отвечает не
|
||||
позиция, а «Готовность источника».
|
||||
|
||||
Ярлыки доставки — `snapshot_date`, `_load_id`, `_load_ts` — не отвечают тоже, и
|
||||
это свойство ODS, а не недосмотр: слой схлопывает доставки, сохраняя деловые
|
||||
версии, поэтому у заказа, который не менялся, ярлык выжившей строки достаётся
|
||||
@@ -67,14 +77,18 @@
|
||||
|
||||
## Готовность источника
|
||||
|
||||
Первый шаг `dds_transform` — дождаться, пока хвост топика `hits` дожёван:
|
||||
отставание его потребителей нулевое (`system.kafka_consumers`). Зелёный
|
||||
приём заказов о событиях не говорит ничего — топики разные и потребители
|
||||
разные. В живом режиме зазор ничтожен, но в пакетном генератор выстреливает
|
||||
дни в топик быстрее, чем чтец их прожёвывает, и сессии собрались бы по
|
||||
недожёванному дню. DQ-сверка такой день подсветит, но починить его до
|
||||
Первым шагом `dds_transform` обязан дождаться, пока хвост топика `hits`
|
||||
дожёван: отставание его потребителей нулевое (`system.kafka_consumers`).
|
||||
Зелёный приём заказов о событиях не говорит ничего — топики разные и
|
||||
потребители разные. В живом режиме зазор ничтожен, но в пакетном генератор
|
||||
выстреливает дни в топик быстрее, чем чтец их прожёвывает, и сессии собираются
|
||||
по недожёванному дню. DQ-сверка такой день подсветит, но починить его до
|
||||
этапа 5 нечем: профилактика дешевле ремонта, которого ещё нет.
|
||||
|
||||
Шага этого в даге ещё нет — он приезжает отдельным тикетом (#156), и до тех
|
||||
пор дыра открыта: сессии считают день прожитым по позиции мира, а хвост потока
|
||||
не стережёт никто. На приёмке отставание смотрят руками.
|
||||
|
||||
## Идемпотентность
|
||||
|
||||
Партиция не заменяется простой вставкой. День собирается в таблицу-двойник —
|
||||
|
||||
Reference in new issue
Block a user