Сверка purchase против заказов: dm.purchase_vs_orders (A+C) #155
Notifications
Due Date
No due date set.
Blocks
Depends on
#158 Звено DQ конвейера и dm.dq_summary
ddmitry/clickstream-data-platform
#154 Витрины стенда: даг dm_transform, revenue_daily и daily_traffic
ddmitry/clickstream-data-platform
Reference: ddmitry/clickstream-data-platform#155
Reference in new issue
Block a user
Part of #6.
Цель
Витрина сверки A+C: FULL OUTER GLOBAL JOIN клиентских
purchaseи заказовбэкенда, классы расхождений приоритетом, служебный класс
awaiting_orderвнутри живого окна. Учебный результат — сюжет этапа: «поведение по
трекеру, деньги по бэкенду» на живых расхождениях A (отмена) и C (дельта
суммы).
Что войдёт
dm.purchase_vs_orders_rep/_dist/_v: зерно — заказ; колонкиспеки §7 (
order_day,order_id,declared_revenue,items_total,status,mismatch_class); шардированиеcityHash64(order_id)—ко-локация заказов сохранена.
purchase_id[1] = order_id— имя контракта, вODS это
purchaseID; правило одного элемента массива — комментарием(спека §4); обе стороны — подзапросы (FULL OUTER с голой левой
Distributed двоит непарные строки — исследование #129); сравнимая
база —
items_total, неtotal. Вставка в_dist— с осознаннымparallel_distributed_insert_select: источники разложены поclient_idи
order_id, цель — поorder_id(капкан storage.md).cancelled>lost_event>duplicate_event>amount_delta>match;awaiting_order— внеприоритетов, для
purchaseбез заказа внутри живого окна. До этапа 6наблюдаемы только
match,cancelled,amount_delta,awaiting_order— классификатор данных не выдумывает.purchase_vs_ordersв
dm_transform.Границы
не трогать.
Кому что
SQL сверки — Opus: главный учебный SQL этапа. Codex — счётные опыты по
классам по брифу.
Сначала прочитать
спека тикета;
purchase_*вконтракте).
Проверка
make lint; на стендеmake smoke,make check-clickhouseиmake check-services(задача в даге) зелёные.mismatch_classпринимает толькоmatch,cancelled,amount_delta,awaiting_order;match— большинство;cancelledиamount_deltaнаблюдаемы (счёт > 0) — точные счётчикисверит звено DQ против описи.
amount_deltaобъясняется позициями: дельта равна суммевычеркнутой позиции.
awaiting_orderживёт только в днях живого окна; в днях с закрытымокном таких строк нет.
на шарде его
order_id.Резолюция вопросов готовности
Перед реализацией сверены мастер-спека,
dm.md,etl.md, модель стартового мира и фактический мир генератора. Ниже — договор, который закрывает оставленные в постановке развилки.Объём пересборки
У сверки два источника с разными правыми границами. Заказная сторона берёт изменяемые дни из последнего принятого слепка ODS; событийная — дни, которые мир уже прожил.
world_positionопределяет только завершённость событий и не подменяет границы слепка заказов.Прогон заменяет один непрерывный отрезок:
purchase;На стартовом мире это дни 0…7: последний принятый слепок несёт дни 0…6, а в прожитом дне 7 есть 255 покупок без заказа. Поэтому обычный прогон сверки затрагивает K + 1 день, но это следствие состояния двух источников, не арифметика от K.
Отклонено: вычислять K + 1 только от
world_position— позиция не доказывает, какой слепок принят; полностью пересчитывать историю — закрытые дни теряют обещание замерзания; подмешивать живой хвост отдельным представлением — в главном учебном SQL появляется вторая механика без нового урока.Деньги события хранятся как Decimal
Внешний формат остаётся честным: трекер присылает
purchaseRevenueкакArray(Float64), и генератор с описанием выгрузки не меняются. Граница хранения один раз нормализует денежную величину:ods.event_rep.purchaseRevenue—Array(Decimal(18, 2));dds.event_v.purchase_revenueнаследует этот тип без нового вычисления;declared_revenueстановится скаляромNullable(Decimal(18, 2))после свёртки покупки до зерна заказа.Так ODS сохраняет смысл поля источника — деньги с точностью до копейки, — но не переносит вверх неподходящий для денег машинный тип. Сырой JSON остаётся в STG на срок расследования. Создавать ради одной колонки физическую копию широкого события в DDS или повторять округление в каждой витрине не нужно.
Проверка репозитория на текущем
mainне нашла исполняемых потребителейpurchaseRevenue/purchase_revenue: колонка пока только сохраняется и переименовывается. Первые читатели — эта сверка и будущаяutm_effectiveness, поэтому изменение входит в скоуп #155 с ограниченным радиусом. При реализации спорный DDL прямого разбораArray(Decimal(18, 2))сверить через Context7 и на поднятом ClickHouse 26.3; проверку ожидаемой схемы ODS поправить вместе с DDL.Непарные строки и публичный договор
Отсутствие стороны соединения — данные, а не значение по умолчанию. SQL выполняет FULL OUTER JOIN с обнуляемыми непарными колонками (
join_use_nulls = 1); нули и пустые строки стендового умолчанияjoin_use_nulls = 0здесь запрещены, потому что стирают различие «нет события / нет заказа».order_idиorder_dayобязательны: берутся с имеющейся стороны; у пары дни совпадают по модели мира;declared_revenue—Nullable(Decimal(18, 2)); словоdeclaredсохраняет её клиентское происхождение;items_total—Nullable(Decimal(18, 2));status— обнуляемая низкокардинальная строка;mismatch_classобязателен;_load_id/_load_tsвходят в физическую строку и явный договор_vпо правилуdm.md.purchaseбез заказа получаетawaiting_order. Затем для строк с заказом действует приоритет спеки:cancelled>lost_event>duplicate_event>amount_delta>match. Благодаря этому отменённый заказ без события остаётсяcancelled, а неlost_event.Событийная сторона
События
purchaseсворачиваются до зерна заказа до соединения: номер изpurchase_id[1], число событий дляduplicate_event, одна дата и одна объявленная сумма. Генератор гарантирует, что у сюжетного дубля номер, сумма и день одинаковы, поэтому отдельной защитной политики выбора между разными значениями не нужно.amount_deltaсравнивает два Decimal; способ представления исходного Float64 не создаёт класс расхождения и не заставляет потребителя витрины заново решать округление.Проверка
К перечисленным в тикете проверкам добавить
make config-testи явный запускdm_transform.make check-servicesзапускает только пробникиtest_clickhouseиtest_kafka, поэтому сам по себе не доказывает наличие и успех задачиpurchase_vs_orders.На чистом хранении дополнительно проверить типы
purchaseRevenueв ODS иpurchase_revenueв DDS, точное сохранение копеек и равенство суммы событию источника. На стартовом мире сверить, что числоawaiting_orderравно числу покупок последнего прожитого дня без заказа, а после следующего слепка прежний свежий день пересобран и ожидавшие строки перешли в обычные классы. Точные счётчики закрытых дней по-прежнему принадлежат #158 и описи мира.