docs(orders): документация перестроена вокруг текущей модели
- Зачем:
- описание вело нового читателя через историю исправления вместо действующего
контракта слоёв.
- Что:
- прямо описаны версия заказа, границы пакетного приёма и расчёт окна DDS.
- ответственность ODS, DDS и конвейера разделена без ссылок на прежние
ошибки как на основной довод.
- исторический вывод исследования помечен до устаревшего абзаца, а ссылки
ведут к отдельным решениям и контрактам.
- Проверка:
- make lint.
- make config-test.
This commit is contained in:
1 parent
a82c75705d
commit
a9cead2974
6 files changed
+68
-74
No files matched your search
@@ -65,15 +65,15 @@ ODS: это единственный устойчивый ключ сущнос
|
||||
одинаковы у всех строк дня и отвечают на вопрос «каким запуском и когда
|
||||
собран этот день».
|
||||
|
||||
Ярлыки ODS — `snapshot_date`, `_load_id` приёма, `_load_ts` прибытия — в
|
||||
модель не переезжают. Лгать они перестали: колонкой версии в ODS стала дата
|
||||
слепка, и у выжившей строки она равна последнему слепку, в котором заказ был
|
||||
([ADR 0014](../../adr/0014-order-version-snapshot-date.md)). Довод стал проще:
|
||||
читателя у этих колонок здесь нет — модель отвечает на вопрос «что сейчас
|
||||
известно о заказе», а не «когда его показывал источник». Отсюда правило слоя:
|
||||
Ярлыки ODS (`snapshot_date`, `_load_id` приёма, `_load_ts` прибытия) в модель
|
||||
не переезжают. Они описывают наблюдение и доставку строки в ODS. Модель DDS
|
||||
хранит текущее деловое состояние заказа. В ODS `snapshot_date` задаёт версию и
|
||||
называет последний слепок, в котором источник показал заказ
|
||||
([ADR 0014](../../adr/0014-order-version-snapshot-date.md)). Дату
|
||||
наблюдения и координаты доставки читатель спрашивает у ODS по
|
||||
`order_id`. Правило слоя:
|
||||
**служебные колонки — координаты собственной загрузки слоя; чужие координаты
|
||||
спрашиваются у их слоя по ключу сущности**. Этим закрыто наследство #85:
|
||||
читатель у чужих ярлыков выше ODS не нашёлся и при названных читателях модели.
|
||||
спрашиваются у их слоя по ключу сущности**.
|
||||
|
||||
Происшествие качества данных расследуется цепочкой, где каждый шаг
|
||||
спрашивает свой слой на его языке: по `_load_id` строки DDS видно, какой
|
||||
|
||||
+19
-17
@@ -5,10 +5,9 @@
|
||||
движки, что именно пересобирается — живут в доках слоя
|
||||
([карта модели DDS](dds/README.md)); здесь — только конвейер.
|
||||
|
||||
Спека обещала `etl_pipeline` этапом 5 — он понадобился раньше. Конвейер
|
||||
рождается этапом 4, и временного в нём ничего нет: дневная сборка вперёд
|
||||
останется и после. Этапу 5 достаётся его законный предмет — партиционная
|
||||
переобработка, умение переиграть произвольный прошлый день по команде.
|
||||
Дневная сборка вперёд появляется на этапе 4 и остаётся основным режимом
|
||||
конвейера. На этапе 5 к ней добавляется партиционная переобработка: запуск
|
||||
произвольного прошлого дня по команде.
|
||||
|
||||
## Форма
|
||||
|
||||
@@ -47,25 +46,28 @@
|
||||
Поэтому один и тот же запуск одинаково верен из триггера, с кнопки менти и
|
||||
после падения, а разгону на семь дней хватает одного прогона.
|
||||
|
||||
Окно заказов — **дни, которые нёс последний слепок**. Называет их сам
|
||||
источник: `snapshot_date` выжившей строки равен последнему слепку, в котором
|
||||
заказ был, поэтому оба края окна трансформация читает прямо из ODS
|
||||
([ADR 0014](../adr/0014-order-version-snapshot-date.md)). Своего числа окна у
|
||||
стенда нет, и расходиться с миром нечему.
|
||||
Окно заказов задаёт последний слепок, наблюдаемый в ODS. У каждого заказа
|
||||
`snapshot_date` равен последнему слепку, в котором источник его показал.
|
||||
Трансформация выбирает строки с максимальной `snapshot_date` и берёт среди них
|
||||
минимальный и максимальный `order_date`. Это границы дней, состояние которых
|
||||
источник ещё может изменить
|
||||
([ADR 0014](../adr/0014-order-version-snapshot-date.md)). Кроме живого окна,
|
||||
трансформация закрывает пропуски: находит самый ранний день, представленный в
|
||||
ODS и ещё не собранный в DDS, затем при необходимости расширяет до него левую
|
||||
границу.
|
||||
|
||||
Позиция мира заказам не отвечает: она говорит, какой день прожит, а не какой
|
||||
доехал. Прогон дня D везёт слепок дня D − 1, да и ставится позиция раньше
|
||||
трансформации, так что после неудачного приёма обгоняет хранилище. Слой обязан
|
||||
считать объём по тому, что у него есть, а не по тому, что мир о себе заявляет.
|
||||
|
||||
У сессий всё наоборот: им отвечает только позиция, потому что вопрос у них
|
||||
другой. Им нужен не день, о котором рассказал источник, а день, который мир
|
||||
**прожил**: события живой день от прожитого не отличают — и посреди дня D,
|
||||
и сразу после его конца позднейший день событий равен D. Соберись сессии посреди
|
||||
дня, визит, начатый в 23:50, остался бы обрезанным навсегда: замёрзший день слой
|
||||
не пересобирает.
|
||||
Отсюда правило — последний прожитый день равен первому дню событий плюс позиция
|
||||
минус один ([`sql/dds/session_scope.sql`](../../sql/dds/session_scope.sql)).
|
||||
Для сессий границу задаёт `world_position`: она отличает полностью прожитый
|
||||
день от ещё идущего. Сами события этой границы не показывают: и посреди дня D,
|
||||
и сразу после его конца их позднейший день равен D. Если собрать сессии посреди
|
||||
дня, визит, начатый в 23:50, останется обрезанным: завершённый день слой
|
||||
повторно не собирает. Поэтому последний прожитый день равен первому дню
|
||||
событий плюс позиция минус один
|
||||
([`sql/dds/session_scope.sql`](../../sql/dds/session_scope.sql)).
|
||||
Оговорка про «доехал» остаётся в силе и здесь: за хвост потока отвечает не
|
||||
позиция, а «Готовность источника».
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
## Проблема
|
||||
|
||||
Заказы приезжают полным слепком окна изменяемости, но Kafka передаёт его
|
||||
отдельными сообщениями и не сообщает потребителю, где слепок закончился. Прямое
|
||||
чтение Kafka Engine возвращает одну порцию. Поэтому прежняя публикация через
|
||||
`REPLACE PARTITION snapshot_date` приравнивала дату наблюдения к отсутствующей
|
||||
транспортной границе и могла заменить день неполным набором строк.
|
||||
отдельными сообщениями и не сообщает потребителю, где слепок закончился.
|
||||
Прямое чтение Kafka Engine возвращает одну порцию без признака полноты.
|
||||
Поэтому `snapshot_date` не может служить границей публикации: `REPLACE
|
||||
PARTITION` могла бы заменить партицию дня неполным набором строк.
|
||||
|
||||
Одновременно типизированный ODS не должен принимать правдоподобные значения по
|
||||
умолчанию из грязного JSON или останавливать весь пакет из-за одной строки.
|
||||
@@ -81,11 +81,11 @@ JSON-объектом с точным набором ключей: `order_id`, `
|
||||
малый контракт заказа нет причины. Граница строгости заканчивается на форме
|
||||
провода: содержимое позиций и бизнес-инварианты намеренно остаются ниже.
|
||||
|
||||
Одно условие контракта предикатом не проверить, и названо оно всё равно
|
||||
здесь: **одна дата — один слепок**. Вторую выгрузку того же дня приём не
|
||||
отличит от первой, а колонка версии — дата слепка — на ней даёт ничью.
|
||||
Модельный источник выгружает день один раз; настоящему такое условие ставят
|
||||
договором, а не предикатом
|
||||
Контракт источника задаёт частоту: **одной дате соответствует один слепок**. Это
|
||||
свойство всего слепка, поэтому предикат одной строки его не проверяет. Вторая
|
||||
выгрузка того же дня даст ничью по `snapshot_date`, и выбор строки потеряет
|
||||
однозначность. Модельный источник выполняет условие; в настоящей интеграции
|
||||
его фиксируют в договоре с источником
|
||||
([ADR 0014](../../adr/0014-order-version-snapshot-date.md)).
|
||||
|
||||
Класс брака выбирается первым совпадением:
|
||||
@@ -113,11 +113,11 @@ JSON-объектом с точным набором ключей: `order_id`, `
|
||||
- `_load_id` — какой запуск Airflow принял строку;
|
||||
- `_load_ts` — когда строка приехала в хранилище.
|
||||
|
||||
Версию выбирает `snapshot_date`: выживает строка позднейшего слепка
|
||||
([ADR 0014](../../adr/0014-order-version-snapshot-date.md)). Поэтому ярлык
|
||||
выжившей строки — правдивый ответ на вопрос «когда заказ видели в последний
|
||||
раз», и по нему трансформация DDS читает объём пересборки. `_load_id` и
|
||||
`_load_ts` выжившей строки говорят про ту же доставку.
|
||||
Версию выбирает `snapshot_date`: выживает строка с максимальной датой слепка
|
||||
([ADR 0014](../../adr/0014-order-version-snapshot-date.md)). Поэтому
|
||||
`snapshot_date` показывает последнее наблюдение заказа. По строкам последней
|
||||
даты слепка DDS определяет окно пересборки. `_load_id` и `_load_ts` принадлежат
|
||||
той физической строке, которую сохранило слияние, и описывают её доставку.
|
||||
|
||||
Схема версий стоит на дисциплине источника: слепок с поздней датой не
|
||||
показывает состояния старше. Модельный мир её держит — состояние читается на
|
||||
@@ -211,19 +211,15 @@ dbt snapshots — стратегия `check` вместо `timestamp`.
|
||||
`system.query_log` на первой ноде осталось ровно по одному завершённому запросу
|
||||
пользователя `etl` для забора в STG, записи годных строк и записи брака.
|
||||
|
||||
Дисциплина источника замерена 22 августа 2026 года при исполнении #137: во
|
||||
всех 3034 парах строк с равным `updated_at` деловые поля совпали побайтово, по
|
||||
слепкам метка ни разу не убывала, а `ods.order_v` сошёлся построчно с эталоном
|
||||
`argMax` по `updated_at`, посчитанным прямо из сырья. Тем же замером виден
|
||||
произвол ярлыка при версии по деловой метке — довод, на котором стоит
|
||||
[ADR 0014](../../adr/0014-order-version-snapshot-date.md).
|
||||
Дисциплина источника замерена 22 августа 2026 года при исполнении #137.
|
||||
Во всех 3034 парах строк с равным `updated_at` деловые поля совпали
|
||||
побайтово, а по слепкам метка ни разу не убывала.
|
||||
|
||||
Правдивость ярлыка снята на пересобранном мире 26 августа 2026 года при
|
||||
исполнении #165. После двух слепков `snapshot_date` каждой из 1917 строк
|
||||
`ods.order_v` равен последнему слепку, который нёс её заказ: расхождений ноль
|
||||
при 3356 физически лежащих строках — выбор был не единственным. Деловое
|
||||
содержимое сменой колонки версии не тронулось: те же строки построчно совпали
|
||||
с эталоном `argMax` по `updated_at` из сырья по всем полям.
|
||||
Текущее состояние проверено на пересобранном мире 26 августа 2026 года при
|
||||
исполнении #165. После двух слепков физическая таблица содержала 3356 версий
|
||||
1917 заказов. У всех строк `ods.order_v` дата слепка совпала с последним
|
||||
слепком заказа; деловые поля совпали с эталоном `argMax` по `updated_at` из
|
||||
сырья. В обеих сверках расхождений нет.
|
||||
|
||||
Забор из Kafka в STG снят на живом стенде 18 августа 2026 года при исполнении
|
||||
#93: одно прямое чтение приносит весь слепок дня, метаданные доставки доступны,
|
||||
|
||||
@@ -234,12 +234,13 @@ ODS и в таблицу ошибок. Нужен ли он выше ODS, реш
|
||||
версии, которые ещё не убрали фоновые слияния, и не является архивом истории.
|
||||
`ods.order_v` служит поверхностью точного текущего состояния для следующих
|
||||
слоёв. Представление сохраняет язык источника и не решает, какой станет модель
|
||||
DDS. `snapshot_date` в нём — дата наблюдения и колонка версии, но не ключ
|
||||
публикации. Полное решение — в
|
||||
[ADR 0010](../adr/0010-order-versions-in-ods.md), колонка версии —
|
||||
в [ADR 0014](../adr/0014-order-version-snapshot-date.md); механика приёма —
|
||||
в [спецификации приёма заказов](orders/ingestion.md). Какой моделью заказ
|
||||
живёт дальше — [модель заказа в DDS](dds/order.md).
|
||||
DDS. `snapshot_date` в нём одновременно дата наблюдения и колонка версии. Она
|
||||
не доказывает полноту принятой порции, поэтому ODS не публикует партиции по
|
||||
дате слепка. Модель версионного ODS записана в
|
||||
[ADR 0010](../adr/0010-order-versions-in-ods.md), выбор колонки версии описан в
|
||||
[ADR 0014](../adr/0014-order-version-snapshot-date.md), пакетный приём описан в
|
||||
[спецификации приёма заказов](orders/ingestion.md). Контракт следующего слоя
|
||||
задан в [модели заказа в DDS](dds/order.md).
|
||||
|
||||
## Часовые пояса
|
||||
|
||||
|
||||
@@ -126,6 +126,10 @@ parseDateTime64InJodaSyntaxOrNull(
|
||||
использовать и для типизированной строки, и для маршрутизации ошибки; нулевая
|
||||
дата не нужна.
|
||||
|
||||
> Актуальный стенд: версию `ods.order` задаёт `snapshot_date`
|
||||
> ([ADR 0014](../adr/0014-order-version-snapshot-date.md)). Следующий
|
||||
> абзац фиксирует исходный вывод исследования и не описывает текущий контракт.
|
||||
|
||||
`updated_at` допустим как колонка версии `ReplacingMergeTree`: ClickHouse
|
||||
явно разрешает для `ver` тип `DateTime64` и оставляет строку с максимальной
|
||||
версией
|
||||
@@ -135,12 +139,6 @@ parseDateTime64InJodaSyntaxOrNull(
|
||||
монотонные миллисекундные `updated_at` на один заказ; отдельный счётчик версий
|
||||
без такого сценария был бы лишним.
|
||||
|
||||
> Вывод о колонке версии пересмотрен: версию `ods.order` задаёт `snapshot_date`
|
||||
> ([ADR 0014](../adr/0014-order-version-snapshot-date.md)). Там же разобрано,
|
||||
> почему обещанию документации про ничью — «побеждает вставленная позже» —
|
||||
> верить нельзя: замер на стенде его опроверг. Абзац выше оставлен как был,
|
||||
> документ историчен.
|
||||
|
||||
## Дата слепка и константы мира
|
||||
|
||||
Периодический слепок имеет зерно заранее заданного периода — например, дня, —
|
||||
|
||||
@@ -222,10 +222,11 @@ Ecommerce (заполнены только у торговых событий):
|
||||
- `ods.order` принимает версии заказа в
|
||||
`ReplacingMergeTree(snapshot_date)`: `ORDER BY order_id`, партиция по дню
|
||||
неизменного `created_at`, шардирование по `cityHash64(order_id)`. Дата
|
||||
слепка служит колонкой версии — выживает строка позднейшего слепка
|
||||
([ADR 0014](../adr/0014-order-version-snapshot-date.md)), — но публикацию
|
||||
и идемпотентность по-прежнему не задаёт. Физическое чтение может видеть
|
||||
несколько версий; `ods.order_v` возвращает точное текущее состояние. В DDS одна строка хранит
|
||||
слепка служит колонкой версии: выживает строка с максимальной
|
||||
`snapshot_date` ([ADR 0014](../adr/0014-order-version-snapshot-date.md)).
|
||||
Дата не доказывает полноту принятой порции, поэтому ODS не заменяет партицию
|
||||
слепка целиком. Физическое чтение может видеть несколько версий;
|
||||
`ods.order_v` возвращает точное текущее состояние. В DDS одна строка хранит
|
||||
один заказ с позициями в типизированных массивах; физическая модель и
|
||||
пакетное наполнение описаны в [доке заказа](../architecture/dds/order.md).
|
||||
|
||||
@@ -453,13 +454,9 @@ README.
|
||||
матвью к нему не привязана, и сырьё забирает шаг, которым управляет Airflow —
|
||||
одно прямое чтение вставляет порцию в `stg.orders_raw` с `_load_id` запуска.
|
||||
Один следующий `task_id` двумя последовательными запросами пишет годные строки
|
||||
и ошибки из того же среза. `ods.order` хранит версии по дате слепка
|
||||
([ADR 0014](../adr/0014-order-version-snapshot-date.md)), а не публикует
|
||||
партицию `snapshot_date`. Слой сырья остаётся точкой повтора и
|
||||
разбора одной принятой порции.
|
||||
|
||||
Стенд получает от этого два режима приёма рядом, поток и слепок, и сравнение
|
||||
из опорных точек раздела 12 переформулировано под них.
|
||||
и ошибки из того же среза. `ods.order` выбирает текущую версию по максимальной
|
||||
`snapshot_date` ([ADR 0014](../adr/0014-order-version-snapshot-date.md)). Слой
|
||||
сырья остаётся точкой повтора и разбора одной принятой порции.
|
||||
|
||||
Состав служебных колонок задаёт дока хранилища. Спеке важны два следствия:
|
||||
`ods.event` и `ods.order` получают метку загрузки `_load_ts`, но версию
|
||||
|
||||
Reference in new issue
Block a user