docs(adr): версия заказа в ODS пересмотрена на дату слепка
- Зачем:
- лотерея ярлыков доставки при ничьей по updated_at делала слой
недетерминированным и держала окно K двумя копиями без сверки (#163).
- Что:
- добавлен ADR 0014: версия ods.order — snapshot_date, с ответом доводу
ADR 0010, границами допущения и веером отклонённых вариантов.
- в ADR 0010 помечен пересмотр колонки версии.
- Проверка:
- реализация и замеры — тикет #165; решение кодом пока не применено.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9ab43e79cc
commit
c361c36e3d
2 files changed
+99
-1
No files matched your search
@@ -1,7 +1,9 @@
|
||||
# ADR 0010. Заказы в ODS: версии сущности вместо подмены слепка
|
||||
|
||||
Дата: 16 августа 2026 года. Статус: принято. Частично заменяет
|
||||
[ADR 0008](0008-order-ingestion.md).
|
||||
[ADR 0008](0008-order-ingestion.md). Колонка версии пересмотрена
|
||||
[ADR 0014](0014-order-version-snapshot-date.md): вместо `updated_at` —
|
||||
`snapshot_date`.
|
||||
|
||||
## Решение
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
# ADR 0014. Версия заказа в ODS: дата слепка
|
||||
|
||||
Дата: 26 августа 2026 года. Статус: принято. Пересматривает колонку версии
|
||||
из [ADR 0010](0010-order-versions-in-ods.md); остальные решения того ADR в
|
||||
силе. Развилка разобрана в #163, реализация — #165.
|
||||
|
||||
## Решение
|
||||
|
||||
`ods.order` хранит версии заказа в `ReplacingMergeTree(snapshot_date)`: при
|
||||
схлопывании выживает строка позднейшего слепка, который нёс заказ. Формула
|
||||
решения: у полного слепка версия — сам слепок; истина — то, что источник
|
||||
сказал последним.
|
||||
|
||||
Ключ сущности, партиция и шардирование не меняются. `updated_at` остаётся
|
||||
деловым полем, и деловой ответ слоя от смены версии не зависит: на
|
||||
канонических слепках выбор по дате слепка побайтово совпадает с выбором по
|
||||
деловой метке, потому что метка монотонна по слепкам, а слепок дня — чистая
|
||||
функция зерна и дня (тесты генератора
|
||||
`test_the_state_is_read_at_the_boundary_of_the_day` и
|
||||
`test_two_runs_give_the_same_snapshot`).
|
||||
|
||||
Решение покупает две вещи.
|
||||
|
||||
- **Детерминизм слоя.** Разные слепки несут разные даты — ничьей между
|
||||
содержательно разными строками не бывает. При версии `updated_at` доставки
|
||||
неизменившегося заказа равны по версии, и выжившая строка — жребий: у 86
|
||||
заказов стартового мира выживала строка серединного слепка, притом что у
|
||||
всех 3034 пар строк с равным `updated_at` деловые поля совпадали побайтово
|
||||
(замеры #137). Документация ClickHouse обещает при ничьей победу последней
|
||||
вставленной строки — замер это опроверг, полагаться на неё было бы ошибкой.
|
||||
- **Правдивый ярлык.** `snapshot_date` выжившей строки — последний слепок,
|
||||
который нёс заказ. Объём пересборки DDS читается прямо из данных, причём
|
||||
без `FINAL`: слияние сохраняет максимум даты по построению. Константа
|
||||
окна K уходит со стороны стенда вместе с риском дрейфа её двух копий
|
||||
(ревью #151 ставило этому риску MAJOR).
|
||||
|
||||
## Ответ доводу ADR 0010
|
||||
|
||||
ADR 0010 отверг семейство «публикацию решает дата наблюдения»: у потребителя
|
||||
нет признака полноты слепка. Довод целил в подмену партиции — ей полнота
|
||||
нужна, потому что неполная партиция стёрла бы уже принятые версии. Колонке
|
||||
версии полнота не нужна: она сравнивает свежесть строк одного заказа, и
|
||||
недоехавшая строка просто оставляет в силе предыдущую — ровно как при версии
|
||||
по деловой метке.
|
||||
|
||||
## Границы допущения
|
||||
|
||||
- **Дисциплина слепков.** Слепок с поздней датой не несёт состояния старше.
|
||||
Источник, собирающий слепок из отставшей реплики, это ломает; модельный
|
||||
мир — нет: см. тесты выше.
|
||||
- **Одна дата — один слепок.** Ограничение вшито в движок: вторая
|
||||
внутридневная выгрузка дала бы ничью уже на деловых полях. Это условие
|
||||
контракта источника, его место — в границе строгого приёма
|
||||
([приём заказов](../architecture/orders/ingestion.md)).
|
||||
- **Окно наследует потерю строк.** Частичный отказ вставки после коммита
|
||||
офсетов может унести старший день последнего слепка — выведенное окно
|
||||
молча сузится. Лекарство прежнее, сверка слоя с источником в контуре DQ
|
||||
([конвейер](../architecture/etl.md), «Чего конвейер о себе не знает»).
|
||||
- **Тихая правка лечится только внутри окна.** Строку, изменённую без
|
||||
сдвига `updated_at`, перекроет следующий слепок; замёрзший заказ источник
|
||||
больше не показывает, там лечит лишь сверка полей.
|
||||
- **Повторная доставка слепка.** Байты совпадают, жребий остаётся только на
|
||||
`_load_id` и `_load_ts`; ярлык даты детерминирован.
|
||||
- **Правка живого мира не подхватится.** DDL исполняется при рождении мира,
|
||||
`ALTER` колонку версии не меняет: решение вступает в силу пересозданием
|
||||
хранилища (`make rebuild-storage`).
|
||||
|
||||
## Отклонённые варианты
|
||||
|
||||
- **Оставить как есть, сверку копий K отдать DQ.** Лотерея ярлыков остаётся
|
||||
задокументированным грехом, окно — константой с риском дрейфа.
|
||||
- **Убрать координаты доставки из ODS.** Наказывает поле за вопрос, на
|
||||
который оно не бралось отвечать: расследованию (связка годных строк,
|
||||
ошибок и сырья) координаты служили честно.
|
||||
- **Дедупликация на вставке.** Детерминизм «обычно», не по построению:
|
||||
анти-джойн с собственной целью не атомарен, лаг реплик протаскивает дубли.
|
||||
Приём теряет урок «разобрал и вставил», а ярлык со смыслом «первый
|
||||
носитель» хуже для обеих ролей поля: окну нужен последний носитель, и
|
||||
сырьё живо скорее у него.
|
||||
- **Склейка `(updated_at, snapshot_date)` в одну колонку версии.** Движок
|
||||
берёт одну колонку, склейка требует битовой упаковки. Робастнее к
|
||||
недисциплинированному источнику, но платит читаемостью DDL за защиту от
|
||||
сценариев, которых модельный мир не производит.
|
||||
- **Склейка `(snapshot_date, updated_at)`.** Путь отступления: если у
|
||||
источника появятся переиздания слепка одной даты, версия дорастает до
|
||||
этой формы. От правок без сдвига метки внутри одной даты не спасает и она.
|
||||
|
||||
## Связанные решения
|
||||
|
||||
- [ADR 0010](0010-order-versions-in-ods.md) — версии заказа в ODS; здесь
|
||||
пересмотрена только колонка версии.
|
||||
- [Приём заказов](../architecture/orders/ingestion.md) — граница строгого
|
||||
приёма и четыре координаты строки.
|
||||
- [Модель заказа в DDS](../architecture/dds/order.md) — читатель окна
|
||||
пересборки.
|
||||
- Развилка, веер и два холодных ревью — #163.
|
||||
Reference in new issue
Block a user