Генератор слепков: где живёт и чем связан с событиями #71
Notifications
Due Date
No due date set.
Blocks
#72 Расхождения A–D и опоздания: механика, доли и что обещано
ddmitry/clickstream-data-platform
#73 Мост к склейке: user_id и двухкуковые пары
ddmitry/clickstream-data-platform
#80 Брак в слепке: куда уходит и по каким классам
ddmitry/clickstream-data-platform
Reference: ddmitry/clickstream-data-platform#71
Reference in New Issue
Block a user
Part of #69.
Вопрос
Где живёт генератор заказов — отдельным компонентом или вторым режимом существующего пакета, — и как заказ остаётся согласованным со своим событием
purchaseот того же зерна?Ограничение снизу жёсткое: в генераторе кликстрима порядок бросков внутри подпотока — часть контракта, и новая случайность, вставленная в середину, сдвигает весь мир (спека генератора,
docs/specs/2026-08-01-generator.md). Значит вопрос не только «где код», но и «из какого подпотока берёт случайность заказ».Смежное: как собирается слепок окна K = 7 модельных дней и что происходит при пропущенном дне (мастер-спека обещает самовосстановление следующим слепком).
Резолюция
Рамка. Заказ и событие
purchase— не два порождения, а две проекции одногофакта мира. Корзина, цены, купон и номер заказа уже посчитаны торговой половиной
дня-функции, поэтому согласованность не удерживается, а получается по построению.
1. Где живёт
Заказ — второй выход торговой половины:
commerceотдаёт заказы дня структурой,заказная половина навешивает жизнь заказа и собирает слепок. Ни одного нового
броска в подпотоке
COMMERCE— мир не сдвигается.Отклонено: выводить заказ разбором собственного вывода (
purchaseID,purchaseRevenue, сыройecommerce) — бэкенд стал бы читателем трекера ровнотам, где стенд учит, что это разные источники; независимая модель бэкенда
(заказ первичен, событие — эхо, как в жизни) — кто купил, решает воронка, а
воронка это трафик, то есть опрокидывание всего генератора; источника заказов
нет, слепок собирает SQL стенда из событий — второй источник исчезает вместе с
уроком «две версии правды», а ADR 0008 уже построен под топик.
Следствие: у всякого заказа изначально ровно одно событие
purchase. Заказ безсобытия в трекере — не отдельная порода заказов, а класс B, и делает его #72
выбрасыванием события после присвоения номера.
2. Как запускается
Третья команда того же пакета —
snapshot --day D [--days N], свой приёмник,топик
orders. Два источника — два запуска: трекер и бэкенд видны глазамикак два производителя, каждый со своим топиком. Довод учебный: один прогон,
отдающий оба потока, тихо сказал бы, что это одна система.
Отклонено: один прогон с двумя выходами (
--orders-topic) — экономии,ради которой его стоило бы терпеть, он не даёт: со сдвигом на день (пункт 4) окно
слепка и сыгранный день не пересекаются вовсе, переиспользовать нечего. А правило
«приёмник выбирается тем, что для него назвали» он ломает, и в живом дне сводит в
один прогон два разных режима доставки; отдельный пакет и образ — общими остались бы план состава, каталог,
числа мира и торговая половина, то есть библиотека на двоих ради одной команды;
генератор пишет слепок файлом, в топик льёт даг — второй путь доставки и второй
сериализатор; слепок едет топиком
hits— убивает два режима приёма ADR 0008.3. Как собирается окно K = 7
Переигровкой дней D−6…D: заказы дня — производная всей воронки дня, дешёвого пути
к ним нет. Цена — семь проигрышей дня (~14 с) на слепок; подряд идущие дни в
одном прогоне считаются по разу, у начала оси окно усекается само.
Отклонено: кэш заказов дня на томе — заводит состояние между прогонами, том и
инвалидацию, а спека генератора уже отвергала промежуточные файлы; дешёвый путь
к заказам мимо сборки 47 колонок — заказу нужны и время события, и порядок
потока, и
WatchID, экономятся только посточные проходы: оптимизация, а неустройство; окно держит хранилище (генератор шлёт заказы своего дня, полный
слепок собирает SQL из
ods.order_snapshot) — топик перестаёт нести слепок,замена партиции теряет смысл, ADR 0008 переписывается; K = 1 — это страховочный
срез 1 мастер-спеки, он в резерве, а не применён.
4. Когда отправляется
Снимается на границе суток, отправляется следующим прогоном. Даг, играющий
день D, отправляет слепок дня D−1 — ночная выгрузка бэкенда за вчера, как в бою.
Содержимое слепка — чистая функция (зерно, D), от момента отправки не зависит,
поэтому «когда отправили» лежит по транспортную сторону.
Следствия:
обещано:
live --day D— долгий процесс на ~24 реальные минуты, а24-минутный даг из спеки генератора (раздел 8) — это ETL со стороны хранилища,
а не запускатель генератора. Слепок живого дня отправит следующий прогон.
писать не надо: слепок переснимается и даёт те же байты.
awaiting_order— ровно тот сюжет«вчера не сходилось, сегодня сошлось», ради которого мастер-спека этот класс и
завела.
замыкается на только что сыгранном дне, и внимательная реализация обошлась бы
семью проигрышами; со сдвигом окно и сыгранный день не пересекаются, и их всегда
восемь. Около двух секунд за то, что живой день перестал быть особым случаем.
Словарю это не противоречит: «граница суток — слепок заказов снимается на ней»
остаётся верным. Снимается на ней, отправляется позже.
5. Откуда случайность
Свой компонент дня в конце перечисления
Component, ветвление по дню рождениязаказа. Добавление в конец перечисления ничего не сдвигает — подпоток задан
позицией в дереве, а не порядком вычислений.
DISCREPANCIESиLATECOMERSостаются зарезервированными за #72.
Вся судьба заказа решается при рождении, поэтому слепок любого дня — чтение
готовой судьбы, а не накопление состояния.
Отклонено: дописывать броски в конец
COMMERCE— правка заказа и правкаторгового поведения стали бы одним рычагом, а «дописывай строго в конец» —
негласным правилом без сторожа; своя ветвь корня рядом с составом мира и днями —
ось дней завелась бы второй раз; бросать состояние в подпотоке дня слепка —
траектория заказа зависела бы от того, какие слепки снимали.
6. Момент оплаты — бросок, а не правило
При фиксированном сроке
updated_at − created_atодинаков у всех оплаченных, аточные равенства менти читает как склейку в разметке, а не как поведение людей
(урок правки #50).
Форма — таблица целых весов «сколько часов — с каким весом», как
RETURN_DELAY_WEIGHTS. Плавающие распределения не зовутся: дисциплинацелочисленной случайности (спека генератора, раздел 2) снимает межархитектурные
расхождения, а на побайтовом совпадении стоят CI и опись.
Хвост меряется в часах, но тянется на дни: уложись все оплаты в первые сутки,
слепок d+1 вёз бы уже окончательное состояние, а дни d+2…d+6 — неизменные
строки, и «дыхание» выручки внутри окна исчезло бы.
Вес за краем окна означает «не оплачен никогда». Бросок решает не «когда
оплатят», а «оплатят ли и когда». Правила усечения не нужно ни в каком виде;
обещание «за окном заказ неизменяем» становится буквально верным — мир сам
говорит «не оплачен», и хранилище с ним согласно; а стенд получает урок сверх
прежнего: не всякий заказ — выручка. Долю неоплаченных писать в таблице явной
строкой, а не оставлять читателю складывать хвост в уме.
Конкретные веса — калибровка при реализации; здесь решена форма.
Правило шире момента оплаты
Вся судьба заказа обязана уложиться в окно K либо не случиться вовсе. Отмена
подчиняется тому же пределу.
Швы соседям
удвоится и «десять процентов приезжают на D+1» станет D+2. Отмена укладывается
в окно по правилу выше. И появился исход, которого нет в таблице раздела 4
мастер-спеки: заказ навсегда в
createdпри доехавшемpurchase— суммысходятся, событие на месте, выручки нет. Получает ли он своё имя в
mismatch_class— решать там.отправляет при сдвиге на день и куда девается последний.
причина не в живом дне (он своего слепка не отправляет), а в падении между
шагами: офсеты коммитятся при чтении (ADR 0008), поэтому прогон, упавший после
отправки и до забора, оставляет свой слепок в топике, а следующий кладёт рядом
второй. Шаг забора обязан заменить столько партиций, сколько приехало.
модельный день генератором, поэтому переливается ровно то, что он положил в
топик». Со сдвигом фраза остаётся верной буквально — даг и правда сам положил
слепок дня D−1, — но читается как «переливается слепок сыгранного дня», а он
предыдущего. Уточнить тем же коммитом, что и спека (заметки карты #69).
Что всплыло по дороге
Чем деньги, вложенный JSON позиций и даты выглядят на проводе, не решено ни
одним документом, а урок класса C стоит именно на различии Float64 и Decimal.
Заведено тикетом #81, блокирующим #80.